diff --git a/.gitattributes b/.gitattributes index e42ecabd6..d42b256b9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,11 +1,3 @@ -# Custom for Visual Studio -*.cs diff=csharp -*.sln merge=union -*.csproj merge=union -*.vbproj merge=union -*.fsproj merge=union -*.dbproj merge=union - # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain @@ -18,5 +10,13 @@ *.rtf diff=astextplain *.RTF diff=astextplain -[Cc][Mm]ake[Ll]ists.txt text=lf -*.bat text=lf +* text=auto + +[Cc][Mm]ake[Ll]ists.txt text eol=lf +*.bat text eol=lf +*.c text eol=lf +*.h text eol=lf +*.cpp text eol=lf +*.hpp text eol=lf +*.unused-patches text eol=lf +*.rc text eol=lf diff --git a/import/OpenXDK/include/xboxkrnl/ob.h b/import/OpenXDK/include/xboxkrnl/ob.h index f9ca5c488..d6350b1ad 100644 --- a/import/OpenXDK/include/xboxkrnl/ob.h +++ b/import/OpenXDK/include/xboxkrnl/ob.h @@ -1,220 +1,220 @@ -// ****************************************************************** -// * -// * proj : OpenXDK -// * -// * desc : Open Source XBox Development Kit -// * -// * file : ob.h -// * -// * note : XBox Kernel *Object Manager* Declarations -// * -// ****************************************************************** -#ifndef XBOXKRNL_OB_H -#define XBOXKRNL_OB_H - -#define OBJ_NAME_PATH_SEPARATOR ((CHAR)L'\\') - -#define OB_NUMBER_HASH_BUCKETS 11 -typedef struct _OBJECT_DIRECTORY { - struct _OBJECT_HEADER_NAME_INFO *HashBuckets[OB_NUMBER_HASH_BUCKETS]; -} OBJECT_DIRECTORY, *POBJECT_DIRECTORY; - -typedef struct _OBJECT_SYMBOLIC_LINK { - PVOID LinkTargetObject; - OBJECT_STRING LinkTarget; -} OBJECT_SYMBOLIC_LINK, *POBJECT_SYMBOLIC_LINK; - -typedef struct _OBJECT_HEADER_NAME_INFO { - struct _OBJECT_HEADER_NAME_INFO *ChainLink; - struct _OBJECT_DIRECTORY *Directory; - OBJECT_STRING Name; -} OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_NAME_INFO; - -#define ObDosDevicesDirectory() ((HANDLE)-3) -#define ObWin32NamedObjectsDirectory() ((HANDLE)-4) - -#define ObpIsFlagSet(flagset, flag) (((flagset) & (flag)) != 0) -#define ObpIsFlagClear(flagset, flag) (((flagset) & (flag)) == 0) -#define ObpEncodeFreeHandleLink(Link) (((ULONG_PTR)(Link)) | 1) -#define ObpDecodeFreeHandleLink(Link) (((ULONG_PTR)(Link)) & (~1)) -#define ObpIsFreeHandleLink(Link) (((ULONG_PTR)(Link)) & 1) -#define ObpGetTableByteOffsetFromHandle(Handle) (HandleToUlong(Handle) & (OB_HANDLES_PER_TABLE * sizeof(PVOID) - 1)) -#define ObpGetTableFromHandle(Handle) ObpObjectHandleTable.RootTable[HandleToUlong(Handle) >> (OB_HANDLES_PER_TABLE_SHIFT + 2)] -#define ObpGetHandleContentsPointer(Handle) ((PVOID*)((PUCHAR)ObpGetTableFromHandle(Handle) + ObpGetTableByteOffsetFromHandle(Handle))) -#define ObpMaskOffApplicationBits(Handle) ((HANDLE)(((ULONG_PTR)(Handle)) & ~(sizeof(ULONG) - 1))) - -#define OB_FLAG_NAMED_OBJECT 0x01 -#define OB_FLAG_PERMANENT_OBJECT 0x02 -#define OB_FLAG_ATTACHED_OBJECT 0x04 - -#define OBJECT_TO_OBJECT_HEADER(Object) CONTAINING_RECORD(Object, OBJECT_HEADER, Body) -#define OBJECT_TO_OBJECT_HEADER_NAME_INFO(Object) ((POBJECT_HEADER_NAME_INFO)OBJECT_TO_OBJECT_HEADER(Object) - 1) -#define OBJECT_HEADER_NAME_INFO_TO_OBJECT_HEADER(ObjectHeaderNameInfo) ((POBJECT_HEADER)((POBJECT_HEADER_NAME_INFO)(ObjectHeaderNameInfo)+1)) -#define OBJECT_HEADER_TO_OBJECT_HEADER_NAME_INFO(ObjectHeader) ((POBJECT_HEADER_NAME_INFO)(ObjectHeader)-1) -#define OBJECT_HEADER_NAME_INFO_TO_OBJECT(ObjectHeaderNameInfo) (&OBJECT_HEADER_NAME_INFO_TO_OBJECT_HEADER(ObjectHeaderNameInfo)->Body) - -HANDLE ObpCreateObjectHandle(PVOID Object); -BOOLEAN ObpCreatePermanentDirectoryObject( - IN POBJECT_STRING DirectoryName OPTIONAL, - OUT POBJECT_DIRECTORY *DirectoryObject -); - -NTSTATUS ObpReferenceObjectByName( - IN HANDLE RootDirectoryHandle, - IN POBJECT_STRING ObjectName, - IN ULONG Attributes, - IN POBJECT_TYPE ObjectType, - IN OUT PVOID ParseContext OPTIONAL, - OUT PVOID *ReturnedObject -); - -#define XB_InitializeObjectAttributes(p, n, a, r, s){\ - (p)->RootDirectory = r; \ - (p)->Attributes = a; \ - (p)->ObjectName = n; \ -} - -BOOLEAN ObInitSystem(); -BOOLEAN ObpExtendObjectHandleTable(); -VOID ObDissectName(OBJECT_STRING Path, POBJECT_STRING FirstName, POBJECT_STRING RemainingName); -PVOID ObpGetObjectHandleContents(HANDLE Handle); -PVOID ObpGetObjectHandleReference(HANDLE Handle); -ULONG FASTCALL ObpComputeHashIndex(IN POBJECT_STRING ElementName); - -BOOLEAN ObpLookupElementNameInDirectory( - IN POBJECT_DIRECTORY Directory, - IN POBJECT_STRING ElementName, - IN BOOLEAN ResolveSymbolicLink, - OUT PVOID *ReturnedObject -); - -// ****************************************************************** -// * 0x00EF - ObCreateObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(239) NTSTATUS NTAPI ObCreateObject -( - IN POBJECT_TYPE ObjectType, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN ULONG ObjectBodySize, - OUT PVOID *Object -); - -// ****************************************************************** -// * 0x00F0 - ObDirectoryObjectType -// ****************************************************************** -XBSYSAPI EXPORTNUM(240) OBJECT_TYPE ObDirectoryObjectType; - -// ****************************************************************** -// * 0x00F1 - ObInsertObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(241) NTSTATUS NTAPI ObInsertObject -( - IN PVOID Object, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN ULONG ObjectPointerBias, - OUT PHANDLE Handle -); - -// ****************************************************************** -// * 0x00F2 - ObMakeTemporaryObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(242) VOID NTAPI ObMakeTemporaryObject -( - IN PVOID Object -); - -// ****************************************************************** -// * 0x00F3 - ObOpenObjectByName() -// ****************************************************************** -XBSYSAPI EXPORTNUM(243) NTSTATUS NTAPI ObOpenObjectByName -( - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN POBJECT_TYPE ObjectType, - IN OUT PVOID ParseContext OPTIONAL, - OUT PHANDLE Handle -); - -// ****************************************************************** -// * 0x00F4 - ObOpenObjectByPointer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(244) NTSTATUS NTAPI ObOpenObjectByPointer -( - IN PVOID Object, - IN POBJECT_TYPE ObjectType, - OUT PHANDLE Handle -); - -#define OB_HANDLES_PER_TABLE_SHIFT 6 -#define OB_HANDLES_PER_TABLE (1 << OB_HANDLES_PER_TABLE_SHIFT) -#define OB_TABLES_PER_SEGMENT 8 -#define OB_HANDLES_PER_SEGMENT (OB_TABLES_PER_SEGMENT * OB_HANDLES_PER_TABLE) - -typedef struct _OBJECT_HANDLE_TABLE { - LONG HandleCount; - LONG_PTR FirstFreeTableEntry; - HANDLE NextHandleNeedingPool; - PVOID **RootTable; - PVOID *BuiltinRootTable[OB_TABLES_PER_SEGMENT]; -} OBJECT_HANDLE_TABLE, *POBJECT_HANDLE_TABLE; - -// ****************************************************************** -// * 0x00F5 - ObpObjectHandleTable -// ****************************************************************** -XBSYSAPI EXPORTNUM(245) OBJECT_HANDLE_TABLE ObpObjectHandleTable; - -// ****************************************************************** -// * 0x00F6 - ObReferenceObjectByHandle() -// ****************************************************************** -XBSYSAPI EXPORTNUM(246) NTSTATUS NTAPI ObReferenceObjectByHandle -( - IN HANDLE Handle, - IN POBJECT_TYPE ObjectType OPTIONAL, - OUT PVOID *ReturnedObject -); - -// ****************************************************************** -// * 0x00F7 - ObReferenceObjectByName() -// ****************************************************************** -XBSYSAPI EXPORTNUM(247) NTSTATUS NTAPI ObReferenceObjectByName -( - IN POBJECT_STRING ObjectName, - IN ULONG Attributes, - IN POBJECT_TYPE ObjectType, - IN OUT PVOID ParseContext OPTIONAL, - OUT PVOID *Object -); - -// ****************************************************************** -// * 0x00F8 - ObReferenceObjectByPointer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(248) NTSTATUS NTAPI ObReferenceObjectByPointer -( - IN PVOID Object, - IN POBJECT_TYPE ObjectType -); - -// ****************************************************************** -// * 0x00F9 - ObSymbolicLinkObjectType -// ****************************************************************** -XBSYSAPI EXPORTNUM(249) OBJECT_TYPE ObSymbolicLinkObjectType; - -// ****************************************************************** -// * 0x00FA - ObfDereferenceObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(250) VOID FASTCALL ObfDereferenceObject -( - IN PVOID Object -); - -// ****************************************************************** -// * 0x00FB - ObfReferenceObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(251) VOID FASTCALL ObfReferenceObject -( - IN PVOID Object -); - - -#endif - - +// ****************************************************************** +// * +// * proj : OpenXDK +// * +// * desc : Open Source XBox Development Kit +// * +// * file : ob.h +// * +// * note : XBox Kernel *Object Manager* Declarations +// * +// ****************************************************************** +#ifndef XBOXKRNL_OB_H +#define XBOXKRNL_OB_H + +#define OBJ_NAME_PATH_SEPARATOR ((CHAR)L'\\') + +#define OB_NUMBER_HASH_BUCKETS 11 +typedef struct _OBJECT_DIRECTORY { + struct _OBJECT_HEADER_NAME_INFO *HashBuckets[OB_NUMBER_HASH_BUCKETS]; +} OBJECT_DIRECTORY, *POBJECT_DIRECTORY; + +typedef struct _OBJECT_SYMBOLIC_LINK { + PVOID LinkTargetObject; + OBJECT_STRING LinkTarget; +} OBJECT_SYMBOLIC_LINK, *POBJECT_SYMBOLIC_LINK; + +typedef struct _OBJECT_HEADER_NAME_INFO { + struct _OBJECT_HEADER_NAME_INFO *ChainLink; + struct _OBJECT_DIRECTORY *Directory; + OBJECT_STRING Name; +} OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_NAME_INFO; + +#define ObDosDevicesDirectory() ((HANDLE)-3) +#define ObWin32NamedObjectsDirectory() ((HANDLE)-4) + +#define ObpIsFlagSet(flagset, flag) (((flagset) & (flag)) != 0) +#define ObpIsFlagClear(flagset, flag) (((flagset) & (flag)) == 0) +#define ObpEncodeFreeHandleLink(Link) (((ULONG_PTR)(Link)) | 1) +#define ObpDecodeFreeHandleLink(Link) (((ULONG_PTR)(Link)) & (~1)) +#define ObpIsFreeHandleLink(Link) (((ULONG_PTR)(Link)) & 1) +#define ObpGetTableByteOffsetFromHandle(Handle) (HandleToUlong(Handle) & (OB_HANDLES_PER_TABLE * sizeof(PVOID) - 1)) +#define ObpGetTableFromHandle(Handle) ObpObjectHandleTable.RootTable[HandleToUlong(Handle) >> (OB_HANDLES_PER_TABLE_SHIFT + 2)] +#define ObpGetHandleContentsPointer(Handle) ((PVOID*)((PUCHAR)ObpGetTableFromHandle(Handle) + ObpGetTableByteOffsetFromHandle(Handle))) +#define ObpMaskOffApplicationBits(Handle) ((HANDLE)(((ULONG_PTR)(Handle)) & ~(sizeof(ULONG) - 1))) + +#define OB_FLAG_NAMED_OBJECT 0x01 +#define OB_FLAG_PERMANENT_OBJECT 0x02 +#define OB_FLAG_ATTACHED_OBJECT 0x04 + +#define OBJECT_TO_OBJECT_HEADER(Object) CONTAINING_RECORD(Object, OBJECT_HEADER, Body) +#define OBJECT_TO_OBJECT_HEADER_NAME_INFO(Object) ((POBJECT_HEADER_NAME_INFO)OBJECT_TO_OBJECT_HEADER(Object) - 1) +#define OBJECT_HEADER_NAME_INFO_TO_OBJECT_HEADER(ObjectHeaderNameInfo) ((POBJECT_HEADER)((POBJECT_HEADER_NAME_INFO)(ObjectHeaderNameInfo)+1)) +#define OBJECT_HEADER_TO_OBJECT_HEADER_NAME_INFO(ObjectHeader) ((POBJECT_HEADER_NAME_INFO)(ObjectHeader)-1) +#define OBJECT_HEADER_NAME_INFO_TO_OBJECT(ObjectHeaderNameInfo) (&OBJECT_HEADER_NAME_INFO_TO_OBJECT_HEADER(ObjectHeaderNameInfo)->Body) + +HANDLE ObpCreateObjectHandle(PVOID Object); +BOOLEAN ObpCreatePermanentDirectoryObject( + IN POBJECT_STRING DirectoryName OPTIONAL, + OUT POBJECT_DIRECTORY *DirectoryObject +); + +NTSTATUS ObpReferenceObjectByName( + IN HANDLE RootDirectoryHandle, + IN POBJECT_STRING ObjectName, + IN ULONG Attributes, + IN POBJECT_TYPE ObjectType, + IN OUT PVOID ParseContext OPTIONAL, + OUT PVOID *ReturnedObject +); + +#define XB_InitializeObjectAttributes(p, n, a, r, s){\ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ +} + +BOOLEAN ObInitSystem(); +BOOLEAN ObpExtendObjectHandleTable(); +VOID ObDissectName(OBJECT_STRING Path, POBJECT_STRING FirstName, POBJECT_STRING RemainingName); +PVOID ObpGetObjectHandleContents(HANDLE Handle); +PVOID ObpGetObjectHandleReference(HANDLE Handle); +ULONG FASTCALL ObpComputeHashIndex(IN POBJECT_STRING ElementName); + +BOOLEAN ObpLookupElementNameInDirectory( + IN POBJECT_DIRECTORY Directory, + IN POBJECT_STRING ElementName, + IN BOOLEAN ResolveSymbolicLink, + OUT PVOID *ReturnedObject +); + +// ****************************************************************** +// * 0x00EF - ObCreateObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(239) NTSTATUS NTAPI ObCreateObject +( + IN POBJECT_TYPE ObjectType, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN ULONG ObjectBodySize, + OUT PVOID *Object +); + +// ****************************************************************** +// * 0x00F0 - ObDirectoryObjectType +// ****************************************************************** +XBSYSAPI EXPORTNUM(240) OBJECT_TYPE ObDirectoryObjectType; + +// ****************************************************************** +// * 0x00F1 - ObInsertObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(241) NTSTATUS NTAPI ObInsertObject +( + IN PVOID Object, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN ULONG ObjectPointerBias, + OUT PHANDLE Handle +); + +// ****************************************************************** +// * 0x00F2 - ObMakeTemporaryObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(242) VOID NTAPI ObMakeTemporaryObject +( + IN PVOID Object +); + +// ****************************************************************** +// * 0x00F3 - ObOpenObjectByName() +// ****************************************************************** +XBSYSAPI EXPORTNUM(243) NTSTATUS NTAPI ObOpenObjectByName +( + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN POBJECT_TYPE ObjectType, + IN OUT PVOID ParseContext OPTIONAL, + OUT PHANDLE Handle +); + +// ****************************************************************** +// * 0x00F4 - ObOpenObjectByPointer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(244) NTSTATUS NTAPI ObOpenObjectByPointer +( + IN PVOID Object, + IN POBJECT_TYPE ObjectType, + OUT PHANDLE Handle +); + +#define OB_HANDLES_PER_TABLE_SHIFT 6 +#define OB_HANDLES_PER_TABLE (1 << OB_HANDLES_PER_TABLE_SHIFT) +#define OB_TABLES_PER_SEGMENT 8 +#define OB_HANDLES_PER_SEGMENT (OB_TABLES_PER_SEGMENT * OB_HANDLES_PER_TABLE) + +typedef struct _OBJECT_HANDLE_TABLE { + LONG HandleCount; + LONG_PTR FirstFreeTableEntry; + HANDLE NextHandleNeedingPool; + PVOID **RootTable; + PVOID *BuiltinRootTable[OB_TABLES_PER_SEGMENT]; +} OBJECT_HANDLE_TABLE, *POBJECT_HANDLE_TABLE; + +// ****************************************************************** +// * 0x00F5 - ObpObjectHandleTable +// ****************************************************************** +XBSYSAPI EXPORTNUM(245) OBJECT_HANDLE_TABLE ObpObjectHandleTable; + +// ****************************************************************** +// * 0x00F6 - ObReferenceObjectByHandle() +// ****************************************************************** +XBSYSAPI EXPORTNUM(246) NTSTATUS NTAPI ObReferenceObjectByHandle +( + IN HANDLE Handle, + IN POBJECT_TYPE ObjectType OPTIONAL, + OUT PVOID *ReturnedObject +); + +// ****************************************************************** +// * 0x00F7 - ObReferenceObjectByName() +// ****************************************************************** +XBSYSAPI EXPORTNUM(247) NTSTATUS NTAPI ObReferenceObjectByName +( + IN POBJECT_STRING ObjectName, + IN ULONG Attributes, + IN POBJECT_TYPE ObjectType, + IN OUT PVOID ParseContext OPTIONAL, + OUT PVOID *Object +); + +// ****************************************************************** +// * 0x00F8 - ObReferenceObjectByPointer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(248) NTSTATUS NTAPI ObReferenceObjectByPointer +( + IN PVOID Object, + IN POBJECT_TYPE ObjectType +); + +// ****************************************************************** +// * 0x00F9 - ObSymbolicLinkObjectType +// ****************************************************************** +XBSYSAPI EXPORTNUM(249) OBJECT_TYPE ObSymbolicLinkObjectType; + +// ****************************************************************** +// * 0x00FA - ObfDereferenceObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(250) VOID FASTCALL ObfDereferenceObject +( + IN PVOID Object +); + +// ****************************************************************** +// * 0x00FB - ObfReferenceObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(251) VOID FASTCALL ObfReferenceObject +( + IN PVOID Object +); + + +#endif + + diff --git a/import/OpenXDK/include/xboxkrnl/xbox.h b/import/OpenXDK/include/xboxkrnl/xbox.h index 56f868277..455971663 100644 --- a/import/OpenXDK/include/xboxkrnl/xbox.h +++ b/import/OpenXDK/include/xboxkrnl/xbox.h @@ -415,41 +415,41 @@ XBSYSAPI EXPORTNUM(350) ULONG NTAPI XcCryptService ); /* Function pointers which point to all the kernel crypto functions. Used by PCRYPTO_VECTOR. */ -typedef VOID(NTAPI *pfXcSHAInit)(PUCHAR pbSHAContext); -typedef VOID(NTAPI *pfXcSHAUpdate)(PUCHAR pbSHAContext, PUCHAR pbInput, ULONG dwInputLength); -typedef VOID(NTAPI *pfXcSHAFinal)(PUCHAR pbSHAContext, PUCHAR pbDigest); -typedef VOID(NTAPI *pfXcRC4Key)(PUCHAR pbKeyStruct, ULONG dwKeyLength, PUCHAR pbKey); -typedef VOID(NTAPI *pfXcRC4Crypt)(PUCHAR pbKeyStruct, ULONG dwInputLength, PUCHAR pbInput); -typedef VOID(NTAPI *pfXcHMAC)(PBYTE pbKeyMaterial, ULONG cbKeyMaterial, PBYTE pbData, ULONG cbData, PBYTE pbData2, ULONG cbData2, PBYTE HmacData); -typedef ULONG(NTAPI *pfXcPKEncPublic)(PUCHAR pbPubKey, PUCHAR pbInput, PUCHAR pbOutput); -typedef ULONG(NTAPI *pfXcPKDecPrivate)(PUCHAR pbPrvKey, PUCHAR pbInput, PUCHAR pbOutput); -typedef ULONG(NTAPI *pfXcPKGetKeyLen)(PUCHAR pbPubKey); -typedef BOOLEAN(NTAPI *pfXcVerifyPKCS1Signature)(PUCHAR pbSig, PUCHAR pbPubKey, PUCHAR pbDigest); -typedef ULONG(NTAPI *pfXcModExp)(LPDWORD pA, LPDWORD pB, LPDWORD pC, LPDWORD pD, ULONG dwN); -typedef VOID(NTAPI *pfXcDESKeyParity)(PUCHAR pbKey, ULONG dwKeyLength); -typedef VOID(NTAPI *pfXcKeyTable)(ULONG dwCipher, PUCHAR pbKeyTable, PUCHAR pbKey); -typedef VOID(NTAPI *pfXcBlockCrypt)(ULONG dwCipher, PUCHAR pbOutput, PUCHAR pbInput, PUCHAR pbKeyTable, ULONG dwOp); -typedef VOID(NTAPI *pfXcBlockCryptCBC)(ULONG dwCipher, ULONG dwInputLength, PUCHAR pbOutput, PUCHAR pbInput, PUCHAR pbKeyTable, ULONG dwOp, PUCHAR pbFeedback); +typedef VOID(NTAPI *pfXcSHAInit)(PUCHAR pbSHAContext); +typedef VOID(NTAPI *pfXcSHAUpdate)(PUCHAR pbSHAContext, PUCHAR pbInput, ULONG dwInputLength); +typedef VOID(NTAPI *pfXcSHAFinal)(PUCHAR pbSHAContext, PUCHAR pbDigest); +typedef VOID(NTAPI *pfXcRC4Key)(PUCHAR pbKeyStruct, ULONG dwKeyLength, PUCHAR pbKey); +typedef VOID(NTAPI *pfXcRC4Crypt)(PUCHAR pbKeyStruct, ULONG dwInputLength, PUCHAR pbInput); +typedef VOID(NTAPI *pfXcHMAC)(PBYTE pbKeyMaterial, ULONG cbKeyMaterial, PBYTE pbData, ULONG cbData, PBYTE pbData2, ULONG cbData2, PBYTE HmacData); +typedef ULONG(NTAPI *pfXcPKEncPublic)(PUCHAR pbPubKey, PUCHAR pbInput, PUCHAR pbOutput); +typedef ULONG(NTAPI *pfXcPKDecPrivate)(PUCHAR pbPrvKey, PUCHAR pbInput, PUCHAR pbOutput); +typedef ULONG(NTAPI *pfXcPKGetKeyLen)(PUCHAR pbPubKey); +typedef BOOLEAN(NTAPI *pfXcVerifyPKCS1Signature)(PUCHAR pbSig, PUCHAR pbPubKey, PUCHAR pbDigest); +typedef ULONG(NTAPI *pfXcModExp)(LPDWORD pA, LPDWORD pB, LPDWORD pC, LPDWORD pD, ULONG dwN); +typedef VOID(NTAPI *pfXcDESKeyParity)(PUCHAR pbKey, ULONG dwKeyLength); +typedef VOID(NTAPI *pfXcKeyTable)(ULONG dwCipher, PUCHAR pbKeyTable, PUCHAR pbKey); +typedef VOID(NTAPI *pfXcBlockCrypt)(ULONG dwCipher, PUCHAR pbOutput, PUCHAR pbInput, PUCHAR pbKeyTable, ULONG dwOp); +typedef VOID(NTAPI *pfXcBlockCryptCBC)(ULONG dwCipher, ULONG dwInputLength, PUCHAR pbOutput, PUCHAR pbInput, PUCHAR pbKeyTable, ULONG dwOp, PUCHAR pbFeedback); typedef ULONG(NTAPI *pfXcCryptService)(ULONG dwOp, PVOID pArgs); /* Struct which contains all the pointers to the crypto functions */ -typedef struct { - pfXcSHAInit pXcSHAInit; - pfXcSHAUpdate pXcSHAUpdate; - pfXcSHAFinal pXcSHAFinal; - pfXcRC4Key pXcRC4Key; - pfXcRC4Crypt pXcRC4Crypt; - pfXcHMAC pXcHMAC; - pfXcPKEncPublic pXcPKEncPublic; - pfXcPKDecPrivate pXcPKDecPrivate; - pfXcPKGetKeyLen pXcPKGetKeyLen; - pfXcVerifyPKCS1Signature pXcVerifyPKCS1Signature; - pfXcModExp pXcModExp; - pfXcDESKeyParity pXcDESKeyParity; - pfXcKeyTable pXcKeyTable; - pfXcBlockCrypt pXcBlockCrypt; - pfXcBlockCryptCBC pXcBlockCryptCBC; - pfXcCryptService pXcCryptService; +typedef struct { + pfXcSHAInit pXcSHAInit; + pfXcSHAUpdate pXcSHAUpdate; + pfXcSHAFinal pXcSHAFinal; + pfXcRC4Key pXcRC4Key; + pfXcRC4Crypt pXcRC4Crypt; + pfXcHMAC pXcHMAC; + pfXcPKEncPublic pXcPKEncPublic; + pfXcPKDecPrivate pXcPKDecPrivate; + pfXcPKGetKeyLen pXcPKGetKeyLen; + pfXcVerifyPKCS1Signature pXcVerifyPKCS1Signature; + pfXcModExp pXcModExp; + pfXcDESKeyParity pXcDESKeyParity; + pfXcKeyTable pXcKeyTable; + pfXcBlockCrypt pXcBlockCrypt; + pfXcBlockCryptCBC pXcBlockCryptCBC; + pfXcCryptService pXcCryptService; } CRYPTO_VECTOR, *PCRYPTO_VECTOR; // ****************************************************************** diff --git a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h index 3b075636a..d8f357cc2 100644 --- a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h +++ b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h @@ -174,7 +174,7 @@ typedef long NTSTATUS; // * Registry value types // ****************************************************************** // Used in ExQueryNonVolatileSetting and ExSaveNonVolatileSetting -#ifndef _WIN32 // Avoid "warning C4005: 'REG_NONE': macro redefinition" (conflicting with winnt.h) +#ifndef _WIN32 // Avoid "warning C4005: 'REG_NONE': macro redefinition" (conflicting with winnt.h) #define REG_NONE ( 0 ) // No defined value type. #define REG_SZ ( 1 ) // A null - terminated string. This will be either a Unicode or an ANSI string, depending on whether you use the Unicode or ANSI functions. #define REG_EXPAND_SZ ( 2 ) // A null - terminated string that contains unexpanded references to environment variables (for example, "%PATH%"). It will be a Unicode or ANSI string depending on whether you use the Unicode or ANSI functions. To expand the environment variable references, use the ExpandEnvironmentStrings function. @@ -187,7 +187,7 @@ typedef long NTSTATUS; #define REG_RESOURCE_LIST ( 8 ) // Resource list in the resource map #define REG_FULL_RESOURCE_DESCRIPTOR ( 9 ) // Resource list in the hardware description #define REG_RESOURCE_REQUIREMENTS_LIST ( 10 ) -#endif +#endif // ****************************************************************** // * calling conventions @@ -352,13 +352,13 @@ typedef struct _SINGLE_LIST_ENTRY { struct _SINGLE_LIST_ENTRY *Next; } SINGLE_LIST_ENTRY, *PSINGLE_LIST_ENTRY, SLIST_ENTRY, *PSLIST_ENTRY; -typedef union _SLIST_HEADER { - ULONGLONG Alignment; - struct { - SINGLE_LIST_ENTRY Next; - USHORT Depth; - USHORT Sequence; - }; +typedef union _SLIST_HEADER { + ULONGLONG Alignment; + struct { + SINGLE_LIST_ENTRY Next; + USHORT Depth; + USHORT Sequence; + }; } SLIST_HEADER, *PSLIST_HEADER; #define QUERY_DEPTH_SLIST(_listhead_) (USHORT)(_listhead_)->Depth @@ -642,9 +642,9 @@ XBOX_REFURB_INFO, *PXBOX_REFURB_INFO; #define EXCEPTION_MAXIMUM_PARAMETERS 15 // maximum number of exception parameters -#define ALIGN_DOWN(length, type) ((ULONG)(length) & ~(sizeof(type) - 1)) -#define ALIGN_UP(length, type) (ALIGN_DOWN(((ULONG)(length) + sizeof(type) - 1), type)) -#define ALIGN_DOWN_POINTER(address, type) ((PVOID)((ULONG_PTR)(address) & ~((ULONG_PTR)sizeof(type) - 1))) +#define ALIGN_DOWN(length, type) ((ULONG)(length) & ~(sizeof(type) - 1)) +#define ALIGN_UP(length, type) (ALIGN_DOWN(((ULONG)(length) + sizeof(type) - 1), type)) +#define ALIGN_DOWN_POINTER(address, type) ((PVOID)((ULONG_PTR)(address) & ~((ULONG_PTR)sizeof(type) - 1))) #define ALIGN_UP_POINTER(address, type) (ALIGN_DOWN_POINTER(((ULONG_PTR)(address) + sizeof(type) - 1), type)) // ****************************************************************** @@ -1535,11 +1535,11 @@ KDPC, *PKDPC; // ****************************************************************** // * DPC queue entry structure // ****************************************************************** -typedef struct _DPC_QUEUE_ENTRY -{ - PKDPC Dpc; - PKDEFERRED_ROUTINE Routine; - PVOID Context; +typedef struct _DPC_QUEUE_ENTRY +{ + PKDPC Dpc; + PKDEFERRED_ROUTINE Routine; + PVOID Context; } DPC_QUEUE_ENTRY, *PDPC_QUEUE_ENTRY; @@ -1644,7 +1644,7 @@ KINTERRUPT_MODE; #define APC_LEVEL 1 #define DISPATCH_LEVEL 2 #define PROFILE_LEVEL 26 -#define CLOCK1_LEVEL 28 +#define CLOCK1_LEVEL 28 #define CLOCK2_LEVEL 28 #define SYNC_LEVEL 28 #define IPI_LEVEL 29 @@ -2616,43 +2616,43 @@ typedef struct _IO_COMPLETION_BASIC_INFORMATION { LONG Depth; } IO_COMPLETION_BASIC_INFORMATION, *PIO_COMPLETION_BASIC_INFORMATION; -typedef VOID(*PIDE_INTERRUPT_ROUTINE) (void); - -typedef VOID(*PIDE_FINISHIO_ROUTINE) (void); - -typedef BOOLEAN(*PIDE_POLL_RESET_COMPLETE_ROUTINE) (void); - -typedef VOID(*PIDE_TIMEOUT_EXPIRED_ROUTINE) (void); - -typedef VOID(*PIDE_START_PACKET_ROUTINE) ( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp -); - +typedef VOID(*PIDE_INTERRUPT_ROUTINE) (void); + +typedef VOID(*PIDE_FINISHIO_ROUTINE) (void); + +typedef BOOLEAN(*PIDE_POLL_RESET_COMPLETE_ROUTINE) (void); + +typedef VOID(*PIDE_TIMEOUT_EXPIRED_ROUTINE) (void); + +typedef VOID(*PIDE_START_PACKET_ROUTINE) ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +); + typedef VOID(*PIDE_START_NEXT_PACKET_ROUTINE) (void); -typedef struct _IDE_CHANNEL_OBJECT -{ - PIDE_INTERRUPT_ROUTINE InterruptRoutine; - PIDE_FINISHIO_ROUTINE FinishIoRoutine; - PIDE_POLL_RESET_COMPLETE_ROUTINE PollResetCompleteRoutine; - PIDE_TIMEOUT_EXPIRED_ROUTINE TimeoutExpiredRoutine; - PIDE_START_PACKET_ROUTINE StartPacketRoutine; - PIDE_START_NEXT_PACKET_ROUTINE StartNextPacketRoutine; - KIRQL InterruptIrql; - BOOLEAN ExpectingBusMasterInterrupt; - BOOLEAN StartPacketBusy; - BOOLEAN StartPacketRequested; - UCHAR Timeout; - UCHAR IoRetries; - UCHAR MaximumIoRetries; - PIRP CurrentIrp; - KDEVICE_QUEUE DeviceQueue; - ULONG PhysicalRegionDescriptorTablePhysical; - KDPC TimerDpc; - KDPC FinishDpc; - KTIMER Timer; - KINTERRUPT InterruptObject; +typedef struct _IDE_CHANNEL_OBJECT +{ + PIDE_INTERRUPT_ROUTINE InterruptRoutine; + PIDE_FINISHIO_ROUTINE FinishIoRoutine; + PIDE_POLL_RESET_COMPLETE_ROUTINE PollResetCompleteRoutine; + PIDE_TIMEOUT_EXPIRED_ROUTINE TimeoutExpiredRoutine; + PIDE_START_PACKET_ROUTINE StartPacketRoutine; + PIDE_START_NEXT_PACKET_ROUTINE StartNextPacketRoutine; + KIRQL InterruptIrql; + BOOLEAN ExpectingBusMasterInterrupt; + BOOLEAN StartPacketBusy; + BOOLEAN StartPacketRequested; + UCHAR Timeout; + UCHAR IoRetries; + UCHAR MaximumIoRetries; + PIRP CurrentIrp; + KDEVICE_QUEUE DeviceQueue; + ULONG PhysicalRegionDescriptorTablePhysical; + KDPC TimerDpc; + KDPC FinishDpc; + KTIMER Timer; + KINTERRUPT InterruptObject; } IDE_CHANNEL_OBJECT, *PIDE_CHANNEL_OBJECT; // ****************************************************************** diff --git a/setup.bat b/setup.bat index da7ff5dab..9bcb9701c 100644 --- a/setup.bat +++ b/setup.bat @@ -1,26 +1,26 @@ -@echo off - -REM Cxbx-Reloaded setup script -REM -REM Depends on git, cmake and Visual Studio being installed. - -echo Pulling latest version from git... -REM git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/ -git pull --recurse-submodules - -REM echo Synchronizing submodules... -REM git submodule update --init --recursive - -echo Initializing most recent Visual Studio build environment... -@call "%VS140COMNTOOLS%vsvars32.bat" - -echo Generating solution... -mkdir build -cd build -REM cmake .. -G "Visual Studio 16 2019" -A Win32 -cmake .. -A Win32 - -echo Building solution... -cmake --build . -j %NUMBER_OF_PROCESSORS% - -echo Done! Enjoy using Cxbx-Reloaded! +@echo off + +REM Cxbx-Reloaded setup script +REM +REM Depends on git, cmake and Visual Studio being installed. + +echo Pulling latest version from git... +REM git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/ +git pull --recurse-submodules + +REM echo Synchronizing submodules... +REM git submodule update --init --recursive + +echo Initializing most recent Visual Studio build environment... +@call "%VS140COMNTOOLS%vsvars32.bat" + +echo Generating solution... +mkdir build +cd build +REM cmake .. -G "Visual Studio 16 2019" -A Win32 +cmake .. -A Win32 + +echo Building solution... +cmake --build . -j %NUMBER_OF_PROCESSORS% + +echo Done! Enjoy using Cxbx-Reloaded! diff --git a/src/CxbxLoader/CxbxEmulator.cpp b/src/CxbxLoader/CxbxEmulator.cpp index b14e02448..1bfdfc657 100644 --- a/src/CxbxLoader/CxbxEmulator.cpp +++ b/src/CxbxLoader/CxbxEmulator.cpp @@ -1,75 +1,75 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * .,-::::: .,:: .::::::::. .,:: .: -// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;; -// * [[[ '[[,,[[' [[[__[[\. '[[,,[[' -// * $$$ Y$$$P $$""""Y$$ Y$$$P -// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo, -// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm, -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx is free software; you can redistribute it -// * and/or modify it under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2017-2019 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -// CxbxEmulator.cpp : Defines the exported functions for the DLL application. - -#include "stdafx.h" -#include "Cxbx.h" - -DWORD WINAPI Emulate() -{ - FUNC_EXPORTS - - /*! verify Cxbx-Loader.exe is loaded to base address 0x00010000 */ - if ((UINT_PTR)GetModuleHandle(nullptr) != CXBX_BASE_ADDR) - { - /*! CXBX_BASE_ADDR is defined as 0x00010000, which is the base address of - the Cxbx-Loader.exe host executable. - Set in Cxbx-Loader.exe Project options, Linker, Advanced, Base Address */ - MessageBox(NULL, "Cxbx-Loader.exe was not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)", "Cxbx-Reloaded", MB_OK); - return 1; - } - - /* Initialize Cxbx File Paths */ - CxbxInitFilePaths(); - - /*! initialize shared memory */ - if (!EmuShared::Init()) { - MessageBox(NULL, "Could not map shared memory!", "Cxbx-Reloaded", MB_OK); - return 1; - } - - LPSTR CommandLine = GetCommandLine(); - int argc; - PCHAR *argv = CommandLineToArgvA(CommandLine, &argc); - - CxbxKrnlMain(argc, argv); - - LocalFree(argv); - - /*! cleanup shared memory */ - EmuShared::Cleanup(); - - return 0; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * .,-::::: .,:: .::::::::. .,:: .: +// * ,;;;'````' `;;;, .,;; ;;;'';;' `;;;, .,;; +// * [[[ '[[,,[[' [[[__[[\. '[[,,[[' +// * $$$ Y$$$P $$""""Y$$ Y$$$P +// * `88bo,__,o, oP"``"Yo, _88o,,od8P oP"``"Yo, +// * "YUMMMMMP",m" "Mm,""YUMMMP" ,m" "Mm, +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx is free software; you can redistribute it +// * and/or modify it under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2017-2019 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +// CxbxEmulator.cpp : Defines the exported functions for the DLL application. + +#include "stdafx.h" +#include "Cxbx.h" + +DWORD WINAPI Emulate() +{ + FUNC_EXPORTS + + /*! verify Cxbx-Loader.exe is loaded to base address 0x00010000 */ + if ((UINT_PTR)GetModuleHandle(nullptr) != CXBX_BASE_ADDR) + { + /*! CXBX_BASE_ADDR is defined as 0x00010000, which is the base address of + the Cxbx-Loader.exe host executable. + Set in Cxbx-Loader.exe Project options, Linker, Advanced, Base Address */ + MessageBox(NULL, "Cxbx-Loader.exe was not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)", "Cxbx-Reloaded", MB_OK); + return 1; + } + + /* Initialize Cxbx File Paths */ + CxbxInitFilePaths(); + + /*! initialize shared memory */ + if (!EmuShared::Init()) { + MessageBox(NULL, "Could not map shared memory!", "Cxbx-Reloaded", MB_OK); + return 1; + } + + LPSTR CommandLine = GetCommandLine(); + int argc; + PCHAR *argv = CommandLineToArgvA(CommandLine, &argc); + + CxbxKrnlMain(argc, argv); + + LocalFree(argv); + + /*! cleanup shared memory */ + EmuShared::Cleanup(); + + return 0; +} diff --git a/src/HighPerformanceGraphicsEnabler.c b/src/HighPerformanceGraphicsEnabler.c index 3d74dd78b..29643f39d 100644 --- a/src/HighPerformanceGraphicsEnabler.c +++ b/src/HighPerformanceGraphicsEnabler.c @@ -1,5 +1,5 @@ -#include - -// Default to High Performance Mode on machines with dual graphics -__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; // AMD -__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; // NVIDIA +#include + +// Default to High Performance Mode on machines with dual graphics +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; // AMD +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; // NVIDIA diff --git a/src/common/EmuEEPROM.cpp b/src/common/EmuEEPROM.cpp index 9166c2225..da370455c 100644 --- a/src/common/EmuEEPROM.cpp +++ b/src/common/EmuEEPROM.cpp @@ -180,7 +180,7 @@ xboxkrnl::XBOX_EEPROM *CxbxRestoreEEPROM(char *szFilePath_EEPROM_bin) } // Read the HDD (and eventually also the online) keys stored in the eeprom file. Users can input them in the eeprom menu - memcpy(xboxkrnl::XboxHDKey, pEEPROM->EncryptedSettings.HDKey, xboxkrnl::XBOX_KEY_LENGTH); + memcpy(xboxkrnl::XboxHDKey, pEEPROM->EncryptedSettings.HDKey, xboxkrnl::XBOX_KEY_LENGTH); // Verify the checksum of the eeprom header UCHAR Checksum[20] = { 0 }; @@ -230,22 +230,22 @@ void EmuEEPROMReset(xboxkrnl::XBOX_EEPROM* eeprom) std::string serial = ""; for (int i = 0; i < 12; i++) { serial += std::to_string(serialDis(gen)); - } + } memset(eeprom->FactorySettings.SerialNumber, 0, 12); strncpy((char*)eeprom->FactorySettings.SerialNumber, serial.c_str(), 12); // Generate a random mac address - eeprom->FactorySettings.EthernetAddr[0] = 0x00; - eeprom->FactorySettings.EthernetAddr[1] = 0x50; + eeprom->FactorySettings.EthernetAddr[0] = 0x00; + eeprom->FactorySettings.EthernetAddr[1] = 0x50; eeprom->FactorySettings.EthernetAddr[2] = 0xF2; for (int i = 3; i < 6; i++) { eeprom->FactorySettings.EthernetAddr[i] = macOnlineDis(gen); } - + // Generate a random Online Key for (int i = 0; i < 16; i++) { eeprom->FactorySettings.OnlineKey[i] = macOnlineDis(gen); - } + } // TODO: TimeZone Settings } diff --git a/src/common/Logging.cpp b/src/common/Logging.cpp index 777af325b..8e4392534 100644 --- a/src/common/Logging.cpp +++ b/src/common/Logging.cpp @@ -27,119 +27,119 @@ #include // for PULONG -#include "Logging.h" -#include "common\Settings.hpp" +#include "Logging.h" +#include "common\Settings.hpp" #include "EmuShared.h" // For thread_local, see : https://en.cppreference.com/w/cpp/language/storage_duration // TODO : Use Boost.Format https://www.boost.org/doc/libs/1_53_0/libs/format/index.html -thread_local std::string _logThreadPrefix; - -std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)] = { false }; -const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = { - "CXBXR ", - "XBE ", - "INIT ", - "VMEM ", - "PMEM ", - "GUI ", - "EEPR ", - "RSA ", - "POOLMEM ", - "D3D8 ", - "D3DST ", - "D3DCVT ", - "DSOUND ", - "XAPI ", - "XACT ", - "XGRP ", - "XONLINE ", - "FS ", - "PSHB ", - "PXSH ", - "VTXSH ", - "VSHCACHE", - "VTXB ", - "DINP ", - "XINP ", - "SDL ", - "FILE ", - "X86 ", - "HLE ", - "NET ", - "MCPX ", - "NV2A ", - "SMC ", - "OHCI ", - "USB ", - "HUB ", - "XIDCTRL ", - "ADM ", - "INPSYS ", - "DSBUFFER", - "DSSTREAM", - "DS3DCALC", - "XMO ", - "KRNL ", - "LOG ", - "XBOX ", - "XBDM ", - "AV ", - "DBG ", - "EX ", - "FSC ", - "HAL ", - "IO ", - "KD ", - "KE ", - "KI ", - "MM ", - "NT ", - "OB ", - "PS ", - "RTL ", - "XC ", - "XE ", -}; -std::atomic_int g_CurrentLogLevel = to_underlying(LOG_LEVEL::INFO); -std::atomic_bool g_CurrentLogPopupTestCase = true; -static bool g_disablePopupMessages = false; - -const char log_debug[] = "DEBUG: "; -const char log_info[] = "INFO : "; -const char log_warn[] = "WARN : "; -const char log_error[] = "ERROR: "; -const char log_fatal[] = "FATAL: "; -const char log_unkwn[] = "???? : "; - -// Do not use EmuLogOutput function outside of this file. -void EmuLogOutput(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, const va_list argp) +thread_local std::string _logThreadPrefix; + +std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)] = { false }; +const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = { + "CXBXR ", + "XBE ", + "INIT ", + "VMEM ", + "PMEM ", + "GUI ", + "EEPR ", + "RSA ", + "POOLMEM ", + "D3D8 ", + "D3DST ", + "D3DCVT ", + "DSOUND ", + "XAPI ", + "XACT ", + "XGRP ", + "XONLINE ", + "FS ", + "PSHB ", + "PXSH ", + "VTXSH ", + "VSHCACHE", + "VTXB ", + "DINP ", + "XINP ", + "SDL ", + "FILE ", + "X86 ", + "HLE ", + "NET ", + "MCPX ", + "NV2A ", + "SMC ", + "OHCI ", + "USB ", + "HUB ", + "XIDCTRL ", + "ADM ", + "INPSYS ", + "DSBUFFER", + "DSSTREAM", + "DS3DCALC", + "XMO ", + "KRNL ", + "LOG ", + "XBOX ", + "XBDM ", + "AV ", + "DBG ", + "EX ", + "FSC ", + "HAL ", + "IO ", + "KD ", + "KE ", + "KI ", + "MM ", + "NT ", + "OB ", + "PS ", + "RTL ", + "XC ", + "XE ", +}; +std::atomic_int g_CurrentLogLevel = to_underlying(LOG_LEVEL::INFO); +std::atomic_bool g_CurrentLogPopupTestCase = true; +static bool g_disablePopupMessages = false; + +const char log_debug[] = "DEBUG: "; +const char log_info[] = "INFO : "; +const char log_warn[] = "WARN : "; +const char log_error[] = "ERROR: "; +const char log_fatal[] = "FATAL: "; +const char log_unkwn[] = "???? : "; + +// Do not use EmuLogOutput function outside of this file. +void EmuLogOutput(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, const va_list argp) { - LOG_THREAD_INIT; - - const char* level_str; + LOG_THREAD_INIT; + + const char* level_str; switch (level) { - default: - level_str = log_unkwn; + default: + level_str = log_unkwn; break; - case LOG_LEVEL::DEBUG: - level_str = log_debug; + case LOG_LEVEL::DEBUG: + level_str = log_debug; break; - case LOG_LEVEL::INFO: - level_str = log_info; + case LOG_LEVEL::INFO: + level_str = log_info; break; - case LOG_LEVEL::WARNING: - level_str = log_warn; - break; - case LOG_LEVEL::ERROR2: - level_str = log_error; + case LOG_LEVEL::WARNING: + level_str = log_warn; break; - case LOG_LEVEL::FATAL: - level_str = log_fatal; + case LOG_LEVEL::ERROR2: + level_str = log_error; + break; + case LOG_LEVEL::FATAL: + level_str = log_fatal; break; } - std::cout << _logThreadPrefix << level_str + std::cout << _logThreadPrefix << level_str << g_EnumModules2String[to_underlying(cxbxr_module)]; vfprintf(stdout, szWarningMessage, argp); @@ -147,23 +147,23 @@ void EmuLogOutput(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarn fprintf(stdout, "\n"); fflush(stdout); -} -inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const char *szWarningMessage, ...) +} +inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const char *szWarningMessage, ...) { va_list argp; va_start(argp, szWarningMessage); EmuLogOutput(cxbxr_module, level, szWarningMessage, argp); va_end(argp); -} - -// print out a custom message to the console or kernel debug log file -void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...) +} + +// print out a custom message to the console or kernel debug log file +void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...) { if (szWarningMessage == NULL) { return; } - - LOG_CHECK_ENABLED_EX(cxbxr_module, level) { + + LOG_CHECK_ENABLED_EX(cxbxr_module, level) { if (g_bPrintfOn) { LOG_THREAD_INIT; @@ -174,11 +174,11 @@ void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWa EmuLogOutput(cxbxr_module, level, szWarningMessage, argp); va_end(argp); - } + } } -} - -void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...) +} + +void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...) { if (szWarningMessage == NULL) { return; @@ -190,56 +190,56 @@ void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...) EmuLogOutput(CXBXR_MODULE::INIT, level, szWarningMessage, argp); va_end(argp); -} - -// Set up the logging variables for the GUI process -inline void log_get_settings() -{ +} + +// Set up the logging variables for the GUI process +inline void log_get_settings() +{ log_set_config(g_Settings->m_core.LogLevel, g_Settings->m_core.LoggedModules, g_Settings->m_core.bLogPopupTestCase); -} - -inline void log_sync_config() -{ - int LogLevel; - unsigned int LoggedModules[NUM_INTEGERS_LOG]; - bool LogPopupTestCase; - g_EmuShared->GetLogLv(&LogLevel); - g_EmuShared->GetLogModules(LoggedModules); - g_EmuShared->GetLogPopupTestCase(&LogPopupTestCase); +} + +inline void log_sync_config() +{ + int LogLevel; + unsigned int LoggedModules[NUM_INTEGERS_LOG]; + bool LogPopupTestCase; + g_EmuShared->GetLogLv(&LogLevel); + g_EmuShared->GetLogModules(LoggedModules); + g_EmuShared->GetLogPopupTestCase(&LogPopupTestCase); log_set_config(LogLevel, LoggedModules, LogPopupTestCase); -} - -void log_set_config(int LogLevel, unsigned int* LoggedModules, bool LogPopupTestCase) -{ - g_CurrentLogLevel = LogLevel; - for (unsigned int index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) { - if (LoggedModules[index / 32] & (1 << (index % 32))) { - g_EnabledModules[index] = true; - } - else { - g_EnabledModules[index] = false; - } +} + +void log_set_config(int LogLevel, unsigned int* LoggedModules, bool LogPopupTestCase) +{ + g_CurrentLogLevel = LogLevel; + for (unsigned int index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) { + if (LoggedModules[index / 32] & (1 << (index % 32))) { + g_EnabledModules[index] = true; + } + else { + g_EnabledModules[index] = false; + } } g_CurrentLogPopupTestCase = LogPopupTestCase; -} - -// Generate active log filter output. -void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module) +} + +// Generate active log filter output. +void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module) { - LOG_THREAD_INIT; - std::string generic_output_str = _logThreadPrefix + log_info + g_EnumModules2String[to_underlying(cxbxr_module)]; - - std::cout << generic_output_str << "Current log level: " << g_CurrentLogLevel << std::endl; - - generic_output_str.append("Active log filter: "); - for (unsigned int index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) { - if (g_EnabledModules[index]) { - std::cout << generic_output_str << g_EnumModules2String[index] << "\n"; - } - } + LOG_THREAD_INIT; + std::string generic_output_str = _logThreadPrefix + log_info + g_EnumModules2String[to_underlying(cxbxr_module)]; + + std::cout << generic_output_str << "Current log level: " << g_CurrentLogLevel << std::endl; + + generic_output_str.append("Active log filter: "); + for (unsigned int index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) { + if (g_EnabledModules[index]) { + std::cout << generic_output_str << g_EnumModules2String[index] << "\n"; + } + } std::cout << std::flush; -} - +} + // Use kernel managed environment void log_init_popup_msg() { @@ -250,10 +250,10 @@ void log_init_popup_msg() // TODO: Move PopupPlatformHandler into common GUI's window source code or use imgui in the future. // PopupPlatformHandler is intended to be use as internal wrapper function. -static PopupReturn PopupPlatformHandler(const char* msg, const PopupReturn ret_default, const UINT uType, const HWND hWnd) -{ - int ret = MessageBox(hWnd, msg, /*lpCaption=*/TEXT("Cxbx-Reloaded"), uType); - +static PopupReturn PopupPlatformHandler(const char* msg, const PopupReturn ret_default, const UINT uType, const HWND hWnd) +{ + int ret = MessageBox(hWnd, msg, /*lpCaption=*/TEXT("Cxbx-Reloaded"), uType); + switch (ret) { default: case IDCANCEL: @@ -271,73 +271,73 @@ static PopupReturn PopupPlatformHandler(const char* msg, const PopupReturn ret_d case IDNO: return PopupReturn::No; } -} - -PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const PopupIcon icon, const PopupButtons buttons, const PopupReturn ret_default, const char *message, ...) -{ - UINT uType = MB_TOPMOST | MB_SETFOREGROUND; - - // Make assert whenever the format string is null pointer which isn't allow in here. - assert(message != nullptr); - - switch (icon) { - case PopupIcon::Warning: { - uType |= MB_ICONWARNING; - break; - } - case PopupIcon::Error: { - uType |= MB_ICONERROR; // Note : MB_ICONERROR == MB_ICONSTOP == MB_ICONHAND - break; - } - case PopupIcon::Info: { - uType |= MB_ICONINFORMATION; - break; - } - case PopupIcon::Question: - case PopupIcon::Unknown: - default: { - uType |= MB_ICONQUESTION; - break; - } - } - - switch (buttons) { - default: - case PopupButtons::Ok: - uType |= MB_OK; - break; - case PopupButtons::OkCancel: - uType |= MB_OKCANCEL; - break; - case PopupButtons::AbortRetryIgnore: - uType |= MB_RETRYCANCEL; - break; - case PopupButtons::YesNoCancel: - uType |= MB_YESNOCANCEL; - break; - case PopupButtons::YesNo: - uType |= MB_YESNO; - break; - case PopupButtons::RetryCancel: - uType |= MB_RETRYCANCEL; - break; - } - - va_list argp; - va_start(argp, message); - // allocate predicted buffer size then write to buffer afterward. - std::vector Buffer(1+std::vsnprintf(nullptr, 0, message, argp)); - vsnprintf(Buffer.data(), Buffer.size(), message, argp); - va_end(argp); - - EmuLogOutputEx(cxbxr_module, level, "Popup : %s", Buffer); - - // If user is using exclusive fullscreen, we need to refrain all popups. - if (g_disablePopupMessages) { - return ret_default; - } - - return PopupPlatformHandler(Buffer.data(), ret_default, uType, (const HWND)hwnd); +} + +PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const PopupIcon icon, const PopupButtons buttons, const PopupReturn ret_default, const char *message, ...) +{ + UINT uType = MB_TOPMOST | MB_SETFOREGROUND; + + // Make assert whenever the format string is null pointer which isn't allow in here. + assert(message != nullptr); + + switch (icon) { + case PopupIcon::Warning: { + uType |= MB_ICONWARNING; + break; + } + case PopupIcon::Error: { + uType |= MB_ICONERROR; // Note : MB_ICONERROR == MB_ICONSTOP == MB_ICONHAND + break; + } + case PopupIcon::Info: { + uType |= MB_ICONINFORMATION; + break; + } + case PopupIcon::Question: + case PopupIcon::Unknown: + default: { + uType |= MB_ICONQUESTION; + break; + } + } + + switch (buttons) { + default: + case PopupButtons::Ok: + uType |= MB_OK; + break; + case PopupButtons::OkCancel: + uType |= MB_OKCANCEL; + break; + case PopupButtons::AbortRetryIgnore: + uType |= MB_RETRYCANCEL; + break; + case PopupButtons::YesNoCancel: + uType |= MB_YESNOCANCEL; + break; + case PopupButtons::YesNo: + uType |= MB_YESNO; + break; + case PopupButtons::RetryCancel: + uType |= MB_RETRYCANCEL; + break; + } + + va_list argp; + va_start(argp, message); + // allocate predicted buffer size then write to buffer afterward. + std::vector Buffer(1+std::vsnprintf(nullptr, 0, message, argp)); + vsnprintf(Buffer.data(), Buffer.size(), message, argp); + va_end(argp); + + EmuLogOutputEx(cxbxr_module, level, "Popup : %s", Buffer); + + // If user is using exclusive fullscreen, we need to refrain all popups. + if (g_disablePopupMessages) { + return ret_default; + } + + return PopupPlatformHandler(Buffer.data(), ret_default, uType, (const HWND)hwnd); } const bool needs_escape(const wint_t _char) @@ -453,7 +453,7 @@ LOG_SANITIZE_HEADER(sanitized_char_pointer, char *) while (*v && max_length--) { os << *v++; } - } + } return os << "\""; } @@ -467,7 +467,7 @@ LOG_SANITIZE_HEADER(sanitized_wchar_pointer, wchar_t *) return os << "NULL"; bool needsEscaping = false; - int max_length = container.max; + int max_length = container.max; while (*v && max_length--) if (needs_escape(*v++)) { diff --git a/src/common/Logging.h b/src/common/Logging.h index 0f6a61cf6..984e02395 100644 --- a/src/common/Logging.h +++ b/src/common/Logging.h @@ -17,7 +17,7 @@ // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * -// * (c) 2016 Patrick van Logchem +// * (c) 2016 Patrick van Logchem // * (c) 2019 ergo720 // * // * All rights reserved @@ -29,112 +29,112 @@ #include // For DWORD #include // For std::stringstream #include // For std::cout -#include // For std::setw +#include // For std::setw #include // For atomic_bool and atomic_uint -#include "common\util\CxbxUtil.h" // For g_bPrintfOn and to_underlying - -// NOTE: using ERROR2 since windows.h imports an ERROR macro which would conflict otherwise -typedef enum class _LOG_LEVEL { - DEBUG = 0, - INFO, - WARNING, - ERROR2, - FATAL, - MAX, +#include "common\util\CxbxUtil.h" // For g_bPrintfOn and to_underlying + +// NOTE: using ERROR2 since windows.h imports an ERROR macro which would conflict otherwise +typedef enum class _LOG_LEVEL { + DEBUG = 0, + INFO, + WARNING, + ERROR2, + FATAL, + MAX, }LOG_LEVEL; -typedef enum class _CXBXR_MODULE: unsigned int { - // general - CXBXR = 0, - XBE, - INIT, - VMEM, - PMEM, - GUI, - EEPR, - RSA, - POOLMEM, - D3D8, - D3DST, - D3DCVT, - DSOUND, - XAPI, - XACT, - XGRP, - XONLINE, - FS, - PSHB, - PXSH, - VTXSH, - VSHCACHE, - VTXB, - DINP, - XINP, - SDL, - FILE, - X86, - HLE, - NET, - MCPX, - NV2A, - SMC, - OHCI, - USB, - HUB, - XIDCTRL, - ADM, - INPSYS, - DSBUFFER, - DSSTREAM, - DS3DCALC, - XMO, - // kernel - KRNL, - LOG, - XBOX, - XBDM, - AV, - DBG, - EX, - FSC, - HAL, - IO, - KD, - KE, - KI, - MM, - NT, - OB, - PS, - RTL, - XC, - XE, - // max - MAX, -}CXBXR_MODULE; - -extern std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)]; -extern const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)]; -extern std::atomic_int g_CurrentLogLevel; -extern std::atomic_bool g_CurrentLogPopupTestCase; +typedef enum class _CXBXR_MODULE: unsigned int { + // general + CXBXR = 0, + XBE, + INIT, + VMEM, + PMEM, + GUI, + EEPR, + RSA, + POOLMEM, + D3D8, + D3DST, + D3DCVT, + DSOUND, + XAPI, + XACT, + XGRP, + XONLINE, + FS, + PSHB, + PXSH, + VTXSH, + VSHCACHE, + VTXB, + DINP, + XINP, + SDL, + FILE, + X86, + HLE, + NET, + MCPX, + NV2A, + SMC, + OHCI, + USB, + HUB, + XIDCTRL, + ADM, + INPSYS, + DSBUFFER, + DSSTREAM, + DS3DCALC, + XMO, + // kernel + KRNL, + LOG, + XBOX, + XBDM, + AV, + DBG, + EX, + FSC, + HAL, + IO, + KD, + KE, + KI, + MM, + NT, + OB, + PS, + RTL, + XC, + XE, + // max + MAX, +}CXBXR_MODULE; -// print out a log message to the console or kernel debug log file if level is high enough -void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...); -void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...); - -#define EmuLog(level, fmt, ...) EmuLogEx(LOG_PREFIX, level, fmt, ##__VA_ARGS__) - -extern inline void log_get_settings(); - -extern inline void log_sync_config(); - -void log_set_config(int LogLevel, unsigned int* LoggedModules, bool LogPopupTestCase); - -void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module); - -// Use emulation environment to manage popup messages -// If log_init_popup_msg is not called at earliest point of emulation. -// Then users will have a chance of popup message appear during start of emulation in full screen. +extern std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)]; +extern const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)]; +extern std::atomic_int g_CurrentLogLevel; +extern std::atomic_bool g_CurrentLogPopupTestCase; + +// print out a log message to the console or kernel debug log file if level is high enough +void NTAPI EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...); +void NTAPI EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...); + +#define EmuLog(level, fmt, ...) EmuLogEx(LOG_PREFIX, level, fmt, ##__VA_ARGS__) + +extern inline void log_get_settings(); + +extern inline void log_sync_config(); + +void log_set_config(int LogLevel, unsigned int* LoggedModules, bool LogPopupTestCase); + +void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module); + +// Use emulation environment to manage popup messages +// If log_init_popup_msg is not called at earliest point of emulation. +// Then users will have a chance of popup message appear during start of emulation in full screen. void log_init_popup_msg(); typedef enum class _PopupIcon { @@ -164,7 +164,7 @@ typedef enum class _PopupReturn { Ignore, Yes, No -} PopupReturn; +} PopupReturn; PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const PopupIcon icon, const PopupButtons buttons, const PopupReturn ret_default, const char* message, ...); @@ -173,11 +173,11 @@ PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, con #define PopupQuestion(hwnd, fmt, ...) PopupQuestionEx(hwnd, LOG_LEVEL::INFO, PopupButtons::YesNoCancel, PopupReturn::Cancel, fmt, ## __VA_ARGS__) #define PopupInfoEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::INFO, PopupIcon::Info, buttons, ret_default, fmt, ## __VA_ARGS__) #define PopupInfo(hwnd, fmt, ...) (void)PopupInfoEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__) -#define PopupWarningEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::WARNING, PopupIcon::Warning, buttons, ret_default, fmt, ## __VA_ARGS__) +#define PopupWarningEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::WARNING, PopupIcon::Warning, buttons, ret_default, fmt, ## __VA_ARGS__) #define PopupWarning(hwnd, fmt, ...) (void)PopupWarningEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__) -#define PopupErrorEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::ERROR2, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__) -#define PopupError(hwnd, fmt, ...) (void)PopupErrorEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__) -#define PopupFatalEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::FATAL, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__) +#define PopupErrorEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::ERROR2, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__) +#define PopupError(hwnd, fmt, ...) (void)PopupErrorEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__) +#define PopupFatalEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::FATAL, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__) #define PopupFatal(hwnd, fmt, ...) (void)PopupFatalEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__) // For LOG_TEST_CASE @@ -199,7 +199,7 @@ extern inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEV EmuLogOutputEx(LOG_PREFIX, LOG_LEVEL::INFO, "Please report that %s shows the following message:\nLOG_TEST_CASE: %s\nIn %s (%s line %d)", \ CxbxKrnl_Xbe->m_szAsciiTitle, message, __func__, __FILE__, __LINE__); \ } while (0) -// was g_pCertificate->wszTitleName +// was g_pCertificate->wszTitleName // // __FILENAME__ @@ -257,12 +257,12 @@ inline const char * _log_sanitize(BOOL value, int ignored_length = 0) #define LOG_SANITIZE_HEADER(C, T) \ std::ostream& operator<<( \ std::ostream& os, \ - const Sane##C& container) \ + const Sane##C& container) \ #define LOG_SANITIZE(C, T) \ struct Sane##C \ { \ - T value; \ + T value; \ int max; \ Sane##C(T _v, int _m = 80) : value(_v), max(_m) { } \ }; \ @@ -328,14 +328,14 @@ constexpr const char* remove_emupatch_prefix(const char* str) { // For thread_local, see : https://en.cppreference.com/w/cpp/language/storage_duration // TODO : Use Boost.Format https://www.boost.org/doc/libs/1_53_0/libs/format/index.html extern thread_local std::string _logThreadPrefix; - -// Checks if this log should be printed or not -#define LOG_CHECK_ENABLED_EX(cxbxr_module, level) \ - if (g_EnabledModules[to_underlying(cxbxr_module)] && to_underlying(level) >= g_CurrentLogLevel) -// Checks if this log should be printed or not -#define LOG_CHECK_ENABLED(level) \ - LOG_CHECK_ENABLED_EX(LOG_PREFIX, level) +// Checks if this log should be printed or not +#define LOG_CHECK_ENABLED_EX(cxbxr_module, level) \ + if (g_EnabledModules[to_underlying(cxbxr_module)] && to_underlying(level) >= g_CurrentLogLevel) + +// Checks if this log should be printed or not +#define LOG_CHECK_ENABLED(level) \ + LOG_CHECK_ENABLED_EX(LOG_PREFIX, level) #define LOG_THREAD_INIT \ if (_logThreadPrefix.length() == 0) { \ @@ -366,7 +366,7 @@ extern thread_local std::string _logThreadPrefix; msg << _logThreadPrefix << _logFuncPrefix << "("; #define LOG_FUNC_BEGIN \ - LOG_INIT \ + LOG_INIT \ LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \ LOG_FUNC_BEGIN_NO_INIT @@ -388,9 +388,9 @@ extern thread_local std::string _logThreadPrefix; // LOG_FUNC_END closes off function and optional argument logging #define LOG_FUNC_END \ if (_had_arg) msg << "\n"; \ - msg << ");\n"; \ + msg << ");\n"; \ std::cout << msg.str(); \ - } } while (0); \ + } } while (0); \ } #define LOG_FUNC_BEGIN_ARG_RESULT_NO_INIT \ @@ -441,23 +441,23 @@ extern thread_local std::string _logThreadPrefix; // LOG_FORWARD indicates that an api is implemented by a forward to another API #define LOG_FORWARD(api) \ - LOG_INIT \ + LOG_INIT \ LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \ do { if(g_bPrintfOn) { \ std::cout << _logThreadPrefix << _logFuncPrefix << " forwarding to "#api"...\n"; \ - } } while (0); \ + } } while (0); \ } // LOG_IGNORED indicates that Cxbx consiously ignores an api #define LOG_IGNORED() \ do { \ static bool b_echoOnce = true; \ - if(g_bPrintfOn && b_echoOnce) { \ + if(g_bPrintfOn && b_echoOnce) { \ LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \ LOG_THREAD_INIT \ LOG_FUNC_INIT(__func__) \ std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " ignored!\n"; \ - b_echoOnce = false; \ + b_echoOnce = false; \ } \ } \ } while(0) @@ -466,12 +466,12 @@ extern thread_local std::string _logThreadPrefix; #define LOG_UNIMPLEMENTED() \ do { \ static bool b_echoOnce = true; \ - if(g_bPrintfOn && b_echoOnce) { \ + if(g_bPrintfOn && b_echoOnce) { \ LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \ LOG_THREAD_INIT \ LOG_FUNC_INIT(__func__) \ std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " unimplemented!\n"; \ - b_echoOnce = false; \ + b_echoOnce = false; \ } \ } \ } while(0) @@ -480,12 +480,12 @@ extern thread_local std::string _logThreadPrefix; #define LOG_INCOMPLETE() \ do { \ static bool b_echoOnce = true; \ - if(g_bPrintfOn && b_echoOnce) { \ + if(g_bPrintfOn && b_echoOnce) { \ LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \ LOG_THREAD_INIT \ LOG_FUNC_INIT(__func__) \ std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " incomplete!\n"; \ - b_echoOnce = false; \ + b_echoOnce = false; \ } \ } \ } while(0) @@ -494,12 +494,12 @@ extern thread_local std::string _logThreadPrefix; #define LOG_NOT_SUPPORTED() \ do { \ static bool b_echoOnce = true; \ - if(g_bPrintfOn && b_echoOnce) { \ + if(g_bPrintfOn && b_echoOnce) { \ LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \ LOG_THREAD_INIT \ LOG_FUNC_INIT(__func__) \ std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " not supported!\n"; \ - b_echoOnce = false; \ + b_echoOnce = false; \ } \ } \ } while(0) @@ -626,6 +626,6 @@ LOGRENDER_HEADER_BY_REF(Type) \ // An example type rendering, for PVOID // -LOGRENDER_HEADER_BY_REF(PVOID); +LOGRENDER_HEADER_BY_REF(PVOID); #endif _LOGGING_H diff --git a/src/common/ReservedMemory.h b/src/common/ReservedMemory.h index fe3e10e23..6901b4bd7 100644 --- a/src/common/ReservedMemory.h +++ b/src/common/ReservedMemory.h @@ -52,4 +52,4 @@ unsigned char virtual_memory_placeholder[VM_PLACEHOLDER_SIZE]; // = { OPCODE_NOP #endif #endif -#endif // RESERVEDMEMORY_H +#endif // RESERVEDMEMORY_H diff --git a/src/common/Settings.cpp b/src/common/Settings.cpp index fd05f6882..3b5482a3f 100644 --- a/src/common/Settings.cpp +++ b/src/common/Settings.cpp @@ -82,7 +82,7 @@ static struct { const char* RecentXbeFiles = "RecentXbeFiles"; const char* DataStorageToggle = "DataStorageToggle"; const char* DataCustomLocation = "DataCustomLocation"; - const char* IgnoreInvalidXbeSig = "IgnoreInvalidXbeSig"; + const char* IgnoreInvalidXbeSig = "IgnoreInvalidXbeSig"; const char *IgnoreInvalidXbeSec = "IgnoreInvalidXbeSec"; } sect_gui_keys; @@ -316,7 +316,7 @@ bool Settings::LoadConfig() index++; } - m_gui.bIgnoreInvalidXbeSig = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, /*Default=*/false); + m_gui.bIgnoreInvalidXbeSig = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, /*Default=*/false); m_gui.bIgnoreInvalidXbeSec = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, /*Default=*/false); // ==== GUI End ============= @@ -453,17 +453,17 @@ bool Settings::LoadConfig() // ==== Input Profile Begin ==== std::array, to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)> control_names; - - for (int device = 0; device < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); device++) { + + for (int device = 0; device < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); device++) { if (dev_num_buttons[device] == 0) { continue; - } + } for (int i = 0; i < dev_num_buttons[device]; i++) { char control_name[30]; std::sprintf(control_name, sect_input_profiles.control, button_xbox_ctrl_names[i][0]); control_names[device].push_back(control_name); - } + } } // TODO: add the control names of the other devices @@ -519,7 +519,7 @@ bool Settings::Save(std::string file_path) m_si.SetValue(section_gui, sect_gui_keys.RecentXbeFiles, m_gui.szRecentXbeFiles[i].c_str(), nullptr, false); } - m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, m_gui.bIgnoreInvalidXbeSig, nullptr, true); + m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, m_gui.bIgnoreInvalidXbeSig, nullptr, true); m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, m_gui.bIgnoreInvalidXbeSec, nullptr, true); // ==== GUI End ============= @@ -598,16 +598,16 @@ bool Settings::Save(std::string file_path) // ==== Input Profile Begin ==== std::array, to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)> control_names; - for (int device = 0; device < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); device++) { + for (int device = 0; device < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX); device++) { if (dev_num_buttons[device] == 0) { continue; - } + } for (int i = 0; i < dev_num_buttons[device]; i++) { char control_name[30]; std::sprintf(control_name, sect_input_profiles.control, button_xbox_ctrl_names[i][0]); control_names[device].push_back(control_name); - } + } } // TODO: add the control names of the other devices diff --git a/src/common/Settings.hpp b/src/common/Settings.hpp index 648175083..0b7db2b0b 100644 --- a/src/common/Settings.hpp +++ b/src/common/Settings.hpp @@ -18,7 +18,7 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2002-2003 Aaron Robinson -// * (c) 2017-2018 RadWolfie +// * (c) 2017-2018 RadWolfie // * (c) 2019 ergo720 // * // * All rights reserved @@ -28,21 +28,21 @@ #define SETTINGS_HPP #include "Cxbx.h" -#include "SimpleIni.h" -#include "input\InputDevice.h" +#include "SimpleIni.h" +#include "input\InputDevice.h" #include "common\util\CxbxUtil.h" -#include -#include - +#include +#include + extern std::string g_exec_filepath; - -// Individual library version -extern uint16_t g_LibVersion_D3D8; + +// Individual library version +extern uint16_t g_LibVersion_D3D8; extern uint16_t g_LibVersion_DSOUND; #define szSettings_alloc_error "ERROR: Unable to allocate Settings class." #define assert_check_shared_memory(type) \ - "Invalid "#type" size, please verify structure is align, not adding new member, or is using placeholder reserves." \ + "Invalid "#type" size, please verify structure is align, not adding new member, or is using placeholder reserves." \ " Otherwise, please perform versioning upgrade and update "#type" sizeof check." // Cxbx-Reloaded's data storage location. @@ -51,12 +51,12 @@ typedef enum _CXBX_DATA { CXBX_DATA_APPDATA = 0, CXBX_DATA_EXECDIR = 1, CXBX_DATA_CUSTOM = 2, -} CXBX_DATA; - +} CXBX_DATA; + // ****************************************************************** // * Define number of integers required to store logging settings // ****************************************************************** -#define NUM_INTEGERS_LOG 2 +#define NUM_INTEGERS_LOG 2 enum { LLE_NONE = 0, @@ -88,18 +88,18 @@ public: std::string szRecentXbeFiles[10]; unsigned int DataStorageToggle; std::string szCustomLocation = ""; - bool bIgnoreInvalidXbeSig; + bool bIgnoreInvalidXbeSig; bool bIgnoreInvalidXbeSec; } m_gui; // Core settings - struct s_core { + struct s_core { unsigned int Revision; unsigned int FlagsLLE; DebugMode KrnlDebugMode; char szKrnlDebug[MAX_PATH] = ""; char szStorageLocation[MAX_PATH] = ""; - unsigned int LoggedModules[NUM_INTEGERS_LOG]; + unsigned int LoggedModules[NUM_INTEGERS_LOG]; int LogLevel = 1; bool bUseLoaderExec; bool allowAdminPrivilege; @@ -116,7 +116,7 @@ public: unsigned int direct3DDevice; bool bVSync; bool bFullScreen; - bool bHardwareYUV; + bool bHardwareYUV; bool Reserved3; int renderScaleFactor = 1; int Reserved99[9] = { 0 }; @@ -133,34 +133,34 @@ public: int Reserved99[14] = { 0 }; } m_audio; static_assert(sizeof(s_audio) == 0x4C, assert_check_shared_memory(s_audio)); - - struct s_input { - int Type; - std::string DeviceName; - std::string ProfileName; - }; - std::array m_input; - - struct s_input_profiles { - int Type; - std::string ProfileName; - std::string DeviceName; - std::vector ControlList; - }; - std::array, to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)> m_input_profiles; - + + struct s_input { + int Type; + std::string DeviceName; + std::string ProfileName; + }; + std::array m_input; + + struct s_input_profiles { + int Type; + std::string ProfileName; + std::string DeviceName; + std::vector ControlList; + }; + std::array, to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)> m_input_profiles; + // Network settings struct s_network { char adapter_name[MAX_PATH] = ""; } m_network; static_assert(sizeof(s_network) == 0x104, assert_check_shared_memory(s_network)); - // Hack settings - // NOTE: When removing fields, replace them with place-holders - // The size and order of this structure should *not* be allowed to change + // Hack settings + // NOTE: When removing fields, replace them with place-holders + // The size and order of this structure should *not* be allowed to change // TODO: Fix IPC/Shared Memory so this isn't necessary struct s_hack { - bool DisablePixelShaders; + bool DisablePixelShaders; bool Reserved2; bool UseAllCores; bool SkipRdtscPatching; @@ -172,7 +172,7 @@ public: } m_hacks; static_assert(sizeof(s_hack) == 0x28, assert_check_shared_memory(s_hack)); -private: +private: void RemoveLegacyConfigs(unsigned int CurrentRevision); std::string m_file_path = ""; CSimpleIniA m_si; diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index 0a055fc51..15b2a22e1 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -24,32 +24,32 @@ // * All rights reserved // * // ****************************************************************** - + #ifdef _WIN32 -#include +#include #endif #include -#include +#include #include #include "Timer.h" -#include "common\util\CxbxUtil.h" +#include "common\util\CxbxUtil.h" #include "core\kernel\init\CxbxKrnl.h" #ifdef __linux__ #include #endif - -// Virtual clocks will probably become useful once LLE CPU is implemented, but for now we don't need them. + +// Virtual clocks will probably become useful once LLE CPU is implemented, but for now we don't need them. // See the QEMUClockType QEMU_CLOCK_VIRTUAL of XQEMU for more info. #define CLOCK_REALTIME 0 //#define CLOCK_VIRTUALTIME 1 // Vector storing all the timers created -static std::vector TimerList; +static std::vector TimerList; // The frequency of the high resolution clock of the host -uint64_t HostClockFrequency; -// Lock to acquire when accessing TimerList +uint64_t HostClockFrequency; +// Lock to acquire when accessing TimerList std::mutex TimerMtx; @@ -61,8 +61,8 @@ uint64_t GetTime_NS(TimerObject* Timer) QueryPerformanceCounter(&li); uint64_t Ret = Muldiv64(li.QuadPart, SCALE_S_IN_NS, (uint32_t)HostClockFrequency); #elif __linux__ - static struct timespec ts; - clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + static struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); uint64_t Ret = Muldiv64(ts.tv_sec, SCALE_S_IN_NS, 1) + ts.tv_nsec; #else #error "Unsupported OS" @@ -79,16 +79,16 @@ static inline uint64_t GetNextExpireTime(TimerObject* Timer) // Deallocates the memory of the timer void Timer_Destroy(TimerObject* Timer) { - unsigned int index, i; - std::lock_guardlock(TimerMtx); - + unsigned int index, i; + std::lock_guardlock(TimerMtx); + index = TimerList.size(); for (i = 0; i < index; i++) { if (Timer == TimerList[i]) { index = i; } } - + assert(index != TimerList.size()); delete Timer; TimerList.erase(TimerList.begin() + index); @@ -96,12 +96,12 @@ void Timer_Destroy(TimerObject* Timer) // Thread that runs the timer void ClockThread(TimerObject* Timer) -{ - uint64_t NewExpireTime; - +{ + uint64_t NewExpireTime; + if (!Timer->Name.empty()) { CxbxSetThreadName(Timer->Name.c_str()); - } + } if (Timer->CpuAffinity != nullptr) { InitXboxThread(*Timer->CpuAffinity); } @@ -115,7 +115,7 @@ void ClockThread(TimerObject* Timer) } Timer->Callback(Timer->Opaque); NewExpireTime = GetNextExpireTime(Timer); - } + } Sleep(1); // prevent burning the cpu } } @@ -134,22 +134,22 @@ void Timer_Exit(TimerObject* Timer) // Allocates the memory for the timer object TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, unsigned long* Affinity) -{ +{ std::lock_guardlock(TimerMtx); TimerObject* pTimer = new TimerObject; pTimer->Type = CLOCK_REALTIME; pTimer->Callback = Callback; pTimer->ExpireTime_MS.store(0); pTimer->Exit.store(false); - pTimer->Opaque = Arg; - Name.empty() ? pTimer->Name = "Unnamed thread" : pTimer->Name = Name; + pTimer->Opaque = Arg; + Name.empty() ? pTimer->Name = "Unnamed thread" : pTimer->Name = Name; pTimer->CpuAffinity = Affinity; TimerList.emplace_back(pTimer); return pTimer; } -// Starts the timer +// Starts the timer // Expire_MS must be expressed in NS void Timer_Start(TimerObject* Timer, uint64_t Expire_MS) { diff --git a/src/common/Timer.h b/src/common/Timer.h index 7d907ad62..3ca073241 100644 --- a/src/common/Timer.h +++ b/src/common/Timer.h @@ -28,13 +28,13 @@ #ifndef TIMER_H #define TIMER_H -#include - +#include + #define SCALE_S_IN_NS 1000000000 #define SCALE_MS_IN_NS 1000000 #define SCALE_US_IN_NS 1000 -#define SCALE_NS_IN_NS 1 - +#define SCALE_NS_IN_NS 1 + #define SCALE_S_IN_US 1000000 #define SCALE_MS_IN_US 1000 #define SCALE_US_IN_US 1 @@ -47,8 +47,8 @@ typedef struct _TimerObject std::atomic_uint64_t ExpireTime_MS; // when the timer expires (ms) std::atomic_bool Exit; // indicates that the timer should be destroyed TimerCB Callback; // function to call when the timer expires - void* Opaque; // opaque argument to pass to the callback - std::string Name; // the name of the timer thread (if any) + void* Opaque; // opaque argument to pass to the callback + std::string Name; // the name of the timer thread (if any) unsigned long* CpuAffinity; // the cpu affinity of the timer thread (if any) } TimerObject; diff --git a/src/common/crypto/EmuDes.cpp b/src/common/crypto/EmuDes.cpp index 34aff2655..506135703 100644 --- a/src/common/crypto/EmuDes.cpp +++ b/src/common/crypto/EmuDes.cpp @@ -23,665 +23,665 @@ // * // * All rights reserved // * -// ****************************************************************** - -// Acknowledgment: ReactOS (GPLv2) -// https://github.com/reactos/reactos - -/* -* FIPS-46-3 compliant Triple-DES implementation -* -* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved -* SPDX-License-Identifier: GPL-2.0 -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License along -* with this program; if not, write to the Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -* -* This file is part of mbed TLS (https://tls.mbed.org) -*/ -/* -* DES, on which TDES is based, was originally designed by Horst Feistel -* at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). -* -* https://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf -*/ - -// Changed from ReactOS: we never swap the subkeys in order to generate a decryption key schedule. Instead, we opt to simply read the subkeys -// in reverse order during a decryption operation. This is necessary because XcKeyTable doesn't provide an "operation" argument, that is, it -// always generates an encryption key schedule. - -// NOTE: this des implementation doesn't produce exactly the same ciphertext produced by the Xbox. I found that the implementation of Eric Young -// used in OpenSSL does instead, but we can't use it since it's under the Apache 2.0 license, which is incompatible with GPLv2. For reference, -// the DES-YOUN.zip package at https://www.schneier.com/books/applied_cryptography/source.html contains a previous version of the same code under -// the GPLv2 but again it doesn't produce the same ciphertext, and modifying it to make it so would make it identical to the OpenSSL code, which -// is probably a violation of the license, so I won't do it. In practice, as long as the code correctly decrypts the ciphertext (which it does), -// I don't think that the Xbox games will care if the ciphertext is not exactly the same as on the Xbox and should work fine offline. The only -// problem with this is that cxbxr will be unable to communicate with a real Xbox on the network (des is used in console-to-console communications) -// because the des key schedule generated on the console will be different and so it will fail to decrypt packets encrypted by cxbxr. - - -#include -#include -#include "EmuDes.h" - -/* -* 32-bit integer manipulation macros (big endian) -*/ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_BE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) ); \ -} -#endif - -/* -* Expanded DES S-boxes -*/ -static const uint32_t SB1[64] = -{ - 0x01010400, 0x00000000, 0x00010000, 0x01010404, - 0x01010004, 0x00010404, 0x00000004, 0x00010000, - 0x00000400, 0x01010400, 0x01010404, 0x00000400, - 0x01000404, 0x01010004, 0x01000000, 0x00000004, - 0x00000404, 0x01000400, 0x01000400, 0x00010400, - 0x00010400, 0x01010000, 0x01010000, 0x01000404, - 0x00010004, 0x01000004, 0x01000004, 0x00010004, - 0x00000000, 0x00000404, 0x00010404, 0x01000000, - 0x00010000, 0x01010404, 0x00000004, 0x01010000, - 0x01010400, 0x01000000, 0x01000000, 0x00000400, - 0x01010004, 0x00010000, 0x00010400, 0x01000004, - 0x00000400, 0x00000004, 0x01000404, 0x00010404, - 0x01010404, 0x00010004, 0x01010000, 0x01000404, - 0x01000004, 0x00000404, 0x00010404, 0x01010400, - 0x00000404, 0x01000400, 0x01000400, 0x00000000, - 0x00010004, 0x00010400, 0x00000000, 0x01010004 -}; - -static const uint32_t SB2[64] = -{ - 0x80108020, 0x80008000, 0x00008000, 0x00108020, - 0x00100000, 0x00000020, 0x80100020, 0x80008020, - 0x80000020, 0x80108020, 0x80108000, 0x80000000, - 0x80008000, 0x00100000, 0x00000020, 0x80100020, - 0x00108000, 0x00100020, 0x80008020, 0x00000000, - 0x80000000, 0x00008000, 0x00108020, 0x80100000, - 0x00100020, 0x80000020, 0x00000000, 0x00108000, - 0x00008020, 0x80108000, 0x80100000, 0x00008020, - 0x00000000, 0x00108020, 0x80100020, 0x00100000, - 0x80008020, 0x80100000, 0x80108000, 0x00008000, - 0x80100000, 0x80008000, 0x00000020, 0x80108020, - 0x00108020, 0x00000020, 0x00008000, 0x80000000, - 0x00008020, 0x80108000, 0x00100000, 0x80000020, - 0x00100020, 0x80008020, 0x80000020, 0x00100020, - 0x00108000, 0x00000000, 0x80008000, 0x00008020, - 0x80000000, 0x80100020, 0x80108020, 0x00108000 -}; - -static const uint32_t SB3[64] = -{ - 0x00000208, 0x08020200, 0x00000000, 0x08020008, - 0x08000200, 0x00000000, 0x00020208, 0x08000200, - 0x00020008, 0x08000008, 0x08000008, 0x00020000, - 0x08020208, 0x00020008, 0x08020000, 0x00000208, - 0x08000000, 0x00000008, 0x08020200, 0x00000200, - 0x00020200, 0x08020000, 0x08020008, 0x00020208, - 0x08000208, 0x00020200, 0x00020000, 0x08000208, - 0x00000008, 0x08020208, 0x00000200, 0x08000000, - 0x08020200, 0x08000000, 0x00020008, 0x00000208, - 0x00020000, 0x08020200, 0x08000200, 0x00000000, - 0x00000200, 0x00020008, 0x08020208, 0x08000200, - 0x08000008, 0x00000200, 0x00000000, 0x08020008, - 0x08000208, 0x00020000, 0x08000000, 0x08020208, - 0x00000008, 0x00020208, 0x00020200, 0x08000008, - 0x08020000, 0x08000208, 0x00000208, 0x08020000, - 0x00020208, 0x00000008, 0x08020008, 0x00020200 -}; - -static const uint32_t SB4[64] = -{ - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802080, 0x00800081, 0x00800001, 0x00002001, - 0x00000000, 0x00802000, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00800080, 0x00800001, - 0x00000001, 0x00002000, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002001, 0x00002080, - 0x00800081, 0x00000001, 0x00002080, 0x00800080, - 0x00002000, 0x00802080, 0x00802081, 0x00000081, - 0x00800080, 0x00800001, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00000000, 0x00802000, - 0x00002080, 0x00800080, 0x00800081, 0x00000001, - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802081, 0x00000081, 0x00000001, 0x00002000, - 0x00800001, 0x00002001, 0x00802080, 0x00800081, - 0x00002001, 0x00002080, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002000, 0x00802080 -}; - -static const uint32_t SB5[64] = -{ - 0x00000100, 0x02080100, 0x02080000, 0x42000100, - 0x00080000, 0x00000100, 0x40000000, 0x02080000, - 0x40080100, 0x00080000, 0x02000100, 0x40080100, - 0x42000100, 0x42080000, 0x00080100, 0x40000000, - 0x02000000, 0x40080000, 0x40080000, 0x00000000, - 0x40000100, 0x42080100, 0x42080100, 0x02000100, - 0x42080000, 0x40000100, 0x00000000, 0x42000000, - 0x02080100, 0x02000000, 0x42000000, 0x00080100, - 0x00080000, 0x42000100, 0x00000100, 0x02000000, - 0x40000000, 0x02080000, 0x42000100, 0x40080100, - 0x02000100, 0x40000000, 0x42080000, 0x02080100, - 0x40080100, 0x00000100, 0x02000000, 0x42080000, - 0x42080100, 0x00080100, 0x42000000, 0x42080100, - 0x02080000, 0x00000000, 0x40080000, 0x42000000, - 0x00080100, 0x02000100, 0x40000100, 0x00080000, - 0x00000000, 0x40080000, 0x02080100, 0x40000100 -}; - -static const uint32_t SB6[64] = -{ - 0x20000010, 0x20400000, 0x00004000, 0x20404010, - 0x20400000, 0x00000010, 0x20404010, 0x00400000, - 0x20004000, 0x00404010, 0x00400000, 0x20000010, - 0x00400010, 0x20004000, 0x20000000, 0x00004010, - 0x00000000, 0x00400010, 0x20004010, 0x00004000, - 0x00404000, 0x20004010, 0x00000010, 0x20400010, - 0x20400010, 0x00000000, 0x00404010, 0x20404000, - 0x00004010, 0x00404000, 0x20404000, 0x20000000, - 0x20004000, 0x00000010, 0x20400010, 0x00404000, - 0x20404010, 0x00400000, 0x00004010, 0x20000010, - 0x00400000, 0x20004000, 0x20000000, 0x00004010, - 0x20000010, 0x20404010, 0x00404000, 0x20400000, - 0x00404010, 0x20404000, 0x00000000, 0x20400010, - 0x00000010, 0x00004000, 0x20400000, 0x00404010, - 0x00004000, 0x00400010, 0x20004010, 0x00000000, - 0x20404000, 0x20000000, 0x00400010, 0x20004010 -}; - -static const uint32_t SB7[64] = -{ - 0x00200000, 0x04200002, 0x04000802, 0x00000000, - 0x00000800, 0x04000802, 0x00200802, 0x04200800, - 0x04200802, 0x00200000, 0x00000000, 0x04000002, - 0x00000002, 0x04000000, 0x04200002, 0x00000802, - 0x04000800, 0x00200802, 0x00200002, 0x04000800, - 0x04000002, 0x04200000, 0x04200800, 0x00200002, - 0x04200000, 0x00000800, 0x00000802, 0x04200802, - 0x00200800, 0x00000002, 0x04000000, 0x00200800, - 0x04000000, 0x00200800, 0x00200000, 0x04000802, - 0x04000802, 0x04200002, 0x04200002, 0x00000002, - 0x00200002, 0x04000000, 0x04000800, 0x00200000, - 0x04200800, 0x00000802, 0x00200802, 0x04200800, - 0x00000802, 0x04000002, 0x04200802, 0x04200000, - 0x00200800, 0x00000000, 0x00000002, 0x04200802, - 0x00000000, 0x00200802, 0x04200000, 0x00000800, - 0x04000002, 0x04000800, 0x00000800, 0x00200002 -}; - -static const uint32_t SB8[64] = -{ - 0x10001040, 0x00001000, 0x00040000, 0x10041040, - 0x10000000, 0x10001040, 0x00000040, 0x10000000, - 0x00040040, 0x10040000, 0x10041040, 0x00041000, - 0x10041000, 0x00041040, 0x00001000, 0x00000040, - 0x10040000, 0x10000040, 0x10001000, 0x00001040, - 0x00041000, 0x00040040, 0x10040040, 0x10041000, - 0x00001040, 0x00000000, 0x00000000, 0x10040040, - 0x10000040, 0x10001000, 0x00041040, 0x00040000, - 0x00041040, 0x00040000, 0x10041000, 0x00001000, - 0x00000040, 0x10040040, 0x00001000, 0x00041040, - 0x10001000, 0x00000040, 0x10000040, 0x10040000, - 0x10040040, 0x10000000, 0x00040000, 0x10001040, - 0x00000000, 0x10041040, 0x00040040, 0x10000040, - 0x10040000, 0x10001000, 0x10001040, 0x00000000, - 0x10041040, 0x00041000, 0x00041000, 0x00001040, - 0x00001040, 0x00040040, 0x10000000, 0x10041000 -}; - -/* -* PC1: left and right halves bit-swap -*/ -static const uint32_t LHs[16] = -{ - 0x00000000, 0x00000001, 0x00000100, 0x00000101, - 0x00010000, 0x00010001, 0x00010100, 0x00010101, - 0x01000000, 0x01000001, 0x01000100, 0x01000101, - 0x01010000, 0x01010001, 0x01010100, 0x01010101 -}; - -static const uint32_t RHs[16] = -{ - 0x00000000, 0x01000000, 0x00010000, 0x01010000, - 0x00000100, 0x01000100, 0x00010100, 0x01010100, - 0x00000001, 0x01000001, 0x00010001, 0x01010001, - 0x00000101, 0x01000101, 0x00010101, 0x01010101, -}; - -/* -* Initial Permutation macro -*/ -#define DES_IP(X,Y) \ -{ \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ - X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ -} - -/* -* Final Permutation macro -*/ -#define DES_FP(X,Y) \ -{ \ - X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ - Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ -} - -/* -* DES round macro -*/ -#define DES_ROUND(X,Y,I) \ -{ \ - T = (SK[I] ^ X); \ - Y ^= SB8[ (T ) & 0x3F ] ^ \ - SB6[ (T >> 8) & 0x3F ] ^ \ - SB4[ (T >> 16) & 0x3F ] ^ \ - SB2[ (T >> 24) & 0x3F ]; \ - \ - T = (SK[I+1] ^ ((X << 28) | (X >> 4))); \ - Y ^= SB7[ (T ) & 0x3F ] ^ \ - SB5[ (T >> 8) & 0x3F ] ^ \ - SB3[ (T >> 16) & 0x3F ] ^ \ - SB1[ (T >> 24) & 0x3F ]; \ -} - -// For each number between 0x0 and 0xF, this tells how many set bits there are -static const unsigned char DESParityTable[] = { 0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04 }; - - +// ****************************************************************** + +// Acknowledgment: ReactOS (GPLv2) +// https://github.com/reactos/reactos + +/* +* FIPS-46-3 compliant Triple-DES implementation +* +* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved +* SPDX-License-Identifier: GPL-2.0 +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* This file is part of mbed TLS (https://tls.mbed.org) +*/ +/* +* DES, on which TDES is based, was originally designed by Horst Feistel +* at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). +* +* https://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf +*/ + +// Changed from ReactOS: we never swap the subkeys in order to generate a decryption key schedule. Instead, we opt to simply read the subkeys +// in reverse order during a decryption operation. This is necessary because XcKeyTable doesn't provide an "operation" argument, that is, it +// always generates an encryption key schedule. + +// NOTE: this des implementation doesn't produce exactly the same ciphertext produced by the Xbox. I found that the implementation of Eric Young +// used in OpenSSL does instead, but we can't use it since it's under the Apache 2.0 license, which is incompatible with GPLv2. For reference, +// the DES-YOUN.zip package at https://www.schneier.com/books/applied_cryptography/source.html contains a previous version of the same code under +// the GPLv2 but again it doesn't produce the same ciphertext, and modifying it to make it so would make it identical to the OpenSSL code, which +// is probably a violation of the license, so I won't do it. In practice, as long as the code correctly decrypts the ciphertext (which it does), +// I don't think that the Xbox games will care if the ciphertext is not exactly the same as on the Xbox and should work fine offline. The only +// problem with this is that cxbxr will be unable to communicate with a real Xbox on the network (des is used in console-to-console communications) +// because the des key schedule generated on the console will be different and so it will fail to decrypt packets encrypted by cxbxr. + + +#include +#include +#include "EmuDes.h" + +/* +* 32-bit integer manipulation macros (big endian) +*/ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* +* Expanded DES S-boxes +*/ +static const uint32_t SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* +* PC1: left and right halves bit-swap +*/ +static const uint32_t LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* +* Initial Permutation macro +*/ +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* +* Final Permutation macro +*/ +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* +* DES round macro +*/ +#define DES_ROUND(X,Y,I) \ +{ \ + T = (SK[I] ^ X); \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = (SK[I+1] ^ ((X << 28) | (X >> 4))); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +// For each number between 0x0 and 0xF, this tells how many set bits there are +static const unsigned char DESParityTable[] = { 0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04 }; + + // This function sets the parity on the DES key to be odd -// Test case: Halo, Tenchu, Dashboard, Splinter Cell 1 and 2, ... -void mbedtls_des_key_set_parity(unsigned char* Key, unsigned long KeyLenght) -{ - unsigned long i; - +// Test case: Halo, Tenchu, Dashboard, Splinter Cell 1 and 2, ... +void mbedtls_des_key_set_parity(unsigned char* Key, unsigned long KeyLenght) +{ + unsigned long i; + for (i = 0; i < KeyLenght; i++) { if (!((DESParityTable[Key[i] >> 4] + DESParityTable[Key[i] & 0x0F]) % 2)) { Key[i] = Key[i] ^ 0x01; } - } -} - -void mbedtls_des_setkey(uint32_t SK[32], const unsigned char key[MBEDTLS_DES_KEY_SIZE]) -{ - int i; - uint32_t X, Y, T; - - GET_UINT32_BE(X, key, 0); - GET_UINT32_BE(Y, key, 4); - - /* - * Permuted Choice 1 - */ - T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); - T = ((Y) ^ X) & 0x10101010; X ^= T; Y ^= (T); - - X = (LHs[(X) & 0xF] << 3) | (LHs[(X >> 8) & 0xF] << 2) - | (LHs[(X >> 16) & 0xF] << 1) | (LHs[(X >> 24) & 0xF]) - | (LHs[(X >> 5) & 0xF] << 7) | (LHs[(X >> 13) & 0xF] << 6) - | (LHs[(X >> 21) & 0xF] << 5) | (LHs[(X >> 29) & 0xF] << 4); - - Y = (RHs[(Y >> 1) & 0xF] << 3) | (RHs[(Y >> 9) & 0xF] << 2) - | (RHs[(Y >> 17) & 0xF] << 1) | (RHs[(Y >> 25) & 0xF]) - | (RHs[(Y >> 4) & 0xF] << 7) | (RHs[(Y >> 12) & 0xF] << 6) - | (RHs[(Y >> 20) & 0xF] << 5) | (RHs[(Y >> 28) & 0xF] << 4); - - X &= 0x0FFFFFFF; - Y &= 0x0FFFFFFF; - - /* - * calculate subkeys - */ - for (i = 0; i < 16; i++) - { - if (i < 2 || i == 8 || i == 15) - { - X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; - Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; - } - else - { - X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; - Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; - } - - *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) - | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) - | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) - | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) - | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) - | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) - | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) - | ((Y >> 14) & 0x00000200) | ((Y) & 0x00000100) - | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) - | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) - | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); - - *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) - | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) - | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) - | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) - | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) - | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) - | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) - | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) - | ((Y) & 0x00000200) | ((Y << 7) & 0x00000100) - | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) - | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); - } -} - -/* -* DES key schedule (56-bit, encryption) -*/ -void mbedtls_des_setkey_enc(mbedtls_des_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]) -{ - mbedtls_des_setkey(ctx->sk, key); -} - -/* -* DES-ECB block encryption/decryption -*/ -void mbedtls_des_crypt_ecb(mbedtls_des_context* ctx, - const unsigned char input[8], - unsigned char output[8], - unsigned long encrypt) -{ - int i; - uint32_t X, Y, T, *SK; - - SK = ctx->sk; - - GET_UINT32_BE(X, input, 0); - GET_UINT32_BE(Y, input, 4); - - DES_IP(X, Y); - - if (encrypt == MBEDTLS_DES_ENCRYPT) { - for (i = 0; i < 32; i += 4) - { - DES_ROUND(Y, X, i + 0); - DES_ROUND(X, Y, i + 2); - } - } - else { - for (i = 30; i > 0; i -= 4) - { - DES_ROUND(Y, X, i - 0); - DES_ROUND(X, Y, i - 2); - } - } - - DES_FP(Y, X); - - PUT_UINT32_BE(Y, output, 0); - PUT_UINT32_BE(X, output, 4); -} - -/* -* DES-CBC buffer encryption/decryption -*/ -int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, - unsigned long mode, - unsigned long length, - unsigned char iv[8], - const unsigned char* input, - unsigned char* output) -{ - int i, ret, num_des_blocks; - unsigned char temp[8]; - - ret = 0; - num_des_blocks = (length + 7) / 8; - - // The original code of ReactOS correctly checks that the input length is a multiple of a des_block (8 bytes) but the - // kernel doesn't and will encrypt up to block (lenght + 7) / 8. This means that we'll run the risk of reading some - // random bytes after the buffer end and/or touching invalid memory and crash. Because the real kernel does it, we'll - // allow this buggy behaviour for the sake of accuracy. - if (length % 8) { - ret = MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; - } - - if (mode == MBEDTLS_DES_ENCRYPT) - { - while (num_des_blocks > 0) - { - for (i = 0; i < 8; i++) { - output[i] = (unsigned char)(input[i] ^ iv[i]); - } - - mbedtls_des_crypt_ecb(ctx, output, output, MBEDTLS_DES_ENCRYPT); - memcpy(iv, output, 8); - - input += 8; - output += 8; - num_des_blocks--; - } - } - else /* MBEDTLS_DES_DECRYPT */ - { - while (num_des_blocks > 0) - { - memcpy(temp, input, 8); - mbedtls_des_crypt_ecb(ctx, input, output, MBEDTLS_DES_DECRYPT); - - for (i = 0; i < 8; i++) { - output[i] = (unsigned char)(output[i] ^ iv[i]); - } - - memcpy(iv, temp, 8); - - input += 8; - output += 8; - num_des_blocks--; - } - } - - return ret; -} - -/* -* Triple-DES key schedule (168-bit, encryption) -*/ -void mbedtls_des3_set3key_enc(mbedtls_des3_context* ctx, - const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]) -{ - mbedtls_des_setkey(ctx->sk, key); - mbedtls_des_setkey(ctx->sk + 32, key + 8); - mbedtls_des_setkey(ctx->sk + 64, key + 16); -} - -/* -* 3DES-ECB buffer encryption -*/ -void mbedtls_des3_encrypt_ecb(mbedtls_des3_context* ctx, - const unsigned char input[8], - unsigned char output[8]) -{ - int i; - uint32_t X, Y, T, *SK; - - SK = ctx->sk; - - GET_UINT32_BE(X, input, 0); - GET_UINT32_BE(Y, input, 4); - - DES_IP(X, Y); - - for (i = 0; i < 32; i += 4) - { - DES_ROUND(Y, X, i + 0); - DES_ROUND(X, Y, i + 2); - } - - for (i = 62; i > 32; i -= 4) - { - DES_ROUND(X, Y, i - 0); - DES_ROUND(Y, X, i - 2); - } - - for (i = 64; i < 96; i += 4) - { - DES_ROUND(Y, X, i + 0); - DES_ROUND(X, Y, i + 2); - } - - DES_FP(Y, X); - - PUT_UINT32_BE(Y, output, 0); - PUT_UINT32_BE(X, output, 4); -} - -/* -* 3DES-ECB buffer decryption -*/ -void mbedtls_des3_decrypt_ecb(mbedtls_des3_context* ctx, - const unsigned char input[8], - unsigned char output[8]) -{ - int i; - uint32_t X, Y, T, *SK; - - SK = ctx->sk; - - GET_UINT32_BE(X, input, 0); - GET_UINT32_BE(Y, input, 4); - - DES_IP(X, Y); - - for (i = 94; i > 64; i -= 4) - { - DES_ROUND(Y, X, i - 0); - DES_ROUND(X, Y, i - 2); - } - - for (i = 32; i < 64; i += 4) - { - DES_ROUND(X, Y, i + 0); - DES_ROUND(Y, X, i + 2); - } - - for (i = 30; i > 0; i -= 4) - { - DES_ROUND(Y, X, i - 0); - DES_ROUND(X, Y, i - 2); - } - - DES_FP(Y, X); - - PUT_UINT32_BE(Y, output, 0); - PUT_UINT32_BE(X, output, 4); -} - -void mbedtls_des3_crypt_ecb(mbedtls_des3_context* ctx, - const unsigned char input[8], - unsigned char output[8], - unsigned long encrypt) -{ - if (encrypt == MBEDTLS_DES_ENCRYPT) { - mbedtls_des3_encrypt_ecb(ctx, input, output); - } - else { - mbedtls_des3_decrypt_ecb(ctx, input, output); - } -} - -/* -* 3DES-CBC buffer encryption/decryption -*/ -int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, - unsigned long mode, - unsigned long length, - unsigned char iv[8], - const unsigned char *input, - unsigned char *output) -{ - int i, ret, num_des_blocks; - unsigned char temp[8]; - - ret = 0; - num_des_blocks = (length + 7) / 8; - - if (length % 8) { - ret = MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; - } - - if (mode == MBEDTLS_DES_ENCRYPT) - { - while (num_des_blocks > 0) - { - for (i = 0; i < 8; i++) { - output[i] = (unsigned char)(input[i] ^ iv[i]); - } - - mbedtls_des3_encrypt_ecb(ctx, output, output); - memcpy(iv, output, 8); - - input += 8; - output += 8; - num_des_blocks--; - } - } - else /* MBEDTLS_DES_DECRYPT */ - { - while (num_des_blocks > 0) - { - memcpy(temp, input, 8); - mbedtls_des3_decrypt_ecb(ctx, input, output); - - for (i = 0; i < 8; i++) { - output[i] = (unsigned char)(output[i] ^ iv[i]); - } - - memcpy(iv, temp, 8); - - input += 8; - output += 8; - num_des_blocks--; - } - } - - return ret; -} + } +} + +void mbedtls_des_setkey(uint32_t SK[32], const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + uint32_t X, Y, T; + + GET_UINT32_BE(X, key, 0); + GET_UINT32_BE(Y, key, 4); + + /* + * Permuted Choice 1 + */ + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y) ^ X) & 0x10101010; X ^= T; Y ^= (T); + + X = (LHs[(X) & 0xF] << 3) | (LHs[(X >> 8) & 0xF] << 2) + | (LHs[(X >> 16) & 0xF] << 1) | (LHs[(X >> 24) & 0xF]) + | (LHs[(X >> 5) & 0xF] << 7) | (LHs[(X >> 13) & 0xF] << 6) + | (LHs[(X >> 21) & 0xF] << 5) | (LHs[(X >> 29) & 0xF] << 4); + + Y = (RHs[(Y >> 1) & 0xF] << 3) | (RHs[(Y >> 9) & 0xF] << 2) + | (RHs[(Y >> 17) & 0xF] << 1) | (RHs[(Y >> 25) & 0xF]) + | (RHs[(Y >> 4) & 0xF] << 7) | (RHs[(Y >> 12) & 0xF] << 6) + | (RHs[(Y >> 20) & 0xF] << 5) | (RHs[(Y >> 28) & 0xF] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* + * calculate subkeys + */ + for (i = 0; i < 16; i++) + { + if (i < 2 || i == 8 || i == 15) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* +* DES key schedule (56-bit, encryption) +*/ +void mbedtls_des_setkey_enc(mbedtls_des_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + mbedtls_des_setkey(ctx->sk, key); +} + +/* +* DES-ECB block encryption/decryption +*/ +void mbedtls_des_crypt_ecb(mbedtls_des_context* ctx, + const unsigned char input[8], + unsigned char output[8], + unsigned long encrypt) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE(X, input, 0); + GET_UINT32_BE(Y, input, 4); + + DES_IP(X, Y); + + if (encrypt == MBEDTLS_DES_ENCRYPT) { + for (i = 0; i < 32; i += 4) + { + DES_ROUND(Y, X, i + 0); + DES_ROUND(X, Y, i + 2); + } + } + else { + for (i = 30; i > 0; i -= 4) + { + DES_ROUND(Y, X, i - 0); + DES_ROUND(X, Y, i - 2); + } + } + + DES_FP(Y, X); + + PUT_UINT32_BE(Y, output, 0); + PUT_UINT32_BE(X, output, 4); +} + +/* +* DES-CBC buffer encryption/decryption +*/ +int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, + unsigned long mode, + unsigned long length, + unsigned char iv[8], + const unsigned char* input, + unsigned char* output) +{ + int i, ret, num_des_blocks; + unsigned char temp[8]; + + ret = 0; + num_des_blocks = (length + 7) / 8; + + // The original code of ReactOS correctly checks that the input length is a multiple of a des_block (8 bytes) but the + // kernel doesn't and will encrypt up to block (lenght + 7) / 8. This means that we'll run the risk of reading some + // random bytes after the buffer end and/or touching invalid memory and crash. Because the real kernel does it, we'll + // allow this buggy behaviour for the sake of accuracy. + if (length % 8) { + ret = MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_DES_ENCRYPT) + { + while (num_des_blocks > 0) + { + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(input[i] ^ iv[i]); + } + + mbedtls_des_crypt_ecb(ctx, output, output, MBEDTLS_DES_ENCRYPT); + memcpy(iv, output, 8); + + input += 8; + output += 8; + num_des_blocks--; + } + } + else /* MBEDTLS_DES_DECRYPT */ + { + while (num_des_blocks > 0) + { + memcpy(temp, input, 8); + mbedtls_des_crypt_ecb(ctx, input, output, MBEDTLS_DES_DECRYPT); + + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(output[i] ^ iv[i]); + } + + memcpy(iv, temp, 8); + + input += 8; + output += 8; + num_des_blocks--; + } + } + + return ret; +} + +/* +* Triple-DES key schedule (168-bit, encryption) +*/ +void mbedtls_des3_set3key_enc(mbedtls_des3_context* ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]) +{ + mbedtls_des_setkey(ctx->sk, key); + mbedtls_des_setkey(ctx->sk + 32, key + 8); + mbedtls_des_setkey(ctx->sk + 64, key + 16); +} + +/* +* 3DES-ECB buffer encryption +*/ +void mbedtls_des3_encrypt_ecb(mbedtls_des3_context* ctx, + const unsigned char input[8], + unsigned char output[8]) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE(X, input, 0); + GET_UINT32_BE(Y, input, 4); + + DES_IP(X, Y); + + for (i = 0; i < 32; i += 4) + { + DES_ROUND(Y, X, i + 0); + DES_ROUND(X, Y, i + 2); + } + + for (i = 62; i > 32; i -= 4) + { + DES_ROUND(X, Y, i - 0); + DES_ROUND(Y, X, i - 2); + } + + for (i = 64; i < 96; i += 4) + { + DES_ROUND(Y, X, i + 0); + DES_ROUND(X, Y, i + 2); + } + + DES_FP(Y, X); + + PUT_UINT32_BE(Y, output, 0); + PUT_UINT32_BE(X, output, 4); +} + +/* +* 3DES-ECB buffer decryption +*/ +void mbedtls_des3_decrypt_ecb(mbedtls_des3_context* ctx, + const unsigned char input[8], + unsigned char output[8]) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE(X, input, 0); + GET_UINT32_BE(Y, input, 4); + + DES_IP(X, Y); + + for (i = 94; i > 64; i -= 4) + { + DES_ROUND(Y, X, i - 0); + DES_ROUND(X, Y, i - 2); + } + + for (i = 32; i < 64; i += 4) + { + DES_ROUND(X, Y, i + 0); + DES_ROUND(Y, X, i + 2); + } + + for (i = 30; i > 0; i -= 4) + { + DES_ROUND(Y, X, i - 0); + DES_ROUND(X, Y, i - 2); + } + + DES_FP(Y, X); + + PUT_UINT32_BE(Y, output, 0); + PUT_UINT32_BE(X, output, 4); +} + +void mbedtls_des3_crypt_ecb(mbedtls_des3_context* ctx, + const unsigned char input[8], + unsigned char output[8], + unsigned long encrypt) +{ + if (encrypt == MBEDTLS_DES_ENCRYPT) { + mbedtls_des3_encrypt_ecb(ctx, input, output); + } + else { + mbedtls_des3_decrypt_ecb(ctx, input, output); + } +} + +/* +* 3DES-CBC buffer encryption/decryption +*/ +int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, + unsigned long mode, + unsigned long length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output) +{ + int i, ret, num_des_blocks; + unsigned char temp[8]; + + ret = 0; + num_des_blocks = (length + 7) / 8; + + if (length % 8) { + ret = MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_DES_ENCRYPT) + { + while (num_des_blocks > 0) + { + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(input[i] ^ iv[i]); + } + + mbedtls_des3_encrypt_ecb(ctx, output, output); + memcpy(iv, output, 8); + + input += 8; + output += 8; + num_des_blocks--; + } + } + else /* MBEDTLS_DES_DECRYPT */ + { + while (num_des_blocks > 0) + { + memcpy(temp, input, 8); + mbedtls_des3_decrypt_ecb(ctx, input, output); + + for (i = 0; i < 8; i++) { + output[i] = (unsigned char)(output[i] ^ iv[i]); + } + + memcpy(iv, temp, 8); + + input += 8; + output += 8; + num_des_blocks--; + } + } + + return ret; +} diff --git a/src/common/crypto/EmuDes.h b/src/common/crypto/EmuDes.h index db7a9c398..7874966ed 100644 --- a/src/common/crypto/EmuDes.h +++ b/src/common/crypto/EmuDes.h @@ -23,42 +23,42 @@ // * // * All rights reserved // * -// ****************************************************************** - -#ifndef EMUDES_H -#define EMUDES_H - -#define MBEDTLS_DES_KEY_SIZE 8 - -#define MBEDTLS_DES_ENCRYPT 1 -#define MBEDTLS_DES_DECRYPT 0 - -#define MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH -0x0032 /**< The data input has an invalid length. */ - -/** -* \brief DES context structure -*/ -typedef struct -{ - uint32_t sk[32]; /*!< DES subkeys */ -} -mbedtls_des_context; - -/** -* \brief Triple-DES context structure -*/ -typedef struct -{ - uint32_t sk[96]; /*!< 3DES subkeys */ -} -mbedtls_des3_context; - -void mbedtls_des_key_set_parity(unsigned char* Key, unsigned long KeyLenght); -void mbedtls_des_setkey_enc(mbedtls_des_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]); -void mbedtls_des_crypt_ecb(mbedtls_des_context* ctx, const unsigned char input[8], unsigned char output[8], unsigned long encrypt); -int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, unsigned long mode, unsigned long length, unsigned char iv[8], const unsigned char* input, unsigned char* output); -void mbedtls_des3_set3key_enc(mbedtls_des3_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]); -void mbedtls_des3_crypt_ecb(mbedtls_des3_context* ctx, const unsigned char input[8], unsigned char output[8], unsigned long encrypt); -int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, unsigned long mode, unsigned long length, unsigned char iv[8], const unsigned char *input, unsigned char *output); - -#endif EMUDES_H +// ****************************************************************** + +#ifndef EMUDES_H +#define EMUDES_H + +#define MBEDTLS_DES_KEY_SIZE 8 + +#define MBEDTLS_DES_ENCRYPT 1 +#define MBEDTLS_DES_DECRYPT 0 + +#define MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH -0x0032 /**< The data input has an invalid length. */ + +/** +* \brief DES context structure +*/ +typedef struct +{ + uint32_t sk[32]; /*!< DES subkeys */ +} +mbedtls_des_context; + +/** +* \brief Triple-DES context structure +*/ +typedef struct +{ + uint32_t sk[96]; /*!< 3DES subkeys */ +} +mbedtls_des3_context; + +void mbedtls_des_key_set_parity(unsigned char* Key, unsigned long KeyLenght); +void mbedtls_des_setkey_enc(mbedtls_des_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]); +void mbedtls_des_crypt_ecb(mbedtls_des_context* ctx, const unsigned char input[8], unsigned char output[8], unsigned long encrypt); +int mbedtls_des_crypt_cbc(mbedtls_des_context* ctx, unsigned long mode, unsigned long length, unsigned char iv[8], const unsigned char* input, unsigned char* output); +void mbedtls_des3_set3key_enc(mbedtls_des3_context* ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]); +void mbedtls_des3_crypt_ecb(mbedtls_des3_context* ctx, const unsigned char input[8], unsigned char output[8], unsigned long encrypt); +int mbedtls_des3_crypt_cbc(mbedtls_des3_context* ctx, unsigned long mode, unsigned long length, unsigned char iv[8], const unsigned char *input, unsigned char *output); + +#endif EMUDES_H diff --git a/src/common/crypto/EmuRsa.cpp b/src/common/crypto/EmuRsa.cpp index f7b151659..c89f01bf0 100644 --- a/src/common/crypto/EmuRsa.cpp +++ b/src/common/crypto/EmuRsa.cpp @@ -1,161 +1,161 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2018 ergo720 -// * (c) 2019 Jannik Vogel -// * -// * All rights reserved -// * -// ****************************************************************** - -// Acknowledgment: -// verify_hash, RSApkcs1paddingtable and RSA_PUBLIC_KEY are from the -// file xboxlib.c of the xbedump tool (and that file only, GPLv2). -// https://github.com/XboxDev/xbedump/blob/master/xboxlib.c -// mbedtls_swap_endianness is extracted from mbedtls_mpi_read_binary used in the file bignum.h of ReactOS -// https://github.com/reactos/reactos - -// xboxlib.c license -/*************************************************************************** -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -***************************************************************************/ - -/** -* \file bignum.h -* -* \brief Multi-precision integer library -* -* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved -* SPDX-License-Identifier: GPL-2.0 -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License along -* with this program; if not, write to the Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -* -* This file is part of mbed TLS (https://tls.mbed.org) -*/ - -#define LOG_PREFIX CXBXR_MODULE::RSA - -#include "EmuRsa.h" -#include "core\kernel\support\Emu.h" // For EmuLog -#include "tomcrypt.h" -#include "tommath.h" - -#define CHK_MP_RET(x) do { int ret = (x); if (ret != MP_OKAY) return false; } while(0) - - +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018 ergo720 +// * (c) 2019 Jannik Vogel +// * +// * All rights reserved +// * +// ****************************************************************** + +// Acknowledgment: +// verify_hash, RSApkcs1paddingtable and RSA_PUBLIC_KEY are from the +// file xboxlib.c of the xbedump tool (and that file only, GPLv2). +// https://github.com/XboxDev/xbedump/blob/master/xboxlib.c +// mbedtls_swap_endianness is extracted from mbedtls_mpi_read_binary used in the file bignum.h of ReactOS +// https://github.com/reactos/reactos + +// xboxlib.c license +/*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +***************************************************************************/ + +/** +* \file bignum.h +* +* \brief Multi-precision integer library +* +* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved +* SPDX-License-Identifier: GPL-2.0 +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* This file is part of mbed TLS (https://tls.mbed.org) +*/ + +#define LOG_PREFIX CXBXR_MODULE::RSA + +#include "EmuRsa.h" +#include "core\kernel\support\Emu.h" // For EmuLog +#include "tomcrypt.h" +#include "tommath.h" + +#define CHK_MP_RET(x) do { int ret = (x); if (ret != MP_OKAY) return false; } while(0) + + const unsigned char RSApkcs1paddingtable[3][16] = { { 0x0F, 0x14,0x04,0x00,0x05,0x1A,0x02,0x03,0x0E,0x2B,0x05,0x06,0x09,0x30,0x21,0x30 }, { 0x0D, 0x14,0x04,0x1A,0x02,0x03,0x0E,0x2B,0x05,0x06,0x07,0x30,0x1F,0x30,0x00,0x00 }, { 0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } -}; - -// Move this to CxbxUtil.h if it's ever needed in other places of the emu as well -void mbedtls_swap_endianness(const unsigned char* in_buf, unsigned char* out_buf, size_t size) -{ - size_t i, j, n; - uint32_t* out_buf_uint = (uint32_t*)out_buf; - - memset(out_buf_uint, 0, size); - - for (n = 0; n < size; n++) - if (in_buf[n] != 0) - break; - - for (i = size, j = 0; i > n; i--, j++) { - out_buf_uint[j / 4] |= ((uint32_t)in_buf[i - 1]) << ((j % 4) << 3); - } -} - -void init_tom_lib() -{ - // NOTE: init_LTM has been deprecated in favor to crypt_mp_init("L"). However, in the latest master branch crypt_mp_init - // is still undefined. - static bool need_init = true; - if (need_init) { - init_LTM(); - need_init = false; - } -} - -bool xbox_exp_mod(unsigned char* pA, const unsigned char* pB, const unsigned char* pC, const unsigned char* pD, - size_t a_size, size_t b_size, size_t c_size, size_t d_size) -{ - mp_int a, b, c, d; - CHK_MP_RET(mp_init(&a)); - CHK_MP_RET(mp_init(&b)); - CHK_MP_RET(mp_init(&c)); - CHK_MP_RET(mp_init(&d)); - CHK_MP_RET(mp_import(&b, 1, -1, b_size, 0, 0, pB)); - CHK_MP_RET(mp_import(&c, 1, -1, c_size, 0, 0, pC)); - CHK_MP_RET(mp_import(&d, 1, -1, d_size, 0, 0, pD)); - - CHK_MP_RET(mp_exptmod(&b, &c, &d, &a)); - - CHK_MP_RET(mp_export(pA, NULL, -1, a_size, 0, 0, &a)); - - return true; -} - -bool xbox_rsa_public(const unsigned char* in_buf, unsigned char* out_buf, RSA_PUBLIC_KEY key) -{ - rsa_key tom_key; - unsigned char in_buf_be[256] = { 0 }; - unsigned char out_buf_be[256] = { 0 }; - unsigned long out_len = 256; - unsigned char modulus_be[256] = { 0 }; - unsigned char exp_be[4] = { 0 }; - - // We must swap the data since libtom expects the data to be in big endian - mbedtls_swap_endianness (key.KeyData.Modulus, modulus_be, 256); - mbedtls_swap_endianness (key.KeyData.Exponent, exp_be, 4); - mbedtls_swap_endianness (in_buf, in_buf_be, 256); - if (rsa_set_key(modulus_be, 256, exp_be, 4, NULL, 0, &tom_key) != CRYPT_OK) { - EmuLog(LOG_LEVEL::WARNING, "Failed to load rsa key"); - return false; - } - if (rsa_exptmod(in_buf_be, 256, out_buf_be, &out_len, PK_PUBLIC, &tom_key) != CRYPT_OK) { - EmuLog(LOG_LEVEL::WARNING, "rsa_exptmod failed"); - return false; - } - mbedtls_swap_endianness(out_buf_be, out_buf, 256); - return true; -} - -bool verify_hash(const unsigned char* hash, const unsigned char* decryptBuffer, RSA_PUBLIC_KEY key) -{ +}; + +// Move this to CxbxUtil.h if it's ever needed in other places of the emu as well +void mbedtls_swap_endianness(const unsigned char* in_buf, unsigned char* out_buf, size_t size) +{ + size_t i, j, n; + uint32_t* out_buf_uint = (uint32_t*)out_buf; + + memset(out_buf_uint, 0, size); + + for (n = 0; n < size; n++) + if (in_buf[n] != 0) + break; + + for (i = size, j = 0; i > n; i--, j++) { + out_buf_uint[j / 4] |= ((uint32_t)in_buf[i - 1]) << ((j % 4) << 3); + } +} + +void init_tom_lib() +{ + // NOTE: init_LTM has been deprecated in favor to crypt_mp_init("L"). However, in the latest master branch crypt_mp_init + // is still undefined. + static bool need_init = true; + if (need_init) { + init_LTM(); + need_init = false; + } +} + +bool xbox_exp_mod(unsigned char* pA, const unsigned char* pB, const unsigned char* pC, const unsigned char* pD, + size_t a_size, size_t b_size, size_t c_size, size_t d_size) +{ + mp_int a, b, c, d; + CHK_MP_RET(mp_init(&a)); + CHK_MP_RET(mp_init(&b)); + CHK_MP_RET(mp_init(&c)); + CHK_MP_RET(mp_init(&d)); + CHK_MP_RET(mp_import(&b, 1, -1, b_size, 0, 0, pB)); + CHK_MP_RET(mp_import(&c, 1, -1, c_size, 0, 0, pC)); + CHK_MP_RET(mp_import(&d, 1, -1, d_size, 0, 0, pD)); + + CHK_MP_RET(mp_exptmod(&b, &c, &d, &a)); + + CHK_MP_RET(mp_export(pA, NULL, -1, a_size, 0, 0, &a)); + + return true; +} + +bool xbox_rsa_public(const unsigned char* in_buf, unsigned char* out_buf, RSA_PUBLIC_KEY key) +{ + rsa_key tom_key; + unsigned char in_buf_be[256] = { 0 }; + unsigned char out_buf_be[256] = { 0 }; + unsigned long out_len = 256; + unsigned char modulus_be[256] = { 0 }; + unsigned char exp_be[4] = { 0 }; + + // We must swap the data since libtom expects the data to be in big endian + mbedtls_swap_endianness (key.KeyData.Modulus, modulus_be, 256); + mbedtls_swap_endianness (key.KeyData.Exponent, exp_be, 4); + mbedtls_swap_endianness (in_buf, in_buf_be, 256); + if (rsa_set_key(modulus_be, 256, exp_be, 4, NULL, 0, &tom_key) != CRYPT_OK) { + EmuLog(LOG_LEVEL::WARNING, "Failed to load rsa key"); + return false; + } + if (rsa_exptmod(in_buf_be, 256, out_buf_be, &out_len, PK_PUBLIC, &tom_key) != CRYPT_OK) { + EmuLog(LOG_LEVEL::WARNING, "rsa_exptmod failed"); + return false; + } + mbedtls_swap_endianness(out_buf_be, out_buf, 256); + return true; +} + +bool verify_hash(const unsigned char* hash, const unsigned char* decryptBuffer, RSA_PUBLIC_KEY key) +{ unsigned char cmphash[20]; int a; int zero_position = 20; @@ -192,5 +192,5 @@ bool verify_hash(const unsigned char* hash, const unsigned char* decryptBuffer, if (decryptBuffer[i] != 0xff) return false; } - return true; -} + return true; +} diff --git a/src/common/crypto/EmuRsa.h b/src/common/crypto/EmuRsa.h index 2faef42cf..8f2f448b4 100644 --- a/src/common/crypto/EmuRsa.h +++ b/src/common/crypto/EmuRsa.h @@ -1,55 +1,55 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2018-2019 ergo720 -// * -// * All rights reserved -// * -// ****************************************************************** - -#ifndef EMURSA_H -#define EMURSA_H - -#include // For size_t - -#pragma pack(4) - -typedef union _RSA_PUBLIC_KEY -{ - unsigned char Default[284]; - struct { - char Magic[4]; // "RSA1" - unsigned int Bloblen; // 264 (Modulus + Exponent + Modulussize) - unsigned char Bitlen[4]; // 2048 - unsigned int ModulusSize; // 255 (bytes in the Modulus) - unsigned char Exponent[4]; // Public exponent - unsigned char Modulus[256]; // Bit endian style - unsigned char Unknown[8]; // ? - }KeyData; -} RSA_PUBLIC_KEY; - -#pragma pack() - -void init_tom_lib(); -bool xbox_exp_mod(unsigned char* pA, const unsigned char* pB, const unsigned char* pC, const unsigned char* pD, - size_t a_size, size_t b_size, size_t c_size, size_t d_size); -bool xbox_rsa_public(const unsigned char* in_buf, unsigned char* out_buf, RSA_PUBLIC_KEY key); -bool verify_hash(const unsigned char* hash, const unsigned char* decryptBuffer, RSA_PUBLIC_KEY key); - -#endif +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018-2019 ergo720 +// * +// * All rights reserved +// * +// ****************************************************************** + +#ifndef EMURSA_H +#define EMURSA_H + +#include // For size_t + +#pragma pack(4) + +typedef union _RSA_PUBLIC_KEY +{ + unsigned char Default[284]; + struct { + char Magic[4]; // "RSA1" + unsigned int Bloblen; // 264 (Modulus + Exponent + Modulussize) + unsigned char Bitlen[4]; // 2048 + unsigned int ModulusSize; // 255 (bytes in the Modulus) + unsigned char Exponent[4]; // Public exponent + unsigned char Modulus[256]; // Bit endian style + unsigned char Unknown[8]; // ? + }KeyData; +} RSA_PUBLIC_KEY; + +#pragma pack() + +void init_tom_lib(); +bool xbox_exp_mod(unsigned char* pA, const unsigned char* pB, const unsigned char* pC, const unsigned char* pD, + size_t a_size, size_t b_size, size_t c_size, size_t d_size); +bool xbox_rsa_public(const unsigned char* in_buf, unsigned char* out_buf, RSA_PUBLIC_KEY key); +bool verify_hash(const unsigned char* hash, const unsigned char* decryptBuffer, RSA_PUBLIC_KEY key); + +#endif diff --git a/src/common/crypto/EmuSha.cpp b/src/common/crypto/EmuSha.cpp index caef45264..5748bf63f 100644 --- a/src/common/crypto/EmuSha.cpp +++ b/src/common/crypto/EmuSha.cpp @@ -43,7 +43,7 @@ A million repetitions of "a" /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ /* #define SHA1HANDSOFF * Copies data before messing with it. */ -// Acknowledgment: Steve Reid +// Acknowledgment: Steve Reid // https://github.com/clibs/sha1 #define SHA1HANDSOFF diff --git a/src/common/input/EmuDevice.cpp b/src/common/input/EmuDevice.cpp index 8649a8ba7..58e282340 100644 --- a/src/common/input/EmuDevice.cpp +++ b/src/common/input/EmuDevice.cpp @@ -35,7 +35,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd) { switch (type) { - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): { m_hwnd = hwnd; for (size_t i = 0; i < ARRAY_SIZE(button_xbox_ctrl_id); i++) { diff --git a/src/common/util/CxbxUtil.cpp b/src/common/util/CxbxUtil.cpp index e42d0beaa..9b1282ad3 100644 --- a/src/common/util/CxbxUtil.cpp +++ b/src/common/util/CxbxUtil.cpp @@ -25,9 +25,9 @@ // * // ****************************************************************** -// Acknowledgment: Muldiv64 is from qemu-common.h which doesn't have a license header, iovector functions are from iov.c file. -// Both files were originally taken from XQEMU (GPLv2) -// https://xqemu.com/ +// Acknowledgment: Muldiv64 is from qemu-common.h which doesn't have a license header, iovector functions are from iov.c file. +// Both files were originally taken from XQEMU (GPLv2) +// https://xqemu.com/ /* * Helpers for getting linearized buffers from iov / filling buffers into iovs @@ -49,15 +49,15 @@ // The intent of this file is to add general functions which are not kernel specific (for those CxbxKrnl.h should be used instead) - + #include // For memcpy -#include "common\util\CxbxUtil.h" -#include "core\kernel\init\CxbxKrnl.h" +#include "common\util\CxbxUtil.h" +#include "core\kernel\init\CxbxKrnl.h" #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - +#endif + // Disable a compiler warning relative to uint64_t -> uint32_t conversions in Muldiv64. This function is taken from // QEMU so it should be safe regardless @@ -150,133 +150,133 @@ size_t IoVecFromBuffer(const IoVec* iov, unsigned int iov_cnt, size_t offset, vo // Read an array of DWORDs in memory bool GetDwords(xbaddr Paddr, uint32_t* Buffer, int Number) { - for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { - // dropped little -> big endian conversion from XQEMU + for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { + // dropped little -> big endian conversion from XQEMU if (Memory_R(reinterpret_cast(Paddr), Buffer, 4)) { return true; } - } + } return false; } // Write an array of DWORDs in memory bool WriteDwords(xbaddr Paddr, uint32_t* Buffer, int Number) { - for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { - // dropped big -> little endian conversion from XQEMU + for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { + // dropped big -> little endian conversion from XQEMU if (Memory_W(reinterpret_cast(Paddr), Buffer, 4)) { return true; } - } + } return false; } // Read an array of WORDs in memory bool GetWords(xbaddr Paddr, uint16_t* Buffer, int Number) { - for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { - // dropped little -> big endian conversion from XQEMU + for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { + // dropped little -> big endian conversion from XQEMU if (Memory_R(reinterpret_cast(Paddr), Buffer, 2)) { return true; } - } + } return false; } // Write an array of WORDs in memory bool WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number) { - for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { - // dropped big -> little endian conversion from XQEMU + for (int i = 0; i < Number; i++, Buffer++, Paddr += sizeof(*Buffer)) { + // dropped big -> little endian conversion from XQEMU if (Memory_W(reinterpret_cast(Paddr), Buffer, 2)) { return true; } - } + } return false; -} - -bool Memory_R(void* Addr, void* Buf, size_t Num) +} + +bool Memory_R(void* Addr, void* Buf, size_t Num) { - bool Error = false; - + bool Error = false; + if (Num != 0) { if (Addr == nullptr) { Error = true; - } + } else { std::memcpy(Buf, Addr, Num); } - } + } return Error; -} - -bool Memory_W(void* Addr, void* Buf, size_t Num) +} + +bool Memory_W(void* Addr, void* Buf, size_t Num) { - bool Error = false; - + bool Error = false; + if (Num != 0) { if (Addr == nullptr) { Error = true; - } + } else { std::memcpy(Addr, Buf, Num); } - } + } return Error; -} - -bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite) +} + +bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite) { - if (bIsWrite) { + if (bIsWrite) { return Memory_W(Addr, Buf, Num); } - else { + else { return Memory_R(Addr, Buf, Num); } } - -// Converts LF to CRLF line endings -void unix2dos(std::string& string) -{ - size_t position = 0; - while (true) { - position = string.find('\n', position); - if (position == std::string::npos) { - break; - } - if (position != 0 && string.compare(position - 1, 2U, "\r\n") == 0) { - position++; - continue; - } - string.insert(position, 1, '\r'); - position += 2; - } -} - + +// Converts LF to CRLF line endings +void unix2dos(std::string& string) +{ + size_t position = 0; + while (true) { + position = string.find('\n', position); + if (position == std::string::npos) { + break; + } + if (position != 0 && string.compare(position - 1, 2U, "\r\n") == 0) { + position++; + continue; + } + string.insert(position, 1, '\r'); + position += 2; + } +} + // Copyright 2008 Dolphin Emulator Project // Licensed under GPLv2+ -// Refer to the license.txt file of Dolphin at https://github.com/dolphin-emu/dolphin/blob/master/license.txt. - -// Source: StringUtil.cpp of Dolphin emulator -std::string StripChars(const std::string& str, const char* strip_chars) -{ - const size_t s = str.find_first_not_of(strip_chars); - - if (str.npos != s) { - return str.substr(s, str.find_last_not_of(strip_chars) - s + 1); - } - else { - return ""; - } -} - -/* Turns " hello " into "hello". Also handles tabs */ -std::string StripSpaces(const std::string& str) -{ - return StripChars(str, " \t\r\n"); -} - -std::string StripQuotes(const std::string& str) -{ - return StripChars(str, "\""); -} +// Refer to the license.txt file of Dolphin at https://github.com/dolphin-emu/dolphin/blob/master/license.txt. + +// Source: StringUtil.cpp of Dolphin emulator +std::string StripChars(const std::string& str, const char* strip_chars) +{ + const size_t s = str.find_first_not_of(strip_chars); + + if (str.npos != s) { + return str.substr(s, str.find_last_not_of(strip_chars) - s + 1); + } + else { + return ""; + } +} + +/* Turns " hello " into "hello". Also handles tabs */ +std::string StripSpaces(const std::string& str) +{ + return StripChars(str, " \t\r\n"); +} + +std::string StripQuotes(const std::string& str) +{ + return StripChars(str, "\""); +} diff --git a/src/common/util/CxbxUtil.h b/src/common/util/CxbxUtil.h index 448c23552..d2ca17e8b 100644 --- a/src/common/util/CxbxUtil.h +++ b/src/common/util/CxbxUtil.h @@ -17,7 +17,7 @@ // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * -// * (c) 2002-2003 Aaron Robinson +// * (c) 2002-2003 Aaron Robinson // * (c) 2018 ergo720 // * // * All rights reserved @@ -26,13 +26,13 @@ #ifndef CXBXUTIL_H #define CXBXUTIL_H -#include "Cxbx.h" -#include -#include -#include +#include "Cxbx.h" +#include +#include +#include #include #include "std_extend.hpp" // for ARRAY_SIZE - + /* This is a linux struct for vectored I/O. See readv() and writev() */ struct IoVec { @@ -58,24 +58,24 @@ size_t IoVecFromBuffer(const IoVec* iov, unsigned int iov_cnt, size_t offset, vo bool WriteDwords(xbaddr Paddr, uint32_t* Buffer, int Number); bool GetDwords(xbaddr Paddr, uint32_t* Buffer, int Number); bool GetWords(xbaddr Paddr, uint16_t* Buffer, int Number); -bool WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number); -bool Memory_R(void* Addr, void* Buf, size_t Num); -bool Memory_W(void* Addr, void* Buf, size_t Num); -bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite); - -void unix2dos(std::string& string); -std::string StripSpaces(const std::string& str); -std::string StripQuotes(const std::string& str); - -// Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time -template -constexpr typename std::underlying_type::type to_underlying(E e) noexcept -{ - return static_cast>(e); +bool WriteWords(xbaddr Paddr, uint16_t* Buffer, int Number); +bool Memory_R(void* Addr, void* Buf, size_t Num); +bool Memory_W(void* Addr, void* Buf, size_t Num); +bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite); + +void unix2dos(std::string& string); +std::string StripSpaces(const std::string& str); +std::string StripQuotes(const std::string& str); + +// Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time +template +constexpr typename std::underlying_type::type to_underlying(E e) noexcept +{ + return static_cast>(e); } - + #define GET_WORD_LOW(value) (uint8_t)((value) & 0xFF) -#define GET_WORD_HIGH(value) (uint8_t)(((value) >> 8) & 0xFF) +#define GET_WORD_HIGH(value) (uint8_t)(((value) >> 8) & 0xFF) /*! round dwValue to the nearest multiple of dwMult */ static uint32_t RoundUp(uint32_t dwValue, uint32_t dwMult) @@ -87,6 +87,6 @@ static uint32_t RoundUp(uint32_t dwValue, uint32_t dwMult) return dwValue; return dwValue + dwMult - remainder; -} +} #endif diff --git a/src/common/util/gloffscreen/gloffscreen_wgl.cpp b/src/common/util/gloffscreen/gloffscreen_wgl.cpp index 8c8f19b98..d1cfe3a8c 100644 --- a/src/common/util/gloffscreen/gloffscreen_wgl.cpp +++ b/src/common/util/gloffscreen/gloffscreen_wgl.cpp @@ -199,11 +199,11 @@ void glo_context_destroy(GloContext *context) free(context); } - -void glo_swap(GloContext *context) -{ - if (!context) { return; } - - SwapBuffers(context->hDC); - -} + +void glo_swap(GloContext *context) +{ + if (!context) { return; } + + SwapBuffers(context->hDC); + +} diff --git a/src/common/win32/EmuShared.h b/src/common/win32/EmuShared.h index 1758492f6..7fb732120 100644 --- a/src/common/win32/EmuShared.h +++ b/src/common/win32/EmuShared.h @@ -25,31 +25,31 @@ #ifndef EMUSHARED_H #define EMUSHARED_H -#include "Cxbx.h" -#include "common\Settings.hpp" -#include "Mutex.h" -#include "common\IPCHybrid.hpp" -#include "common\input\Button.h" +#include "Cxbx.h" +#include "common\Settings.hpp" +#include "Mutex.h" +#include "common\IPCHybrid.hpp" +#include "common\input\Button.h" #include -extern HMODULE hActiveModule; // Equals EXE Module handle in (GUI) Cxbx.exe / cxbxr.exe, equals DLL Module handle in cxbxr-emu.dll +extern HMODULE hActiveModule; // Equals EXE Module handle in (GUI) Cxbx.exe / cxbxr.exe, equals DLL Module handle in cxbxr-emu.dll typedef enum _XBOX_LED_COLOUR: unsigned char { XBOX_LED_COLOUR_OFF, XBOX_LED_COLOUR_GREEN, XBOX_LED_COLOUR_RED, XBOX_LED_COLOUR_ORANGE, -} XBOX_LED_COLOUR; - -// Kernel boot flags -enum { - BOOT_NONE = 0, - BOOT_EJECT_PENDING = 1 << 0, - BOOT_FATAL_ERROR = 1 << 1, - BOOT_SKIP_ANIMATION = 1 << 2, - BOOT_RUN_DASHBOARD = 1 << 3, - BOOT_QUICK_REBOOT = 1 << 4, +} XBOX_LED_COLOUR; + +// Kernel boot flags +enum { + BOOT_NONE = 0, + BOOT_EJECT_PENDING = 1 << 0, + BOOT_FATAL_ERROR = 1 << 1, + BOOT_SKIP_ANIMATION = 1 << 2, + BOOT_RUN_DASHBOARD = 1 << 3, + BOOT_QUICK_REBOOT = 1 << 4, }; // ****************************************************************** @@ -85,7 +85,7 @@ class EmuShared : public Mutex // * Check if parent process is emulating title // ****************************************************************** void GetIsEmulating(bool *isEmulating) { Lock(); *isEmulating = m_bEmulating_status; Unlock(); } - void SetIsEmulating(const bool isEmulating) { Lock(); m_bEmulating_status = isEmulating; Unlock(); } + void SetIsEmulating(const bool isEmulating) { Lock(); m_bEmulating_status = isEmulating; Unlock(); } // ****************************************************************** // * Each child process need to wait until parent process is ready @@ -115,38 +115,38 @@ class EmuShared : public Mutex // * Xbox Audio Accessors // ****************************************************************** void GetAudioSettings( Settings::s_audio *audio) { Lock(); *audio = m_audio; Unlock(); } - void SetAudioSettings(const Settings::s_audio *audio) { Lock(); m_audio = *audio; Unlock(); } - + void SetAudioSettings(const Settings::s_audio *audio) { Lock(); m_audio = *audio; Unlock(); } + // ****************************************************************** // * Xbox Network Accessors // ****************************************************************** void GetNetworkSettings(Settings::s_network *network) { Lock(); *network = m_network; Unlock(); } void SetNetworkSettings(const Settings::s_network *network) { Lock(); m_network = *network; Unlock(); } - // ****************************************************************** - // * Input config Accessors - // ****************************************************************** - void GetInputDevTypeSettings(int* type, int port) { Lock(); *type = m_DeviceType[port]; Unlock(); } - void SetInputDevTypeSettings(const int* type, int port) { Lock(); m_DeviceType[port] = *type; Unlock(); } - void GetInputDevNameSettings(char* name, int port) { Lock(); strncpy(name, m_DeviceName[port], 50); Unlock(); } - void SetInputDevNameSettings(const char* name, int port) { Lock(); strncpy(m_DeviceName[port], name, 50); Unlock(); } - void GetInputBindingsSettings(char button_str[][30], int max_num_buttons, int port) - { - assert(max_num_buttons <= XBOX_CTRL_NUM_BUTTONS); - Lock(); - for (int i = 0; i < max_num_buttons; i++) { - strncpy(button_str[i], m_DeviceControlNames[port][i], 30); - } - Unlock(); - } - void SetInputBindingsSettings(const char button_str[][30], int max_num_buttons, int port) - { - assert(max_num_buttons <= XBOX_CTRL_NUM_BUTTONS); - Lock(); - for (int i = 0; i < max_num_buttons; i++) { - strncpy(m_DeviceControlNames[port][i], button_str[i], 30); - } - Unlock(); + // ****************************************************************** + // * Input config Accessors + // ****************************************************************** + void GetInputDevTypeSettings(int* type, int port) { Lock(); *type = m_DeviceType[port]; Unlock(); } + void SetInputDevTypeSettings(const int* type, int port) { Lock(); m_DeviceType[port] = *type; Unlock(); } + void GetInputDevNameSettings(char* name, int port) { Lock(); strncpy(name, m_DeviceName[port], 50); Unlock(); } + void SetInputDevNameSettings(const char* name, int port) { Lock(); strncpy(m_DeviceName[port], name, 50); Unlock(); } + void GetInputBindingsSettings(char button_str[][30], int max_num_buttons, int port) + { + assert(max_num_buttons <= XBOX_CTRL_NUM_BUTTONS); + Lock(); + for (int i = 0; i < max_num_buttons; i++) { + strncpy(button_str[i], m_DeviceControlNames[port][i], 30); + } + Unlock(); + } + void SetInputBindingsSettings(const char button_str[][30], int max_num_buttons, int port) + { + assert(max_num_buttons <= XBOX_CTRL_NUM_BUTTONS); + Lock(); + for (int i = 0; i < max_num_buttons; i++) { + strncpy(m_DeviceControlNames[port][i], button_str[i], 30); + } + Unlock(); } // ****************************************************************** @@ -163,9 +163,9 @@ class EmuShared : public Mutex // ****************************************************************** // * Hack Flag Accessors - // ****************************************************************** - void GetHackSettings(Settings::s_hack *hacks) { Lock(); *hacks = m_hacks; Unlock(); } - void SetHackSettings(Settings::s_hack *hacks) { Lock(); m_hacks = *hacks; Unlock(); } + // ****************************************************************** + void GetHackSettings(Settings::s_hack *hacks) { Lock(); *hacks = m_hacks; Unlock(); } + void SetHackSettings(Settings::s_hack *hacks) { Lock(); m_hacks = *hacks; Unlock(); } void GetDisablePixelShaders(int* value) { Lock(); *value = m_hacks.DisablePixelShaders; Unlock(); } void SetDisablePixelShaders(const int* value) { Lock(); m_hacks.DisablePixelShaders = *value; Unlock(); } @@ -173,37 +173,37 @@ class EmuShared : public Mutex void SetUseAllCores(const int* value) { Lock(); m_hacks.UseAllCores = *value; Unlock(); } void GetSkipRdtscPatching(int* value) { Lock(); *value = m_hacks.SkipRdtscPatching; Unlock(); } void SetSkipRdtscPatching(const int* value) { Lock(); m_hacks.SkipRdtscPatching = *value; Unlock(); } - - // ****************************************************************** - // * FPS/Benchmark values Accessors - // ****************************************************************** - void GetCurrentFPS(float *value) { Lock(); *value = m_FPS_status; Unlock(); } - void SetCurrentFPS(const float *value) { Lock(); m_FPS_status = *value; Unlock(); } - - // ****************************************************************** - // * FPS/Benchmark values Accessors - // ****************************************************************** - void GetIsKrnlLogEnabled(bool *value) { Lock(); *value = m_Krnl_Log_enabled; Unlock(); } + + // ****************************************************************** + // * FPS/Benchmark values Accessors + // ****************************************************************** + void GetCurrentFPS(float *value) { Lock(); *value = m_FPS_status; Unlock(); } + void SetCurrentFPS(const float *value) { Lock(); m_FPS_status = *value; Unlock(); } + + // ****************************************************************** + // * FPS/Benchmark values Accessors + // ****************************************************************** + void GetIsKrnlLogEnabled(bool *value) { Lock(); *value = m_Krnl_Log_enabled; Unlock(); } void SetIsKrnlLogEnabled(const bool value) { Lock(); m_Krnl_Log_enabled = value; Unlock(); } // ****************************************************************** // * Debugging flag Accessors // ****************************************************************** void GetDebuggingFlag(bool *value) { Lock(); *value = m_bDebugging; Unlock(); } - void SetDebuggingFlag(const bool *value) { Lock(); m_bDebugging = *value; Unlock(); } -#ifndef CXBX_LOADER // Temporary usage for cxbx.exe's emu + void SetDebuggingFlag(const bool *value) { Lock(); m_bDebugging = *value; Unlock(); } +#ifndef CXBX_LOADER // Temporary usage for cxbx.exe's emu // ****************************************************************** // * Previous Memory Layout value Accessors // ****************************************************************** void GetMmLayout(unsigned int* value) { Lock(); *value = m_PreviousMmLayout; Unlock(); } - void SetMmLayout(unsigned int* value) { Lock(); m_PreviousMmLayout = *value; Unlock(); } -#endif + void SetMmLayout(unsigned int* value) { Lock(); m_PreviousMmLayout = *value; Unlock(); } +#endif // ****************************************************************** // * Log Level value Accessors // ****************************************************************** void GetLogLv(int *value) { Lock(); *value = m_core.LogLevel; Unlock(); } - void SetLogLv(int *value) { Lock(); m_core.LogLevel = *value; Unlock(); } - + void SetLogLv(int *value) { Lock(); m_core.LogLevel = *value; Unlock(); } + // ****************************************************************** // * Log modules value Accessors // ****************************************************************** @@ -222,41 +222,41 @@ class EmuShared : public Mutex m_core.LoggedModules[i] = value[i]; } Unlock(); - } - + } + // ****************************************************************** // * Log Level value Accessors // ****************************************************************** void GetLogPopupTestCase(bool *value) { Lock(); *value = m_core.bLogPopupTestCase; Unlock(); } - void SetLogPopupTestCase(const bool value) { Lock(); m_core.bLogPopupTestCase = value; Unlock(); } - + void SetLogPopupTestCase(const bool value) { Lock(); m_core.bLogPopupTestCase = value; Unlock(); } + // ****************************************************************** // * File storage location - // ****************************************************************** - void GetStorageLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, MAX_PATH); Unlock(); } + // ****************************************************************** + void GetStorageLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, MAX_PATH); Unlock(); } void SetStorageLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, MAX_PATH); Unlock(); } - + // ****************************************************************** // * Reset specific variables to default for kernel mode. - // ****************************************************************** - void ResetKrnl() - { + // ****************************************************************** + void ResetKrnl() + { Lock(); - m_BootFlags_status = 0; - m_FPS_status = 0.0f; - Unlock(); + m_BootFlags_status = 0; + m_FPS_status = 0.0f; + Unlock(); } - + // ****************************************************************** // * Reset specific variables to default for gui mode. - // ****************************************************************** - void Reset() - { + // ****************************************************************** + void Reset() + { Lock(); - ResetKrnl(); - m_bEmulating_status = 0; - m_dwKrnlProcID = 0; - Unlock(); + ResetKrnl(); + m_bEmulating_status = 0; + m_dwKrnlProcID = 0; + Unlock(); } private: @@ -274,31 +274,31 @@ class EmuShared : public Mutex float m_Reserved6; float m_FPS_status; // NOTE: If move into ipc_send_gui_update will spam GUI's message system (one message per frame) bool m_Krnl_Log_enabled; // Is require in order to preserve previous set for support multi-xbe. - bool m_bDebugging; - bool m_bReady_status; - bool m_bEmulating_status; -#ifndef CXBX_LOADER // Temporary usage for cxbx.exe's emu - unsigned int m_PreviousMmLayout; - int m_Reserved7[3]; -#else - int m_Reserved7[4]; -#endif - bool m_bFirstLaunch; - bool m_bReserved2; - bool m_bReserved3; - bool m_bReserved4; - unsigned int m_dwKrnlProcID; // Only used for kernel mode level. - int m_DeviceType[4]; - char m_DeviceControlNames[4][XBOX_CTRL_NUM_BUTTONS][30]; // macro should be num of buttons of dev with highest num buttons + bool m_bDebugging; + bool m_bReady_status; + bool m_bEmulating_status; +#ifndef CXBX_LOADER // Temporary usage for cxbx.exe's emu + unsigned int m_PreviousMmLayout; + int m_Reserved7[3]; +#else + int m_Reserved7[4]; +#endif + bool m_bFirstLaunch; + bool m_bReserved2; + bool m_bReserved3; + bool m_bReserved4; + unsigned int m_dwKrnlProcID; // Only used for kernel mode level. + int m_DeviceType[4]; + char m_DeviceControlNames[4][XBOX_CTRL_NUM_BUTTONS][30]; // macro should be num of buttons of dev with highest num buttons char m_DeviceName[4][50]; - int m_Reserved99[28]; // Reserve space - - // Settings class in memory should not be tampered by third-party. - // Third-party program should only be allow to edit settings.ini file. + int m_Reserved99[28]; // Reserve space + + // Settings class in memory should not be tampered by third-party. + // Third-party program should only be allow to edit settings.ini file. Settings::s_core m_core; - Settings::s_video m_video; - Settings::s_audio m_audio; - Settings::s_network m_network; + Settings::s_video m_video; + Settings::s_audio m_audio; + Settings::s_network m_network; Settings::s_hack m_hacks; }; diff --git a/src/common/win32/InlineFunc.cpp b/src/common/win32/InlineFunc.cpp index ff870a329..9f0f784da 100644 --- a/src/common/win32/InlineFunc.cpp +++ b/src/common/win32/InlineFunc.cpp @@ -55,7 +55,7 @@ bool CxbxExec(bool useDebugger, HANDLE* hProcess, bool requestHandleProcess) { if (!cli_config::GenCMD(szProcArgsBuffer)) { return false; } - + // TODO: Set a configuration variable for this. For now it will be within the same folder as Cxbx.exe if (useDebugger) { szProcArgsBuffer = "cxbxr-debugger.exe " + szProcArgsBuffer; diff --git a/src/common/xbe/Xbe.cpp b/src/common/xbe/Xbe.cpp index a555f5faa..79d6d0e0c 100644 --- a/src/common/xbe/Xbe.cpp +++ b/src/common/xbe/Xbe.cpp @@ -1,842 +1,842 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2017 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - - -#include -#include "common\xbe\Xbe.h" -#include "common\util\CxbxUtil.h" // For RoundUp -#include // filesystem related functions available on C++ 17 -#include // For ctime -#include -#include "devices\LED.h" // For LED::Sequence -#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlPrintUEM -#include "common\crypto\EmuSha.h" // For the SHA functions -#include "common\crypto\EmuRsa.h" // For the RSA functions -#include "core\hle\XAPI\Xapi.h" // For LDT_FROM_DASHBOARD -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow -#include "common/AddressRanges.h" -#include "common/xbox/Types.hpp" - -namespace fs = std::filesystem; - - - -// construct via Xbe file -Xbe::Xbe(const char *x_szFilename, bool bFromGUI) -{ - char szBuffer[MAX_PATH]; - - ConstructorInit(); - - printf("Xbe::Xbe: Opening Xbe file..."); - - FILE *XbeFile = fopen(x_szFilename, "rb"); - - // verify Xbe file was opened successfully - if(XbeFile == 0) - { - using namespace fs; // limit its scope inside here - - std::string XbeName = path(x_szFilename).filename().string(); // recover the xbe name - - // NOTE: the check for the existence of the child window is necessary because the user could have previously loaded the dashboard, - // removed/changed the path and attempt to load it again from the recent list, which will crash CxbxInitWindow below - // Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons - if (XbeName.compare(std::string("xboxdash.xbe")) == 0 && !bFromGUI) - { - // The dashboard could not be found on partition2. This is a fatal error on the Xbox so we display the UEM. The - // error code is different if we have a launch data page - - CxbxInitWindow(false); - - ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC; - - if (xboxkrnl::LaunchDataPage && xboxkrnl::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD) - { - xboxkrnl::PDASH_LAUNCH_DATA pLaunchDashboard = (xboxkrnl::PDASH_LAUNCH_DATA)&(xboxkrnl::LaunchDataPage->LaunchData[0]); - FatalErrorCode += pLaunchDashboard->dwReason; - } - SetLEDSequence(0xE1); // green, red, red, red - CxbxKrnlPrintUEM(FatalErrorCode); // won't return - - // TODO: FATAL_ERROR_XBE_DASH_X2_PASS (requires DVD drive authentication emulation...) - } - else - { - // Report which xbe could not be found - SetFatalError(std::string("Could not open the Xbe file ") + XbeName); - return; - } - } - - printf("OK\n"); - - // remember the Xbe path - { - printf("Xbe::Xbe: Storing Xbe Path..."); - - strcpy(m_szPath, x_szFilename); - - char * c = strrchr(m_szPath, '\\'); - if (c != nullptr) - *(++c) = '\0'; - } - - printf("OK\n"); - - // read Xbe image header - { - printf("Xbe::Xbe: Reading Image Header..."); - - if(fread(&m_Header, sizeof(m_Header), 1, XbeFile) != 1) - { - SetFatalError("Unexpected end of file while reading Xbe Image Header"); - goto cleanup; - } - - if(m_Header.dwMagic != *(uint32_t *)"XBEH") - { - SetFatalError("Invalid magic number in Xbe file"); - goto cleanup; - } - - printf("OK\n"); - } - - // read Xbe image header extra bytes - if(m_Header.dwSizeofHeaders > sizeof(m_Header)) - { - printf("Xbe::Xbe: Reading Image Header Extra Bytes..."); - - m_ExSize = RoundUp(m_Header.dwSizeofHeaders, PAGE_SIZE) - sizeof(m_Header); - - m_HeaderEx = new char[m_ExSize]; - - if(fread(m_HeaderEx, m_ExSize, 1, XbeFile) != 1) - { - SetFatalError("Unexpected end of file while reading Xbe Image Header (Ex)"); - goto cleanup; - } - - printf("OK\n"); - } - - // read Xbe certificate - { - printf("Xbe::Xbe: Reading Certificate..."); - - fseek(XbeFile, m_Header.dwCertificateAddr - m_Header.dwBaseAddr, SEEK_SET); - - if(fread(&m_Certificate, sizeof(m_Certificate), 1, XbeFile) != 1) - { - SetFatalError("Unexpected end of file while reading Xbe Certificate"); - goto cleanup; - } - - setlocale( LC_ALL, "English" ); - - wcstombs(m_szAsciiTitle, m_Certificate.wszTitleName, 40); - - printf("OK\n"); - - printf("Xbe::Xbe: Title identified as %s\n", m_szAsciiTitle); - - // Detect empty title : - int len = strlen(m_szAsciiTitle); - while (len > 0 && m_szAsciiTitle[len-1] <= ' ') - len--; - if (len <= 0) { - // Try to fix empty title; first, try the executable name: - char Dir[_MAX_DIR]; - char Filename[_MAX_FNAME]; - _splitpath(x_szFilename, nullptr, Dir, Filename, nullptr); - if (_stricmp(Filename, "default") != 0) { - strcpy(m_szAsciiTitle, Filename); - } - else { - // If executable is named "default.xbe", try the parent folder name: - len = strlen(Dir); - if (len > 0) { - Dir[len - 1] = '\0'; - _splitpath(Dir, nullptr, nullptr, m_szAsciiTitle, nullptr); - } - } - - printf("Xbe::Xbe: Replaced empty title with fallback : %s\n", m_szAsciiTitle); - } - } - - // read Xbe section headers - { - printf("Xbe::Xbe: Reading Section Headers...\n"); - - fseek(XbeFile, m_Header.dwSectionHeadersAddr - m_Header.dwBaseAddr, SEEK_SET); - - m_SectionHeader = new SectionHeader[m_Header.dwSections]; - - for(uint32_t v=0;v %s\n", GetError().c_str()); - } - - fclose(XbeFile); - - return; -} - -// deconstructor -Xbe::~Xbe() -{ - if(m_bzSection != 0) - { - for(uint32_t v=0;v %s\n", GetError().c_str()); - } - - fclose(XbeFile); -} - -// constructor initialization -void Xbe::ConstructorInit() -{ - m_HeaderEx = 0; - m_SectionHeader = 0; - m_szSectionName = 0; - m_LibraryVersion = 0; - m_TLS = 0; - m_bzSection = 0; - m_SignatureHeader = 0; -} - -// better time -static char *BetterTime(uint32_t x_timeDate) -{ - time_t x_time = x_timeDate; - char *x_ctime = ctime(&x_time); - - int v=0; - - for(v=0;x_ctime[v] != '\n';v++); - - x_ctime[v] = '\0'; - - return x_ctime; -} - -// import logo bitmap from raw monochrome data -void Xbe::ImportLogoBitmap(const uint8_t x_Gray[100*17]) -{ - char *LogoBuffer = new char[4*1024]; - uint32_t LogoSize = 0; - - // encode logo bitmap - { - for(uint32_t v=1;v<100*17;LogoSize++) - { - char color = x_Gray[v] >> 4; - - uint32_t len = 1; - - while(++v<100*17-1 && len < 1024 && color == x_Gray[v] >> 4) - len++; - - LogoRLE *cur = (LogoRLE *)&LogoBuffer[LogoSize]; - - if(len <= 7) - { - cur->m_Eight.bType1 = 1; - cur->m_Eight.Len = len; - cur->m_Eight.Data = color; - } - else - { - cur->m_Sixteen.bType1 = 0; - cur->m_Sixteen.bType2 = 1; - cur->m_Sixteen.Len = len; - cur->m_Sixteen.Data = color; - LogoSize++; - } - } - } - - // check if there is room to save this, if not then throw an error - { - uint8_t *RLE = GetLogoBitmap(LogoSize); - - if(RLE == 0) - { - if (false == HasError()) - SetError("Logo bitmap could not be imported (not enough space in file?)"); - - return; - } - - memcpy(RLE, LogoBuffer, LogoSize); - } - - return; -} - -// ****************************************************************** -// * ExportLogoBitmap -// ****************************************************************** -// * -// * This algorithm was originally discovered by superfro. I couldnt -// * figure out what the hell the encoding format was before he gave -// * me the information: -// * -// * basically what is going on here is a single pass through the -// * bitmap data, with 2 possible encodings per rle chunk. data is -// * stored as 4 bit grayscale, so the logical approach is to expand -// * this to 8 bit using a simple 4 bit left shift (*16). However, it -// * has been suggested to me by superfro that you might calculate a -// * slightly darker image by multiplying by 15 and adding .5. It's -// * a toss up, but i've choosen a simple bit shift left. -// * -// ****************************************************************** -void Xbe::ExportLogoBitmap(uint8_t x_Gray[100*17]) -{ - memset(x_Gray, 0, 100*17); - - uint32_t dwLength = m_Header.dwSizeofLogoBitmap; - - uint8_t *RLE = GetAddr(m_Header.dwLogoBitmapAddr); - - if(RLE == nullptr || HasError()) - return; - - uint32_t o = 0; - - for(uint32_t v=0;vm_Eight.bType1) - { - len = cur->m_Eight.Len; - data = cur->m_Eight.Data; - } - else - { - if(cur->m_Sixteen.bType2) - { - len = cur->m_Sixteen.Len; - data = cur->m_Sixteen.Data; - v += 1; - } - } - - for(uint32_t j=0;j= VirtAddr) && (x_dwVirtualAddress < (VirtAddr + VirtSize)) ) - return &m_bzSection[v][x_dwVirtualAddress - VirtAddr]; - } - } - - return 0; -} - -// return a modifiable pointer to logo bitmap data -uint8_t *Xbe::GetLogoBitmap(uint32_t x_dwSize) -{ - uint32_t dwOffs = m_Header.dwLogoBitmapAddr - m_Header.dwBaseAddr; - uint32_t dwLength = m_Header.dwSizeofLogoBitmap; - - if(dwOffs == 0 || dwLength == 0) - return 0; - - // if this bitmap will fit inside the already existing one, we don't need to resize, just return pointer - if(dwLength >= x_dwSize) - { - // update size of headers, if necessary - if(dwOffs < m_Header.dwSizeofHeaders) - { - m_Header.dwSizeofHeaders -= dwLength; - m_Header.dwSizeofHeaders += x_dwSize; - m_Header.dwSizeofLogoBitmap = x_dwSize; - - return GetAddr(m_Header.dwLogoBitmapAddr); - } - } - - return 0; -} - -void *Xbe::FindSection(char *zsSectionName) -{ - for (uint32_t v = 0; v < m_Header.dwSections; v++) { - if (strcmp(m_szSectionName[v], zsSectionName) == 0) { - if (m_SectionHeader[v].dwVirtualAddr > 0 && m_SectionHeader[v].dwVirtualSize > 0) { - return m_bzSection[v]; - } - } - } - - return nullptr; -} - -void* Xbe::FindSection(xboxkrnl::PXBEIMAGE_SECTION section) -{ - for (uint32_t v = 0; v < m_Header.dwSections; v++) { - if (m_SectionHeader[v].dwVirtualAddr > 0 && m_SectionHeader[v].dwVirtualSize > 0) { - if (m_SectionHeader[v].dwRawAddr == section->FileAddress && m_SectionHeader[v].dwVirtualAddr == (uint32_t)(section->VirtualAddress) - && m_SectionHeader[v].dwVirtualSize == section->VirtualSize) { - return m_bzSection[v]; - } - } - - } - - return nullptr; -} - -void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars) -{ - for (auto it = s.begin(); it < s.end(); ++it) - { - bool found = illegalChars.find(*it) != std::string::npos; - if (found) { *it = '_'; } - } -} - -const char *Xbe::GameRegionToString() -{ - const char *Region_text[] = { - "Unknown", "NTSC", "JAP", "NTSC+JAP", - "PAL", "PAL+NTSC", "PAL+JAP", "Region Free", - "DEBUG", "NTSC (DEBUG)", "JAP (DEBUG)", "NTSC+JAP (DEBUG)", - "PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAP (DEBUG)", "Region Free (DEBUG)" - }; - const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA | - XBEIMAGE_GAME_REGION_JAPAN | - XBEIMAGE_GAME_REGION_RESTOFWORLD | - XBEIMAGE_GAME_REGION_MANUFACTURING; - - if(m_Certificate.dwGameRegion & ~all_regions) { - return "REGION ERROR"; - } - - uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0; - index |= (m_Certificate.dwGameRegion & 0x7); - return Region_text[index]; -} - -const wchar_t *Xbe::GetUnicodeFilenameAddr() -{ - return (const wchar_t *)GetAddr(m_Header.dwDebugUnicodeFilenameAddr); -} - -bool Xbe::CheckSignature() -{ - init_tom_lib(); - - DWORD HeaderDigestSize = m_Header.dwSizeofHeaders - (sizeof(m_Header.dwMagic) + sizeof(m_Header.pbDigitalSignature)); - UCHAR SHADigest[A_SHA_DIGEST_LEN]; - unsigned char crypt_buffer[256]; - CalcSHA1Hash(SHADigest, m_SignatureHeader, HeaderDigestSize); - - // Hash against all currently known public keys, if these pass, we can guarantee the Xbe is unmodified - std::array keys = { 0 }; - memcpy(keys[0].Default, (void*)xboxkrnl::XePublicKeyDataRetail, 284); - memcpy(keys[1].Default, (void*)xboxkrnl::XePublicKeyDataChihiroGame, 284); - memcpy(keys[2].Default, (void*)xboxkrnl::XePublicKeyDataChihiroBoot, 284); - // TODO: memcpy(keys[3].Default, (void*)xboxkrnl::XePublicKeyDataDebug, 284); - - for (unsigned int i = 0; i < keys.size(); i++) { - if (xbox_rsa_public(m_Header.pbDigitalSignature, crypt_buffer, keys[i])) { - if (verify_hash(SHADigest, crypt_buffer, keys[i])) { - // Load the successful key into XboxKrnl::XePublicKeyData for application use - memcpy(xboxkrnl::XePublicKeyData, keys[i].Default, 284); - return true; // success - } - } - } - - // Default to the Retail key if no key matched, just to make sure we don't init in an invalid state - memcpy(xboxkrnl::XePublicKeyData, xboxkrnl::XePublicKeyDataRetail, 284); - return false; // signature check failed -} - -bool Xbe::CheckSectionIntegrity(uint32_t sectionIndex) -{ - uint32_t RawSize = m_SectionHeader[sectionIndex].dwSizeofRaw; - if (RawSize == 0) { - return true; - } - - unsigned char SHADigest[A_SHA_DIGEST_LEN]; - CalcSHA1Hash(SHADigest, m_bzSection[sectionIndex], RawSize); - - if (std::memcmp(SHADigest, m_SectionHeader[sectionIndex].bzSectionDigest, A_SHA_DIGEST_LEN) != 0) { - return false; - } - else { - return true; - } -} - -// ported from Dxbx's XbeExplorer -XbeType Xbe::GetXbeType() -{ - // Detect if the XBE is for Chihiro (Untested!) : - // This is based on https://github.com/radare/radare2/blob/master/libr/bin/p/bin_xbe.c#L45 - if ((m_Header.dwEntryAddr & XBOX_WRITE_COMBINED_BASE) == SEGABOOT_EP_XOR) - return XbeType::xtChihiro; - - // Check for Debug XBE, using high bit of the kernel thunk address : - // (DO NOT test like https://github.com/radare/radare2/blob/master/libr/bin/p/bin_xbe.c#L49 !) - if ((m_Header.dwKernelImageThunkAddr & KSEG0_BASE) > 0) - return XbeType::xtDebug; - - // Otherwise, the XBE is a Retail build : - return XbeType::xtRetail; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2017 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + + +#include +#include "common\xbe\Xbe.h" +#include "common\util\CxbxUtil.h" // For RoundUp +#include // filesystem related functions available on C++ 17 +#include // For ctime +#include +#include "devices\LED.h" // For LED::Sequence +#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlPrintUEM +#include "common\crypto\EmuSha.h" // For the SHA functions +#include "common\crypto\EmuRsa.h" // For the RSA functions +#include "core\hle\XAPI\Xapi.h" // For LDT_FROM_DASHBOARD +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow +#include "common/AddressRanges.h" +#include "common/xbox/Types.hpp" + +namespace fs = std::filesystem; + + + +// construct via Xbe file +Xbe::Xbe(const char *x_szFilename, bool bFromGUI) +{ + char szBuffer[MAX_PATH]; + + ConstructorInit(); + + printf("Xbe::Xbe: Opening Xbe file..."); + + FILE *XbeFile = fopen(x_szFilename, "rb"); + + // verify Xbe file was opened successfully + if(XbeFile == 0) + { + using namespace fs; // limit its scope inside here + + std::string XbeName = path(x_szFilename).filename().string(); // recover the xbe name + + // NOTE: the check for the existence of the child window is necessary because the user could have previously loaded the dashboard, + // removed/changed the path and attempt to load it again from the recent list, which will crash CxbxInitWindow below + // Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons + if (XbeName.compare(std::string("xboxdash.xbe")) == 0 && !bFromGUI) + { + // The dashboard could not be found on partition2. This is a fatal error on the Xbox so we display the UEM. The + // error code is different if we have a launch data page + + CxbxInitWindow(false); + + ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC; + + if (xboxkrnl::LaunchDataPage && xboxkrnl::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD) + { + xboxkrnl::PDASH_LAUNCH_DATA pLaunchDashboard = (xboxkrnl::PDASH_LAUNCH_DATA)&(xboxkrnl::LaunchDataPage->LaunchData[0]); + FatalErrorCode += pLaunchDashboard->dwReason; + } + SetLEDSequence(0xE1); // green, red, red, red + CxbxKrnlPrintUEM(FatalErrorCode); // won't return + + // TODO: FATAL_ERROR_XBE_DASH_X2_PASS (requires DVD drive authentication emulation...) + } + else + { + // Report which xbe could not be found + SetFatalError(std::string("Could not open the Xbe file ") + XbeName); + return; + } + } + + printf("OK\n"); + + // remember the Xbe path + { + printf("Xbe::Xbe: Storing Xbe Path..."); + + strcpy(m_szPath, x_szFilename); + + char * c = strrchr(m_szPath, '\\'); + if (c != nullptr) + *(++c) = '\0'; + } + + printf("OK\n"); + + // read Xbe image header + { + printf("Xbe::Xbe: Reading Image Header..."); + + if(fread(&m_Header, sizeof(m_Header), 1, XbeFile) != 1) + { + SetFatalError("Unexpected end of file while reading Xbe Image Header"); + goto cleanup; + } + + if(m_Header.dwMagic != *(uint32_t *)"XBEH") + { + SetFatalError("Invalid magic number in Xbe file"); + goto cleanup; + } + + printf("OK\n"); + } + + // read Xbe image header extra bytes + if(m_Header.dwSizeofHeaders > sizeof(m_Header)) + { + printf("Xbe::Xbe: Reading Image Header Extra Bytes..."); + + m_ExSize = RoundUp(m_Header.dwSizeofHeaders, PAGE_SIZE) - sizeof(m_Header); + + m_HeaderEx = new char[m_ExSize]; + + if(fread(m_HeaderEx, m_ExSize, 1, XbeFile) != 1) + { + SetFatalError("Unexpected end of file while reading Xbe Image Header (Ex)"); + goto cleanup; + } + + printf("OK\n"); + } + + // read Xbe certificate + { + printf("Xbe::Xbe: Reading Certificate..."); + + fseek(XbeFile, m_Header.dwCertificateAddr - m_Header.dwBaseAddr, SEEK_SET); + + if(fread(&m_Certificate, sizeof(m_Certificate), 1, XbeFile) != 1) + { + SetFatalError("Unexpected end of file while reading Xbe Certificate"); + goto cleanup; + } + + setlocale( LC_ALL, "English" ); + + wcstombs(m_szAsciiTitle, m_Certificate.wszTitleName, 40); + + printf("OK\n"); + + printf("Xbe::Xbe: Title identified as %s\n", m_szAsciiTitle); + + // Detect empty title : + int len = strlen(m_szAsciiTitle); + while (len > 0 && m_szAsciiTitle[len-1] <= ' ') + len--; + if (len <= 0) { + // Try to fix empty title; first, try the executable name: + char Dir[_MAX_DIR]; + char Filename[_MAX_FNAME]; + _splitpath(x_szFilename, nullptr, Dir, Filename, nullptr); + if (_stricmp(Filename, "default") != 0) { + strcpy(m_szAsciiTitle, Filename); + } + else { + // If executable is named "default.xbe", try the parent folder name: + len = strlen(Dir); + if (len > 0) { + Dir[len - 1] = '\0'; + _splitpath(Dir, nullptr, nullptr, m_szAsciiTitle, nullptr); + } + } + + printf("Xbe::Xbe: Replaced empty title with fallback : %s\n", m_szAsciiTitle); + } + } + + // read Xbe section headers + { + printf("Xbe::Xbe: Reading Section Headers...\n"); + + fseek(XbeFile, m_Header.dwSectionHeadersAddr - m_Header.dwBaseAddr, SEEK_SET); + + m_SectionHeader = new SectionHeader[m_Header.dwSections]; + + for(uint32_t v=0;v %s\n", GetError().c_str()); + } + + fclose(XbeFile); + + return; +} + +// deconstructor +Xbe::~Xbe() +{ + if(m_bzSection != 0) + { + for(uint32_t v=0;v %s\n", GetError().c_str()); + } + + fclose(XbeFile); +} + +// constructor initialization +void Xbe::ConstructorInit() +{ + m_HeaderEx = 0; + m_SectionHeader = 0; + m_szSectionName = 0; + m_LibraryVersion = 0; + m_TLS = 0; + m_bzSection = 0; + m_SignatureHeader = 0; +} + +// better time +static char *BetterTime(uint32_t x_timeDate) +{ + time_t x_time = x_timeDate; + char *x_ctime = ctime(&x_time); + + int v=0; + + for(v=0;x_ctime[v] != '\n';v++); + + x_ctime[v] = '\0'; + + return x_ctime; +} + +// import logo bitmap from raw monochrome data +void Xbe::ImportLogoBitmap(const uint8_t x_Gray[100*17]) +{ + char *LogoBuffer = new char[4*1024]; + uint32_t LogoSize = 0; + + // encode logo bitmap + { + for(uint32_t v=1;v<100*17;LogoSize++) + { + char color = x_Gray[v] >> 4; + + uint32_t len = 1; + + while(++v<100*17-1 && len < 1024 && color == x_Gray[v] >> 4) + len++; + + LogoRLE *cur = (LogoRLE *)&LogoBuffer[LogoSize]; + + if(len <= 7) + { + cur->m_Eight.bType1 = 1; + cur->m_Eight.Len = len; + cur->m_Eight.Data = color; + } + else + { + cur->m_Sixteen.bType1 = 0; + cur->m_Sixteen.bType2 = 1; + cur->m_Sixteen.Len = len; + cur->m_Sixteen.Data = color; + LogoSize++; + } + } + } + + // check if there is room to save this, if not then throw an error + { + uint8_t *RLE = GetLogoBitmap(LogoSize); + + if(RLE == 0) + { + if (false == HasError()) + SetError("Logo bitmap could not be imported (not enough space in file?)"); + + return; + } + + memcpy(RLE, LogoBuffer, LogoSize); + } + + return; +} + +// ****************************************************************** +// * ExportLogoBitmap +// ****************************************************************** +// * +// * This algorithm was originally discovered by superfro. I couldnt +// * figure out what the hell the encoding format was before he gave +// * me the information: +// * +// * basically what is going on here is a single pass through the +// * bitmap data, with 2 possible encodings per rle chunk. data is +// * stored as 4 bit grayscale, so the logical approach is to expand +// * this to 8 bit using a simple 4 bit left shift (*16). However, it +// * has been suggested to me by superfro that you might calculate a +// * slightly darker image by multiplying by 15 and adding .5. It's +// * a toss up, but i've choosen a simple bit shift left. +// * +// ****************************************************************** +void Xbe::ExportLogoBitmap(uint8_t x_Gray[100*17]) +{ + memset(x_Gray, 0, 100*17); + + uint32_t dwLength = m_Header.dwSizeofLogoBitmap; + + uint8_t *RLE = GetAddr(m_Header.dwLogoBitmapAddr); + + if(RLE == nullptr || HasError()) + return; + + uint32_t o = 0; + + for(uint32_t v=0;vm_Eight.bType1) + { + len = cur->m_Eight.Len; + data = cur->m_Eight.Data; + } + else + { + if(cur->m_Sixteen.bType2) + { + len = cur->m_Sixteen.Len; + data = cur->m_Sixteen.Data; + v += 1; + } + } + + for(uint32_t j=0;j= VirtAddr) && (x_dwVirtualAddress < (VirtAddr + VirtSize)) ) + return &m_bzSection[v][x_dwVirtualAddress - VirtAddr]; + } + } + + return 0; +} + +// return a modifiable pointer to logo bitmap data +uint8_t *Xbe::GetLogoBitmap(uint32_t x_dwSize) +{ + uint32_t dwOffs = m_Header.dwLogoBitmapAddr - m_Header.dwBaseAddr; + uint32_t dwLength = m_Header.dwSizeofLogoBitmap; + + if(dwOffs == 0 || dwLength == 0) + return 0; + + // if this bitmap will fit inside the already existing one, we don't need to resize, just return pointer + if(dwLength >= x_dwSize) + { + // update size of headers, if necessary + if(dwOffs < m_Header.dwSizeofHeaders) + { + m_Header.dwSizeofHeaders -= dwLength; + m_Header.dwSizeofHeaders += x_dwSize; + m_Header.dwSizeofLogoBitmap = x_dwSize; + + return GetAddr(m_Header.dwLogoBitmapAddr); + } + } + + return 0; +} + +void *Xbe::FindSection(char *zsSectionName) +{ + for (uint32_t v = 0; v < m_Header.dwSections; v++) { + if (strcmp(m_szSectionName[v], zsSectionName) == 0) { + if (m_SectionHeader[v].dwVirtualAddr > 0 && m_SectionHeader[v].dwVirtualSize > 0) { + return m_bzSection[v]; + } + } + } + + return nullptr; +} + +void* Xbe::FindSection(xboxkrnl::PXBEIMAGE_SECTION section) +{ + for (uint32_t v = 0; v < m_Header.dwSections; v++) { + if (m_SectionHeader[v].dwVirtualAddr > 0 && m_SectionHeader[v].dwVirtualSize > 0) { + if (m_SectionHeader[v].dwRawAddr == section->FileAddress && m_SectionHeader[v].dwVirtualAddr == (uint32_t)(section->VirtualAddress) + && m_SectionHeader[v].dwVirtualSize == section->VirtualSize) { + return m_bzSection[v]; + } + } + + } + + return nullptr; +} + +void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars) +{ + for (auto it = s.begin(); it < s.end(); ++it) + { + bool found = illegalChars.find(*it) != std::string::npos; + if (found) { *it = '_'; } + } +} + +const char *Xbe::GameRegionToString() +{ + const char *Region_text[] = { + "Unknown", "NTSC", "JAP", "NTSC+JAP", + "PAL", "PAL+NTSC", "PAL+JAP", "Region Free", + "DEBUG", "NTSC (DEBUG)", "JAP (DEBUG)", "NTSC+JAP (DEBUG)", + "PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAP (DEBUG)", "Region Free (DEBUG)" + }; + const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA | + XBEIMAGE_GAME_REGION_JAPAN | + XBEIMAGE_GAME_REGION_RESTOFWORLD | + XBEIMAGE_GAME_REGION_MANUFACTURING; + + if(m_Certificate.dwGameRegion & ~all_regions) { + return "REGION ERROR"; + } + + uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0; + index |= (m_Certificate.dwGameRegion & 0x7); + return Region_text[index]; +} + +const wchar_t *Xbe::GetUnicodeFilenameAddr() +{ + return (const wchar_t *)GetAddr(m_Header.dwDebugUnicodeFilenameAddr); +} + +bool Xbe::CheckSignature() +{ + init_tom_lib(); + + DWORD HeaderDigestSize = m_Header.dwSizeofHeaders - (sizeof(m_Header.dwMagic) + sizeof(m_Header.pbDigitalSignature)); + UCHAR SHADigest[A_SHA_DIGEST_LEN]; + unsigned char crypt_buffer[256]; + CalcSHA1Hash(SHADigest, m_SignatureHeader, HeaderDigestSize); + + // Hash against all currently known public keys, if these pass, we can guarantee the Xbe is unmodified + std::array keys = { 0 }; + memcpy(keys[0].Default, (void*)xboxkrnl::XePublicKeyDataRetail, 284); + memcpy(keys[1].Default, (void*)xboxkrnl::XePublicKeyDataChihiroGame, 284); + memcpy(keys[2].Default, (void*)xboxkrnl::XePublicKeyDataChihiroBoot, 284); + // TODO: memcpy(keys[3].Default, (void*)xboxkrnl::XePublicKeyDataDebug, 284); + + for (unsigned int i = 0; i < keys.size(); i++) { + if (xbox_rsa_public(m_Header.pbDigitalSignature, crypt_buffer, keys[i])) { + if (verify_hash(SHADigest, crypt_buffer, keys[i])) { + // Load the successful key into XboxKrnl::XePublicKeyData for application use + memcpy(xboxkrnl::XePublicKeyData, keys[i].Default, 284); + return true; // success + } + } + } + + // Default to the Retail key if no key matched, just to make sure we don't init in an invalid state + memcpy(xboxkrnl::XePublicKeyData, xboxkrnl::XePublicKeyDataRetail, 284); + return false; // signature check failed +} + +bool Xbe::CheckSectionIntegrity(uint32_t sectionIndex) +{ + uint32_t RawSize = m_SectionHeader[sectionIndex].dwSizeofRaw; + if (RawSize == 0) { + return true; + } + + unsigned char SHADigest[A_SHA_DIGEST_LEN]; + CalcSHA1Hash(SHADigest, m_bzSection[sectionIndex], RawSize); + + if (std::memcmp(SHADigest, m_SectionHeader[sectionIndex].bzSectionDigest, A_SHA_DIGEST_LEN) != 0) { + return false; + } + else { + return true; + } +} + +// ported from Dxbx's XbeExplorer +XbeType Xbe::GetXbeType() +{ + // Detect if the XBE is for Chihiro (Untested!) : + // This is based on https://github.com/radare/radare2/blob/master/libr/bin/p/bin_xbe.c#L45 + if ((m_Header.dwEntryAddr & XBOX_WRITE_COMBINED_BASE) == SEGABOOT_EP_XOR) + return XbeType::xtChihiro; + + // Check for Debug XBE, using high bit of the kernel thunk address : + // (DO NOT test like https://github.com/radare/radare2/blob/master/libr/bin/p/bin_xbe.c#L49 !) + if ((m_Header.dwKernelImageThunkAddr & KSEG0_BASE) > 0) + return XbeType::xtDebug; + + // Otherwise, the XBE is a Retail build : + return XbeType::xtRetail; +} diff --git a/src/common/xbe/Xbe.h b/src/common/xbe/Xbe.h index 41d2adac9..3619e437d 100644 --- a/src/common/xbe/Xbe.h +++ b/src/common/xbe/Xbe.h @@ -1,397 +1,397 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2017 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef XBE_H -#define XBE_H - -#include "common\Error.h" -#include "common/xbox/Types.hpp" - -#include - - -//#include // For MAX_PATH -// The above leads to 55 compile errors, so until we've sorted out why that happens, declare MAX_PATH ourselves for now : -#define MAX_PATH 260 -#define XPR_IMAGE_WH 128 -#define XPR_IMAGE_DATA_SIZE (XPR_IMAGE_WH * XPR_IMAGE_WH) / 2 -#define XPR_IMAGE_HDR_SIZE 2048 - -namespace xboxkrnl -{ - typedef struct _XBE_SECTION XBEIMAGE_SECTION, *PXBEIMAGE_SECTION; -} - -// Xbe (Xbox Executable) file object -class Xbe : public Error -{ - public: - // construct via Xbe file - Xbe(const char *x_szFilename, bool bFromGUI); - - // deconstructor - ~Xbe(); - - // find an section by name - void *FindSection(char *zsSectionName); - - // Find a section by its definition - void* FindSection(xboxkrnl::PXBEIMAGE_SECTION section); - - // export to Xbe file - void Export(const char *x_szXbeFilename); - - // verify the integrity of the xbe header - bool CheckSignature(); - - // verify the integrity of an xbe section - bool CheckSectionIntegrity(uint32_t sectionIndex); - - // import logo bitmap from raw monochrome data - void ImportLogoBitmap(const uint8_t x_Gray[100*17]); - - // export logo bitmap to raw monochrome data - void ExportLogoBitmap(uint8_t x_Gray[100*17]); - - // purge illegal characters in Windows filenames or other OS's - void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|"); - - // Convert game region field to string - const char *GameRegionToString(); - - XbeType GetXbeType(); - - // Xbe header - #include "AlignPrefix1.h" - struct Header - { - uint32_t dwMagic; // 0x0000 - magic number [should be "XBEH"] - uint8_t pbDigitalSignature[256]; // 0x0004 - digital signature - uint32_t dwBaseAddr; // 0x0104 - base address - uint32_t dwSizeofHeaders; // 0x0108 - size of headers - uint32_t dwSizeofImage; // 0x010C - size of image - uint32_t dwSizeofImageHeader; // 0x0110 - size of image header - uint32_t dwTimeDate; // 0x0114 - timedate stamp - uint32_t dwCertificateAddr; // 0x0118 - certificate address - uint32_t dwSections; // 0x011C - number of sections - uint32_t dwSectionHeadersAddr; // 0x0120 - section headers address - - typedef struct - { - uint32_t bMountUtilityDrive : 1; // mount utility drive flag - uint32_t bFormatUtilityDrive : 1; // format utility drive flag - uint32_t bLimit64MB : 1; // limit development kit run time memory to 64mb flag - uint32_t bDontSetupHarddisk : 1; // don't setup hard disk flag - uint32_t Unused : 4; // unused (or unknown) - uint32_t Unused_b1 : 8; // unused (or unknown) - uint32_t Unused_b2 : 8; // unused (or unknown) - uint32_t Unused_b3 : 8; // unused (or unknown) - } InitFlags; - - union { // 0x0124 - initialization flags - InitFlags dwInitFlags; - uint32_t dwInitFlags_value; - }; - - uint32_t dwEntryAddr; // 0x0128 - entry point address - uint32_t dwTLSAddr; // 0x012C - thread local storage directory address - uint32_t dwPeStackCommit; // 0x0130 - size of stack commit - uint32_t dwPeHeapReserve; // 0x0134 - size of heap reserve - uint32_t dwPeHeapCommit; // 0x0138 - size of heap commit - uint32_t dwPeBaseAddr; // 0x013C - original base address - uint32_t dwPeSizeofImage; // 0x0140 - size of original image - uint32_t dwPeChecksum; // 0x0144 - original checksum - uint32_t dwPeTimeDate; // 0x0148 - original timedate stamp - uint32_t dwDebugPathnameAddr; // 0x014C - debug pathname address - uint32_t dwDebugFilenameAddr; // 0x0150 - debug filename address - uint32_t dwDebugUnicodeFilenameAddr; // 0x0154 - debug unicode filename address - uint32_t dwKernelImageThunkAddr; // 0x0158 - kernel image thunk address - uint32_t dwNonKernelImportDirAddr; // 0x015C - non kernel import directory address - uint32_t dwLibraryVersions; // 0x0160 - number of library versions - uint32_t dwLibraryVersionsAddr; // 0x0164 - library versions address - uint32_t dwKernelLibraryVersionAddr; // 0x0168 - kernel library version address - uint32_t dwXAPILibraryVersionAddr; // 0x016C - xapi library version address - uint32_t dwLogoBitmapAddr; // 0x0170 - logo bitmap address - uint32_t dwSizeofLogoBitmap; // 0x0174 - logo bitmap size - } - #include "AlignPosfix1.h" - m_Header; - - // Xbe header extra byte (used to preserve unknown data) - char *m_HeaderEx; - uint32_t m_ExSize; - - // Xbe certificate - #include "AlignPrefix1.h" - struct Certificate - { - uint32_t dwSize; // 0x0000 - size of certificate - uint32_t dwTimeDate; // 0x0004 - timedate stamp - uint32_t dwTitleId; // 0x0008 - title id - wchar_t wszTitleName[40]; // 0x000C - title name (unicode) - uint32_t dwAlternateTitleId[0x10]; // 0x005C - alternate title ids - uint32_t dwAllowedMedia; // 0x009C - allowed media types - uint32_t dwGameRegion; // 0x00A0 - game region - uint32_t dwGameRatings; // 0x00A4 - game ratings - uint32_t dwDiskNumber; // 0x00A8 - disk number - uint32_t dwVersion; // 0x00AC - version - uint8_t bzLanKey[16]; // 0x00B0 - lan key - uint8_t bzSignatureKey[16]; // 0x00C0 - signature key - // NOT ALL XBEs have these fields! - uint8_t bzTitleAlternateSignatureKey[16][16]; // 0x00D0 - alternate signature keys - uint32_t dwOriginalCertificateSize; // 0x01D0 - Original Certificate Size? - uint32_t dwOnlineService; // 0x01D4 - Online Service ID - uint32_t dwSecurityFlags; // 0x01D8 - Extra Security Flags - uint8_t bzCodeEncKey[16]; // 0x01DC - Code Encryption Key? - } - #include "AlignPosfix1.h" - m_Certificate; - - // Xbe section header - #include "AlignPrefix1.h" - struct SectionHeader - { - typedef struct - { - uint32_t bWritable : 1; // writable flag - uint32_t bPreload : 1; // preload flag - uint32_t bExecutable : 1; // executable flag - uint32_t bInsertedFile : 1; // inserted file flag - uint32_t bHeadPageRO : 1; // head page read only flag - uint32_t bTailPageRO : 1; // tail page read only flag - uint32_t Unused_a1 : 1; // unused (or unknown) - uint32_t Unused_a2 : 1; // unused (or unknown) - uint32_t Unused_b1 : 8; // unused (or unknown) - uint32_t Unused_b2 : 8; // unused (or unknown) - uint32_t Unused_b3 : 8; // unused (or unknown) - } _Flags; - - union { - _Flags dwFlags; - uint32_t dwFlags_value; - }; - - uint32_t dwVirtualAddr; // virtual address - uint32_t dwVirtualSize; // virtual size - uint32_t dwRawAddr; // file offset to raw data - uint32_t dwSizeofRaw; // size of raw data - uint32_t dwSectionNameAddr; // section name addr - uint32_t dwSectionRefCount; // section reference count - uint32_t dwHeadSharedRefCountAddr; // head shared page reference count address - uint32_t dwTailSharedRefCountAddr; // tail shared page reference count address - uint8_t bzSectionDigest[20]; // section digest - } - #include "AlignPosfix1.h" - *m_SectionHeader; - - // Xbe library versions - #include "AlignPrefix1.h" - struct LibraryVersion - { - char szName[8]; // library name - uint16_t wMajorVersion; // major version - uint16_t wMinorVersion; // minor version - uint16_t wBuildVersion; // build version - - typedef struct - { - uint16_t QFEVersion : 13; // QFE Version - uint16_t Approved : 2; // Approved? (0:no, 1:possibly, 2:yes) - uint16_t bDebugBuild : 1; // Is this a debug build? - } Flags; - - union { - Flags wFlags; - uint16_t wFlags_value; - }; - } - #include "AlignPosfix1.h" - *m_LibraryVersion; - - // Xbe thread local storage - #include "AlignPrefix1.h" - struct TLS - { - uint32_t dwDataStartAddr; // raw start address - uint32_t dwDataEndAddr; // raw end address - uint32_t dwTLSIndexAddr; // tls index address - uint32_t dwTLSCallbackAddr; // tls callback address - uint32_t dwSizeofZeroFill; // size of zero fill - uint32_t dwCharacteristics; // characteristics - } - #include "AlignPosfix1.h" - *m_TLS; - - // Xbe signature header - uint8_t* m_SignatureHeader; - - // Xbe section names, stored null terminated - char (*m_szSectionName)[10]; - - // Xbe sections - uint8_t **m_bzSection; - - // Xbe original path - char m_szPath[MAX_PATH]; - - // Xbe ascii title, translated from certificate title - char m_szAsciiTitle[40]; - - // retrieve thread local storage data address - uint8_t *GetTLSData() { if(m_TLS == 0) return 0; else return GetAddr(m_TLS->dwDataStartAddr); } - - // retrieve thread local storage index address - uint32_t *GetTLSIndex() { if(m_TLS == 0) return 0; else return (uint32_t*)GetAddr(m_TLS->dwTLSIndexAddr); } - - // return a modifiable pointer inside this structure that corresponds to a virtual address - uint8_t *GetAddr(uint32_t x_dwVirtualAddress); - - const wchar_t *GetUnicodeFilenameAddr(); - private: - // constructor initialization - void ConstructorInit(); - - // return a modifiable pointer to logo bitmap data - uint8_t *GetLogoBitmap(uint32_t x_dwSize); - - - // used to encode/decode logo bitmap data - union LogoRLE - { - struct Eight - { - uint32_t bType1 : 1; - uint32_t Len : 3; - uint32_t Data : 4; - } - m_Eight; - - struct Sixteen - { - uint32_t bType1 : 1; - uint32_t bType2 : 1; - uint32_t Len : 10; - uint32_t Data : 4; - } - m_Sixteen; - }; - - public: - // used to decode game logo bitmap data - #include "AlignPrefix1.h" - struct X_D3DResourceLoc - { - uint32_t Common; - uint32_t Data; - uint32_t Lock; - uint32_t Format; - uint32_t Size; - } - #include "AlignPosfix1.h" - ; - - #include "AlignPrefix1.h" - // XPR structures - - // Purpose: - // The XPR file format allows multiple graphics resources to be pre-defined - // and bundled together into one file. These resources can be copied into - // memory and then immediately used in-place as D3D objects such as textures - // and vertex buffers. The structure below defines the XPR header and the - // unique identifier for this file type. - struct XprHeader - { - uint32_t dwXprMagic; // 'XPR0' or 'XPR1' - uint32_t dwXprTotalSize; - uint32_t dwXprHeaderSize; - } - #include "AlignPosfix1.h" - *m_xprHeader; - - #include "AlignPrefix1.h" - // Layout of SaveImage.xbx saved game image file - // - // File is XPR0 format. Since the XPR will always contain only a single - // 256x256 DXT1 image, we know exactly what the header portion will look like - struct XprImageHeader - { - XprHeader xprHeader; // Standard XPR struct - X_D3DResourceLoc d3dTexture; // Standard D3D texture struct - uint32_t dwEndOfHeader; // $FFFFFFFF - } - #include "AlignPosfix1.h" - *m_xprImageHeader; - - - #include "AlignPrefix1.h" - struct XprImage - { - XprImageHeader xprImageHeader; - char strPad[XPR_IMAGE_HDR_SIZE - sizeof(XprImageHeader)]; - unsigned char pBits; - } - #include "AlignPosfix1.h" - *m_xprImage; -}; - -// debug/retail XOR keys -const uint32_t XOR_EP_DEBUG = 0x94859D4B; // Entry Point (Debug) -const uint32_t XOR_EP_RETAIL = 0xA8FC57AB; // Entry Point (Retail) -const uint32_t XOR_KT_DEBUG = 0xEFB1F152; // Kernel Thunk (Debug) -const uint32_t XOR_KT_RETAIL = 0x5B6D40B6; // Kernel Thunk (Retail) - -// Sega Chihiro XOR keys -const uint32_t XOR_EP_CHIHIRO = 0x40B5C16E; -const uint32_t XOR_KT_CHIHIRO = 0x2290059D; - -// game region flags for Xbe certificate -const uint32_t XBEIMAGE_GAME_REGION_NA = 0x00000001; -const uint32_t XBEIMAGE_GAME_REGION_JAPAN = 0x00000002; -const uint32_t XBEIMAGE_GAME_REGION_RESTOFWORLD = 0x00000004; -const uint32_t XBEIMAGE_GAME_REGION_MANUFACTURING = 0x80000000; - -// media type flags for Xbe certificate -const uint32_t XBEIMAGE_MEDIA_TYPE_HARD_DISK = 0x00000001; -const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_X2 = 0x00000002; -const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_CD = 0x00000004; -const uint32_t XBEIMAGE_MEDIA_TYPE_CD = 0x00000008; -const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_5_RO = 0x00000010; -const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_9_RO = 0x00000020; -const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_5_RW = 0x00000040; -const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_9_RW = 0x00000080; -const uint32_t XBEIMAGE_MEDIA_TYPE_DONGLE = 0x00000100; -const uint32_t XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD = 0x00000200; -const uint32_t XBEIMAGE_MEDIA_TYPE_NONSECURE_HARD_DISK = 0x40000000; -const uint32_t XBEIMAGE_MEDIA_TYPE_NONSECURE_MODE = 0x80000000; -const uint32_t XBEIMAGE_MEDIA_TYPE_MEDIA_MASK = 0x00FFFFFF; - -// section type flags for Xbe -const uint32_t XBEIMAGE_SECTION_WRITEABLE = 0x00000001; -const uint32_t XBEIMAGE_SECTION_PRELOAD = 0x00000002; -const uint32_t XBEIMAGE_SECTION_EXECUTABLE = 0x00000004; -const uint32_t XBEIMAGE_SECTION_INSERTFILE = 0x00000008; -const uint32_t XBEIMAGE_SECTION_HEAD_PAGE_READONLY = 0x00000010; -const uint32_t XBEIMAGE_SECTION_TAIL_PAGE_READONLY = 0x00000020; -#endif +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2017 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef XBE_H +#define XBE_H + +#include "common\Error.h" +#include "common/xbox/Types.hpp" + +#include + + +//#include // For MAX_PATH +// The above leads to 55 compile errors, so until we've sorted out why that happens, declare MAX_PATH ourselves for now : +#define MAX_PATH 260 +#define XPR_IMAGE_WH 128 +#define XPR_IMAGE_DATA_SIZE (XPR_IMAGE_WH * XPR_IMAGE_WH) / 2 +#define XPR_IMAGE_HDR_SIZE 2048 + +namespace xboxkrnl +{ + typedef struct _XBE_SECTION XBEIMAGE_SECTION, *PXBEIMAGE_SECTION; +} + +// Xbe (Xbox Executable) file object +class Xbe : public Error +{ + public: + // construct via Xbe file + Xbe(const char *x_szFilename, bool bFromGUI); + + // deconstructor + ~Xbe(); + + // find an section by name + void *FindSection(char *zsSectionName); + + // Find a section by its definition + void* FindSection(xboxkrnl::PXBEIMAGE_SECTION section); + + // export to Xbe file + void Export(const char *x_szXbeFilename); + + // verify the integrity of the xbe header + bool CheckSignature(); + + // verify the integrity of an xbe section + bool CheckSectionIntegrity(uint32_t sectionIndex); + + // import logo bitmap from raw monochrome data + void ImportLogoBitmap(const uint8_t x_Gray[100*17]); + + // export logo bitmap to raw monochrome data + void ExportLogoBitmap(uint8_t x_Gray[100*17]); + + // purge illegal characters in Windows filenames or other OS's + void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|"); + + // Convert game region field to string + const char *GameRegionToString(); + + XbeType GetXbeType(); + + // Xbe header + #include "AlignPrefix1.h" + struct Header + { + uint32_t dwMagic; // 0x0000 - magic number [should be "XBEH"] + uint8_t pbDigitalSignature[256]; // 0x0004 - digital signature + uint32_t dwBaseAddr; // 0x0104 - base address + uint32_t dwSizeofHeaders; // 0x0108 - size of headers + uint32_t dwSizeofImage; // 0x010C - size of image + uint32_t dwSizeofImageHeader; // 0x0110 - size of image header + uint32_t dwTimeDate; // 0x0114 - timedate stamp + uint32_t dwCertificateAddr; // 0x0118 - certificate address + uint32_t dwSections; // 0x011C - number of sections + uint32_t dwSectionHeadersAddr; // 0x0120 - section headers address + + typedef struct + { + uint32_t bMountUtilityDrive : 1; // mount utility drive flag + uint32_t bFormatUtilityDrive : 1; // format utility drive flag + uint32_t bLimit64MB : 1; // limit development kit run time memory to 64mb flag + uint32_t bDontSetupHarddisk : 1; // don't setup hard disk flag + uint32_t Unused : 4; // unused (or unknown) + uint32_t Unused_b1 : 8; // unused (or unknown) + uint32_t Unused_b2 : 8; // unused (or unknown) + uint32_t Unused_b3 : 8; // unused (or unknown) + } InitFlags; + + union { // 0x0124 - initialization flags + InitFlags dwInitFlags; + uint32_t dwInitFlags_value; + }; + + uint32_t dwEntryAddr; // 0x0128 - entry point address + uint32_t dwTLSAddr; // 0x012C - thread local storage directory address + uint32_t dwPeStackCommit; // 0x0130 - size of stack commit + uint32_t dwPeHeapReserve; // 0x0134 - size of heap reserve + uint32_t dwPeHeapCommit; // 0x0138 - size of heap commit + uint32_t dwPeBaseAddr; // 0x013C - original base address + uint32_t dwPeSizeofImage; // 0x0140 - size of original image + uint32_t dwPeChecksum; // 0x0144 - original checksum + uint32_t dwPeTimeDate; // 0x0148 - original timedate stamp + uint32_t dwDebugPathnameAddr; // 0x014C - debug pathname address + uint32_t dwDebugFilenameAddr; // 0x0150 - debug filename address + uint32_t dwDebugUnicodeFilenameAddr; // 0x0154 - debug unicode filename address + uint32_t dwKernelImageThunkAddr; // 0x0158 - kernel image thunk address + uint32_t dwNonKernelImportDirAddr; // 0x015C - non kernel import directory address + uint32_t dwLibraryVersions; // 0x0160 - number of library versions + uint32_t dwLibraryVersionsAddr; // 0x0164 - library versions address + uint32_t dwKernelLibraryVersionAddr; // 0x0168 - kernel library version address + uint32_t dwXAPILibraryVersionAddr; // 0x016C - xapi library version address + uint32_t dwLogoBitmapAddr; // 0x0170 - logo bitmap address + uint32_t dwSizeofLogoBitmap; // 0x0174 - logo bitmap size + } + #include "AlignPosfix1.h" + m_Header; + + // Xbe header extra byte (used to preserve unknown data) + char *m_HeaderEx; + uint32_t m_ExSize; + + // Xbe certificate + #include "AlignPrefix1.h" + struct Certificate + { + uint32_t dwSize; // 0x0000 - size of certificate + uint32_t dwTimeDate; // 0x0004 - timedate stamp + uint32_t dwTitleId; // 0x0008 - title id + wchar_t wszTitleName[40]; // 0x000C - title name (unicode) + uint32_t dwAlternateTitleId[0x10]; // 0x005C - alternate title ids + uint32_t dwAllowedMedia; // 0x009C - allowed media types + uint32_t dwGameRegion; // 0x00A0 - game region + uint32_t dwGameRatings; // 0x00A4 - game ratings + uint32_t dwDiskNumber; // 0x00A8 - disk number + uint32_t dwVersion; // 0x00AC - version + uint8_t bzLanKey[16]; // 0x00B0 - lan key + uint8_t bzSignatureKey[16]; // 0x00C0 - signature key + // NOT ALL XBEs have these fields! + uint8_t bzTitleAlternateSignatureKey[16][16]; // 0x00D0 - alternate signature keys + uint32_t dwOriginalCertificateSize; // 0x01D0 - Original Certificate Size? + uint32_t dwOnlineService; // 0x01D4 - Online Service ID + uint32_t dwSecurityFlags; // 0x01D8 - Extra Security Flags + uint8_t bzCodeEncKey[16]; // 0x01DC - Code Encryption Key? + } + #include "AlignPosfix1.h" + m_Certificate; + + // Xbe section header + #include "AlignPrefix1.h" + struct SectionHeader + { + typedef struct + { + uint32_t bWritable : 1; // writable flag + uint32_t bPreload : 1; // preload flag + uint32_t bExecutable : 1; // executable flag + uint32_t bInsertedFile : 1; // inserted file flag + uint32_t bHeadPageRO : 1; // head page read only flag + uint32_t bTailPageRO : 1; // tail page read only flag + uint32_t Unused_a1 : 1; // unused (or unknown) + uint32_t Unused_a2 : 1; // unused (or unknown) + uint32_t Unused_b1 : 8; // unused (or unknown) + uint32_t Unused_b2 : 8; // unused (or unknown) + uint32_t Unused_b3 : 8; // unused (or unknown) + } _Flags; + + union { + _Flags dwFlags; + uint32_t dwFlags_value; + }; + + uint32_t dwVirtualAddr; // virtual address + uint32_t dwVirtualSize; // virtual size + uint32_t dwRawAddr; // file offset to raw data + uint32_t dwSizeofRaw; // size of raw data + uint32_t dwSectionNameAddr; // section name addr + uint32_t dwSectionRefCount; // section reference count + uint32_t dwHeadSharedRefCountAddr; // head shared page reference count address + uint32_t dwTailSharedRefCountAddr; // tail shared page reference count address + uint8_t bzSectionDigest[20]; // section digest + } + #include "AlignPosfix1.h" + *m_SectionHeader; + + // Xbe library versions + #include "AlignPrefix1.h" + struct LibraryVersion + { + char szName[8]; // library name + uint16_t wMajorVersion; // major version + uint16_t wMinorVersion; // minor version + uint16_t wBuildVersion; // build version + + typedef struct + { + uint16_t QFEVersion : 13; // QFE Version + uint16_t Approved : 2; // Approved? (0:no, 1:possibly, 2:yes) + uint16_t bDebugBuild : 1; // Is this a debug build? + } Flags; + + union { + Flags wFlags; + uint16_t wFlags_value; + }; + } + #include "AlignPosfix1.h" + *m_LibraryVersion; + + // Xbe thread local storage + #include "AlignPrefix1.h" + struct TLS + { + uint32_t dwDataStartAddr; // raw start address + uint32_t dwDataEndAddr; // raw end address + uint32_t dwTLSIndexAddr; // tls index address + uint32_t dwTLSCallbackAddr; // tls callback address + uint32_t dwSizeofZeroFill; // size of zero fill + uint32_t dwCharacteristics; // characteristics + } + #include "AlignPosfix1.h" + *m_TLS; + + // Xbe signature header + uint8_t* m_SignatureHeader; + + // Xbe section names, stored null terminated + char (*m_szSectionName)[10]; + + // Xbe sections + uint8_t **m_bzSection; + + // Xbe original path + char m_szPath[MAX_PATH]; + + // Xbe ascii title, translated from certificate title + char m_szAsciiTitle[40]; + + // retrieve thread local storage data address + uint8_t *GetTLSData() { if(m_TLS == 0) return 0; else return GetAddr(m_TLS->dwDataStartAddr); } + + // retrieve thread local storage index address + uint32_t *GetTLSIndex() { if(m_TLS == 0) return 0; else return (uint32_t*)GetAddr(m_TLS->dwTLSIndexAddr); } + + // return a modifiable pointer inside this structure that corresponds to a virtual address + uint8_t *GetAddr(uint32_t x_dwVirtualAddress); + + const wchar_t *GetUnicodeFilenameAddr(); + private: + // constructor initialization + void ConstructorInit(); + + // return a modifiable pointer to logo bitmap data + uint8_t *GetLogoBitmap(uint32_t x_dwSize); + + + // used to encode/decode logo bitmap data + union LogoRLE + { + struct Eight + { + uint32_t bType1 : 1; + uint32_t Len : 3; + uint32_t Data : 4; + } + m_Eight; + + struct Sixteen + { + uint32_t bType1 : 1; + uint32_t bType2 : 1; + uint32_t Len : 10; + uint32_t Data : 4; + } + m_Sixteen; + }; + + public: + // used to decode game logo bitmap data + #include "AlignPrefix1.h" + struct X_D3DResourceLoc + { + uint32_t Common; + uint32_t Data; + uint32_t Lock; + uint32_t Format; + uint32_t Size; + } + #include "AlignPosfix1.h" + ; + + #include "AlignPrefix1.h" + // XPR structures + + // Purpose: + // The XPR file format allows multiple graphics resources to be pre-defined + // and bundled together into one file. These resources can be copied into + // memory and then immediately used in-place as D3D objects such as textures + // and vertex buffers. The structure below defines the XPR header and the + // unique identifier for this file type. + struct XprHeader + { + uint32_t dwXprMagic; // 'XPR0' or 'XPR1' + uint32_t dwXprTotalSize; + uint32_t dwXprHeaderSize; + } + #include "AlignPosfix1.h" + *m_xprHeader; + + #include "AlignPrefix1.h" + // Layout of SaveImage.xbx saved game image file + // + // File is XPR0 format. Since the XPR will always contain only a single + // 256x256 DXT1 image, we know exactly what the header portion will look like + struct XprImageHeader + { + XprHeader xprHeader; // Standard XPR struct + X_D3DResourceLoc d3dTexture; // Standard D3D texture struct + uint32_t dwEndOfHeader; // $FFFFFFFF + } + #include "AlignPosfix1.h" + *m_xprImageHeader; + + + #include "AlignPrefix1.h" + struct XprImage + { + XprImageHeader xprImageHeader; + char strPad[XPR_IMAGE_HDR_SIZE - sizeof(XprImageHeader)]; + unsigned char pBits; + } + #include "AlignPosfix1.h" + *m_xprImage; +}; + +// debug/retail XOR keys +const uint32_t XOR_EP_DEBUG = 0x94859D4B; // Entry Point (Debug) +const uint32_t XOR_EP_RETAIL = 0xA8FC57AB; // Entry Point (Retail) +const uint32_t XOR_KT_DEBUG = 0xEFB1F152; // Kernel Thunk (Debug) +const uint32_t XOR_KT_RETAIL = 0x5B6D40B6; // Kernel Thunk (Retail) + +// Sega Chihiro XOR keys +const uint32_t XOR_EP_CHIHIRO = 0x40B5C16E; +const uint32_t XOR_KT_CHIHIRO = 0x2290059D; + +// game region flags for Xbe certificate +const uint32_t XBEIMAGE_GAME_REGION_NA = 0x00000001; +const uint32_t XBEIMAGE_GAME_REGION_JAPAN = 0x00000002; +const uint32_t XBEIMAGE_GAME_REGION_RESTOFWORLD = 0x00000004; +const uint32_t XBEIMAGE_GAME_REGION_MANUFACTURING = 0x80000000; + +// media type flags for Xbe certificate +const uint32_t XBEIMAGE_MEDIA_TYPE_HARD_DISK = 0x00000001; +const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_X2 = 0x00000002; +const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_CD = 0x00000004; +const uint32_t XBEIMAGE_MEDIA_TYPE_CD = 0x00000008; +const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_5_RO = 0x00000010; +const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_9_RO = 0x00000020; +const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_5_RW = 0x00000040; +const uint32_t XBEIMAGE_MEDIA_TYPE_DVD_9_RW = 0x00000080; +const uint32_t XBEIMAGE_MEDIA_TYPE_DONGLE = 0x00000100; +const uint32_t XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD = 0x00000200; +const uint32_t XBEIMAGE_MEDIA_TYPE_NONSECURE_HARD_DISK = 0x40000000; +const uint32_t XBEIMAGE_MEDIA_TYPE_NONSECURE_MODE = 0x80000000; +const uint32_t XBEIMAGE_MEDIA_TYPE_MEDIA_MASK = 0x00FFFFFF; + +// section type flags for Xbe +const uint32_t XBEIMAGE_SECTION_WRITEABLE = 0x00000001; +const uint32_t XBEIMAGE_SECTION_PRELOAD = 0x00000002; +const uint32_t XBEIMAGE_SECTION_EXECUTABLE = 0x00000004; +const uint32_t XBEIMAGE_SECTION_INSERTFILE = 0x00000008; +const uint32_t XBEIMAGE_SECTION_HEAD_PAGE_READONLY = 0x00000010; +const uint32_t XBEIMAGE_SECTION_TAIL_PAGE_READONLY = 0x00000020; +#endif diff --git a/src/common/xbe/XbePrinter.cpp b/src/common/xbe/XbePrinter.cpp index c3237fea1..77291d8f0 100644 --- a/src/common/xbe/XbePrinter.cpp +++ b/src/common/xbe/XbePrinter.cpp @@ -31,7 +31,7 @@ #include // For ctime #include // For std::stringstream #include // For std::setfill, std::uppercase, std::hex -#include "common/util/strConverter.hpp" // for utf16le_to_ascii +#include "common/util/strConverter.hpp" // for utf16le_to_ascii extern std::string FormatTitleId(uint32_t title_id); // Exposed in Emu.cpp @@ -416,12 +416,12 @@ std::string XbePrinter::GenSectionHeaders() text << "Section Reference Count : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwSectionRefCount << "\n"; text << "Head Shared Reference Count Addr : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwHeadSharedRefCountAddr << "\n"; text << "Tail Shared Reference Count Addr : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwTailSharedRefCountAddr << "\n"; - text << GenSectionDigest(Xbe_to_print->m_SectionHeader[v]) << "\n"; - if (Xbe_to_print->CheckSectionIntegrity(v)) { - text << "SHA hash check of section " << Xbe_to_print->m_szSectionName[v] << " successful" << "\n\n"; - } - else { - text << "SHA hash of section " << Xbe_to_print->m_szSectionName[v] << " doesn't match, section is corrupted" << "\n\n"; + text << GenSectionDigest(Xbe_to_print->m_SectionHeader[v]) << "\n"; + if (Xbe_to_print->CheckSectionIntegrity(v)) { + text << "SHA hash check of section " << Xbe_to_print->m_szSectionName[v] << " successful" << "\n\n"; + } + else { + text << "SHA hash of section " << Xbe_to_print->m_szSectionName[v] << " doesn't match, section is corrupted" << "\n\n"; } } return text.str(); diff --git a/src/common/xdvdfs-tools/xdvdfs.h b/src/common/xdvdfs-tools/xdvdfs.h index 031cf278b..c4bde8453 100644 --- a/src/common/xdvdfs-tools/xdvdfs.h +++ b/src/common/xdvdfs-tools/xdvdfs.h @@ -5,8 +5,8 @@ #include //#include -#include "buffered_io.h" - +#include "buffered_io.h" + using namespace xboxkrnl; CONST CHAR *XDVDFS_Signature = "MICROSOFT*XBOX*MEDIA"; @@ -155,4 +155,4 @@ extern DWORD XDVDFS_FileSeek( int Delta, DWORD SeekMode); -#endif // __XDVDFS_H__ +#endif // __XDVDFS_H__ diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 867b0eadf..d156abb1c 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -1,8482 +1,8482 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** -#define LOG_PREFIX CXBXR_MODULE::D3D8 - -#ifdef CXBXR_EMU_EXPORTS // DbgConsole only in Cxbx/cxbxr, not in cxbxr-emu - #undef INCLUDE_DBG_CONSOLE -#else - #define INCLUDE_DBG_CONSOLE -#endif -#include "common\util\hasher.h" -#include -#include - - -#include -#include "common\util\CxbxUtil.h" -#include "CxbxVersion.h" -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\support\Emu.h" -#include "EmuShared.h" -#ifdef INCLUDE_DBG_CONSOLE -#include "gui\DbgConsole.h" -#endif -#include "core\hle\D3D8\ResourceTracker.h" -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For LPDIRECTDRAWSURFACE7 -#include "core\hle\D3D8\XbVertexBuffer.h" -#include "core\hle\D3D8\XbVertexShader.h" -#include "core\hle\D3D8\XbPixelShader.h" // For DxbxUpdateActivePixelShader -#include "core\hle\D3D8\XbPushBuffer.h" -#include "core\kernel\memory-manager\VMManager.h" // for g_VMManager -#include "core\hle\XAPI\Xapi.h" // For EMUPATCH -#include "core\hle\D3D8\XbConvert.h" -#include "Logging.h" -#include "..\XbD3D8Logging.h" -#include "core\hle\Intercept.hpp" // for bLLE_GPU -#include "devices\video\nv2a.h" // For GET_MASK, NV_PGRAPH_CONTROL_0, PUSH_METHOD -#include "gui/resource/ResCxbx.h" -#include "RenderStates.h" -#include "TextureStates.h" -#include "WalkIndexBuffer.h" -#include "core\kernel\common\strings.hpp" // For uem_str -#include "common\input\SdlJoystick.h" -#include "common/util/strConverter.hpp" // for utf8_to_utf16 -#include "VertexShaderSource.h" - -#include -#include -#include -#include -#include - -XboxRenderStateConverter XboxRenderStates; -XboxTextureStateConverter XboxTextureStates; - -// Allow use of time duration literals (making 16ms, etc possible) -using namespace std::literals::chrono_literals; - -// Global(s) -HWND g_hEmuWindow = NULL; // rendering window -IDirect3DDevice *g_pD3DDevice = nullptr; // Direct3D Device - -// Static Variable(s) -static IDirectDrawSurface7 *g_pDDSPrimary = nullptr; // DirectDraw7 Primary Surface -static IDirectDrawClipper *g_pDDClipper = nullptr; // DirectDraw7 Clipper -static IDirectDraw7 *g_pDD7 = nullptr; // DirectDraw7 -static HMONITOR g_hMonitor = NULL; // Handle to DirectDraw monitor -static GUID g_ddguid = { 0 }; // DirectDraw driver GUID -static DDCAPS g_DriverCaps = { 0 }; - -static bool g_bSupportsFormatSurface[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support surface format? -static bool g_bSupportsFormatSurfaceRenderTarget[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support surface format? -static bool g_bSupportsFormatSurfaceDepthStencil[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support surface format? -static bool g_bSupportsFormatTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support texture format? -static bool g_bSupportsFormatTextureRenderTarget[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support texture format? -static bool g_bSupportsFormatTextureDepthStencil[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support texture format? -static bool g_bSupportsFormatVolumeTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support surface format? -static bool g_bSupportsFormatCubeTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support surface format? -static HBRUSH g_hBgBrush = NULL; // Background Brush -static volatile bool g_bRenderWindowActive = false; -static BOOL g_bIsFauxFullscreen = FALSE; -static DWORD g_OverlaySwap = 0; // Set in D3DDevice_UpdateOverlay -static int g_iWireframe = 0; // wireframe toggle -static bool g_bHack_UnlockFramerate = false; // ignore the xbox presentation interval -static bool g_bHasDepth = false; // Does device have a Depth Buffer? -static bool g_bHasStencil = false; // Does device have a Stencil Buffer? -static DWORD g_dwPrimPerFrame = 0; // Number of primitives within one frame - -static Settings::s_video g_XBVideo; - -// D3D based variables -static IDirect3D *g_pDirect3D = nullptr; - D3DCAPS g_D3DCaps = {}; // Direct3D Caps -static IDirect3DVertexBuffer *g_pDummyBuffer = nullptr; // Dummy buffer, used to set unused stream sources with -static IDirect3DIndexBuffer *g_pClosingLineLoopHostIndexBuffer = nullptr; -static IDirect3DIndexBuffer *g_pQuadToTriangleHostIndexBuffer = nullptr; - -static bool g_bEnableHostQueryVisibilityTest = true; -static std::stack g_HostQueryVisibilityTests; -static std::map g_HostVisibilityTestMap; - -// cached Direct3D state variable(s) -static size_t g_QuadToTriangleHostIndexBuffer_Size = 0; // = NrOfQuadIndices -static INDEX16 *g_pQuadToTriangleIndexData = nullptr; -static size_t g_QuadToTriangleIndexData_Size = 0; // = NrOfQuadIndices - -static CxbxVertexBufferConverter VertexBufferConverter = {}; - -struct { - XTL::X_D3DSurface Surface; - RECT SrcRect; - RECT DstRect; - BOOL EnableColorKey; - D3DCOLOR ColorKey; -} g_OverlayProxy; - -typedef struct { - // Arguments to D3DDevice_InsertCallback : - XTL::X_D3DCALLBACK pCallback; - XTL::X_D3DCALLBACKTYPE Type; - XTL::DWORD Context; -} s_Xbox_Callback; - -static std::queue g_Xbox_CallbackQueue; -static bool g_bHack_DisableHostGPUQueries = false; // TODO : Make configurable -static IDirect3DQuery *g_pHostQueryWaitForIdle = nullptr; -static IDirect3DQuery *g_pHostQueryCallbackEvent = nullptr; - -// Vertex buffer symbols, declared in XbVertexBuffer.cpp -extern void CxbxImpl_SetStreamSource(UINT StreamNumber, XTL::X_D3DVertexBuffer* pStreamData, UINT Stride); - -static std::condition_variable g_VBConditionVariable; // Used in BlockUntilVerticalBlank -static std::mutex g_VBConditionMutex; // Used in BlockUntilVerticalBlank -static DWORD g_VBLastSwap = 0; - -static XTL::DWORD g_Xbox_PresentationInterval_Default = D3DPRESENT_INTERVAL_IMMEDIATE; - XTL::DWORD g_Xbox_PresentationInterval_Override = 0; -static XTL::X_D3DSWAPDATA g_Xbox_SwapData = {0}; // current swap information -static XTL::X_D3DSWAPCALLBACK g_pXbox_SwapCallback = xbnullptr; // Swap/Present callback routine -static XTL::X_D3DVBLANKDATA g_Xbox_VBlankData = {0}; // current vertical blank information -static XTL::X_D3DVBLANKCALLBACK g_pXbox_VerticalBlankCallback = xbnullptr; // Vertical-Blank callback routine - - XTL::X_D3DSurface *g_pXbox_BackBufferSurface = xbnullptr; -static XTL::X_D3DSurface *g_pXbox_DefaultDepthStencilSurface = xbnullptr; - XTL::X_D3DSurface *g_pXbox_RenderTarget = xbnullptr; -static XTL::X_D3DSurface *g_pXbox_DepthStencil = xbnullptr; - XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; - - XTL::X_VERTEXSHADERCONSTANTMODE g_Xbox_VertexShaderConstantMode = X_D3DSCM_192CONSTANTS; // Set by D3DDevice_SetShaderConstantMode, TODO : Move to XbVertexShader.cpp -static XTL::DWORD g_Xbox_BaseVertexIndex = 0; // Set by D3DDevice_SetIndices, read by D3DDevice_DrawIndexedVertices : a value that's effectively added to every vertex index (as stored in an index buffer) by multiplying this by vertex stride and added to the vertex buffer start (see BaseVertexIndex in CxbxDrawIndexed) -static XTL::DWORD *g_pXbox_BeginPush_Buffer = xbnullptr; // primary push buffer - - XTL::X_PixelShader* g_pXbox_PixelShader = xbnullptr; -static XTL::PVOID g_pXbox_Palette_Data[XTL::X_D3DTS_STAGECOUNT] = { xbnullptr, xbnullptr, xbnullptr, xbnullptr }; // cached palette pointer -static unsigned g_Xbox_Palette_Size[XTL::X_D3DTS_STAGECOUNT] = { 0 }; // cached palette size - - - XTL::X_D3DBaseTexture *g_pXbox_SetTexture[XTL::X_D3DTS_STAGECOUNT] = {0,0,0,0}; // Set by our D3DDevice_SetTexture and D3DDevice_SwitchTexture patches -static XTL::X_D3DBaseTexture CxbxActiveTextureCopies[XTL::X_D3DTS_STAGECOUNT] = {}; // Set by D3DDevice_SwitchTexture. Cached active texture - -/* Unused : -static XTL::DWORD *g_Xbox_D3DDevice; // TODO: This should be a D3DDevice structure - -static DWORD g_dwVertexShaderUsage = 0; // Unused. If needed, move to XbVertexShader.cpp -*/ - - XTL::DWORD g_Xbox_VertexShader_Handle = 0; - -// Static Function(s) -static BOOL WINAPI EmuEnumDisplayDevices(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm); -static DWORD WINAPI EmuRenderWindow(LPVOID); -static DWORD WINAPI EmuCreateDeviceProxy(LPVOID); -static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -static DWORD WINAPI EmuUpdateTickCount(LPVOID); -static inline void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize); -static void UpdateCurrentMSpFAndFPS(); // Used for benchmarking/fps count - -extern void UpdateFPSCounter(); - -#define CXBX_D3DCOMMON_IDENTIFYING_MASK (X_D3DCOMMON_TYPE_MASK | X_D3DCOMMON_D3DCREATED) - -typedef struct resource_key_hash { - // All Xbox X_D3DResource structs have these fields : - DWORD Common; // We set this to the CXBX_D3DCOMMON_IDENTIFYING_MASK bits of the source Common field - DWORD Data; // We set this as-is to a copy of the source Data field - // DWORD Lock; // We leave away the source Lock field, since it's entirely volatile (not deterministic) - union { - struct { - // For non-pixel-containers, we set the Xbox resource address for now (TODO : Come up with something better) : - xbaddr ResourceAddr; // We set this as-is - }; - struct { - // For XTL::X_D3DPixelContainer's we also set these fields : - DWORD Format; // We set this as-is - DWORD Size; // We set this as-is - // For X_D3DFMT_P8 paletized pixel-containers, we also set this field : - uint64_t PaletteHash; - }; - }; - - // These operator overloads are required to use resource_key_t in resource_cache_t : - bool operator==(const struct resource_key_hash& other) const - { - return (Common == other.Common) - && (Data == other.Data) - && (Format == other.Format) - && (Size == other.Size) - && (PaletteHash == other.PaletteHash); - // Note : ResourceAddr doesn't need comparison, since it's union'ed with Format,Size,PaletteHash already - } - - // See https://marknelson.us/posts/2011/09/03/hash-functions-for-c-unordered-containers.html - size_t operator()(const struct resource_key_hash& value) const - { - return (size_t)ComputeHash((void*)&value, sizeof(value)); - } -} resource_key_t; - -// information passed to the create device proxy thread -struct EmuD3D8CreateDeviceProxyData -{ - // Set by EmuD3DInit() - XTL::UINT Adapter; - D3DDEVTYPE DeviceType; - HWND hFocusWindow; - // Set byt EMUPATCH(Direct3D_CreateDevice) - XTL::X_D3DPRESENT_PARAMETERS XboxPresentationParameters; - volatile bool bReady; - volatile bool bCreate; // false : release - // Set by EmuCreateDeviceProxy() - XTL::DWORD BehaviorFlags; - D3DPRESENT_PARAMETERS HostPresentationParameters; - volatile HRESULT hRet; -} -g_EmuCDPD = {0}; - -// Declare trampolines -#define XB_TRAMPOLINES(XB_MACRO) \ - XB_MACRO(HRESULT, WINAPI, D3DDevice_CreateVertexShader, (CONST DWORD*, CONST DWORD*, DWORD*, DWORD) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_DeleteVertexShader, (DWORD) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_GetBackBuffer, (INT, D3DBACKBUFFER_TYPE, XTL::X_D3DSurface**) ); \ - XB_MACRO(XTL::X_D3DSurface*, WINAPI, D3DDevice_GetBackBuffer2, (INT) ); \ - XB_MACRO(HRESULT, WINAPI, D3DDevice_GetDepthStencilSurface, (XTL::X_D3DSurface**) ); \ - XB_MACRO(XTL::X_D3DSurface*, WINAPI, D3DDevice_GetDepthStencilSurface2, (VOID) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_GetDisplayMode, (XTL::X_D3DDISPLAYMODE*) ); \ - XB_MACRO(HRESULT, WINAPI, D3DDevice_GetRenderTarget, (XTL::X_D3DSurface**) ); \ - XB_MACRO(XTL::X_D3DSurface*, WINAPI, D3DDevice_GetRenderTarget2, (VOID) ); \ - XB_MACRO(HRESULT, WINAPI, D3DDevice_LightEnable, (DWORD, BOOL) ); \ - /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShader, (DWORD, DWORD) );*/\ - /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShaderProgram, (CONST DWORD*, DWORD) );*/\ - /*XB_MACRO(VOID, __stdcall, D3DDevice_LoadVertexShader_0, () );*/\ - /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShader_4, (DWORD) );*/\ - XB_MACRO(HRESULT, WINAPI, D3DDevice_PersistDisplay, (VOID) ); \ - XB_MACRO(HRESULT, WINAPI, D3DDevice_Reset, (XTL::X_D3DPRESENT_PARAMETERS*) ); \ - /*XB_MACRO(VOID, WINAPI, D3DDevice_SelectVertexShader, (DWORD, DWORD) );*/\ - /*XB_MACRO(VOID, __stdcall, D3DDevice_SelectVertexShader_0, () );*/\ - /*XB_MACRO(VOID, __stdcall, D3DDevice_SelectVertexShader_4, (DWORD) );*/\ - /*XB_MACRO(VOID, WINAPI, D3DDevice_SetGammaRamp, (DWORD, CONST X_D3DGAMMARAMP*) );*/\ - XB_MACRO(VOID, WINAPI, D3DDevice_SetIndices, (XTL::X_D3DIndexBuffer*, UINT) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetIndices_4, (UINT) ); \ - XB_MACRO(HRESULT, WINAPI, D3DDevice_SetLight, (DWORD, CONST XTL::X_D3DLIGHT8*) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetPixelShader, (DWORD) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetPixelShader_0, () ); \ - XB_MACRO(VOID, __fastcall, D3DDevice_SetRenderState_Simple, (DWORD, DWORD) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetRenderTarget, (XTL::X_D3DSurface*, XTL::X_D3DSurface*) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetStreamSource, (UINT, XTL::X_D3DVertexBuffer*, UINT) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetStreamSource_4, (UINT, XTL::X_D3DVertexBuffer*, UINT) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetStreamSource_8, (XTL::X_D3DVertexBuffer*, UINT) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetTexture, (DWORD, XTL::X_D3DBaseTexture*) ); \ - XB_MACRO(VOID, WINAPI, D3DDevice_SetTexture_4, (XTL::X_D3DBaseTexture*) ); \ - /*XB_MACRO(VOID, WINAPI, D3DDevice_SetVertexShader, (DWORD) );*/\ - /*XB_MACRO(VOID, WINAPI, D3DDevice_SetVertexShaderInput, (DWORD, UINT, XTL::X_STREAMINPUT*) );*/\ - XB_MACRO(VOID, WINAPI, D3DDevice_SetViewport, (CONST XTL::X_D3DVIEWPORT8*) ); \ - XB_MACRO(VOID, WINAPI, D3D_DestroyResource, (XTL::X_D3DResource*) ); \ - XB_MACRO(VOID, WINAPI, D3D_DestroyResource__LTCG, (VOID) ); \ - XB_MACRO(HRESULT, WINAPI, Direct3D_CreateDevice, (UINT, D3DDEVTYPE, HWND, DWORD, XTL::X_D3DPRESENT_PARAMETERS*, IDirect3DDevice**) ); \ - XB_MACRO(HRESULT, WINAPI, Direct3D_CreateDevice_16, (UINT, D3DDEVTYPE, HWND, XTL::X_D3DPRESENT_PARAMETERS*) ); \ - XB_MACRO(HRESULT, WINAPI, Direct3D_CreateDevice_4, (XTL::X_D3DPRESENT_PARAMETERS*) ); \ - XB_MACRO(VOID, WINAPI, Lock2DSurface, (XTL::X_D3DPixelContainer*, D3DCUBEMAP_FACES, UINT, D3DLOCKED_RECT*, RECT*, DWORD) ); \ - XB_MACRO(VOID, WINAPI, Lock3DSurface, (XTL::X_D3DPixelContainer*, UINT, D3DLOCKED_BOX*, D3DBOX*, DWORD) ); \ - -XB_TRAMPOLINES(XB_trampoline_declare); - -void LookupTrampolines() -{ - XB_TRAMPOLINES(XB_trampoline_lookup); -} - -#undef XB_TRAMPOLINES - -const char *CxbxGetErrorDescription(HRESULT hResult) -{ - // TODO : For D3D9, Use DXGetErrorDescription9(hResult) (requires another DLL though) - // See : https://www.fairyengine.com/articles/dxmultiviews.htm - // and : https://web.archive.org/web/20101231193248/https://www.gamedev.net/community/forums/showfaq.asp?forum_id=10 - // and : https://www.gamedev.net/community/forums/topic.asp?topic_id=16157 - // But https://blogs.msdn.microsoft.com/chuckw/2012/04/24/wheres-dxerr-lib/ - // suggests to use FormatMessage with FORMAT_MESSAGE_FROM_SYSTEM for DirectX errors - switch (hResult) - { - case D3DERR_INVALIDCALL: return "Invalid Call"; - case D3DERR_NOTAVAILABLE: return "Not Available"; - // case D3DERR_OUTOFVIDEOMEMORY: return "Out of Video Memory"; // duplicate of DDERR_OUTOFVIDEOMEMORY - - case D3D_OK: return "No error occurred."; -#if 0 - case D3DERR_BADMAJORVERSION: return "The service that you requested is unavailable in this major version of DirectX. (A major version denotes a primary release, such as DirectX 6.0.) "; - case D3DERR_BADMINORVERSION: return "The service that you requested is available in this major version of DirectX, but not in this minor version. Get the latest version of the component run time from Microsoft. (A minor version denotes a secondary release, such as DirectX 6.1.) "; - case D3DERR_COLORKEYATTACHED: return "The application attempted to create a texture with a surface that uses a color key for transparency. "; -#endif - case D3DERR_CONFLICTINGTEXTUREFILTER: return "The current texture filters cannot be used together. "; - case D3DERR_CONFLICTINGTEXTUREPALETTE: return "The current textures cannot be used simultaneously. This generally occurs when a multitexture device requires that all palettized textures simultaneously enabled also share the same palette. "; - case D3DERR_CONFLICTINGRENDERSTATE: return "The currently set render states cannot be used together. "; -#if 0 - case D3DERR_DEVICEAGGREGATED: return "The IDirect3DDevice7::SetRenderTarget method was called on a device that was retrieved from the render target surface. "; - case D3DERR_EXECUTE_CLIPPED_FAILED: return "The execute buffer could not be clipped during execution. "; - case D3DERR_EXECUTE_CREATE_FAILED: return "The execute buffer could not be created. This typically occurs when no memory is available to allocate the execute buffer. "; - case D3DERR_EXECUTE_DESTROY_FAILED: return "The memory for the execute buffer could not be deallocated. "; - case D3DERR_EXECUTE_FAILED: return "The contents of the execute buffer are invalid and cannot be executed. "; - case D3DERR_EXECUTE_LOCK_FAILED: return "The execute buffer could not be locked. "; - case D3DERR_EXECUTE_LOCKED: return "The operation requested by the application could not be completed because the execute buffer is locked. "; - case D3DERR_EXECUTE_NOT_LOCKED: return "The execute buffer could not be unlocked because it is not currently locked. "; - case D3DERR_EXECUTE_UNLOCK_FAILED: return "The execute buffer could not be unlocked. "; - case D3DERR_INBEGIN: return "The requested operation cannot be completed while scene rendering is taking place. Try again after the scene is completed and the IDirect3DDevice7::EndScene method is called. "; - case D3DERR_INBEGINSTATEBLOCK: return "The operation cannot be completed while recording states for a state block. Complete recording by calling the IDirect3DDevice7::EndStateBlock method, and try again. "; - case D3DERR_INITFAILED: return "A rendering device could not be created because the new device could not be initialized. "; - case D3DERR_INVALID_DEVICE: return "The requested device type is not valid. "; - case D3DERR_INVALIDCURRENTVIEWPORT: return "The currently selected viewport is not valid. "; - case D3DERR_INVALIDMATRIX: return "The requested operation could not be completed because the combination of the currently set world, view, and projection matrices is invalid (the determinant of the combined matrix is 0). "; - case D3DERR_INVALIDPALETTE: return "The palette associated with a surface is invalid. "; - case D3DERR_INVALIDPRIMITIVETYPE: return "The primitive type specified by the application is invalid. "; - case D3DERR_INVALIDRAMPTEXTURE: return "Ramp mode is being used, and the texture handle in the current material does not match the current texture handle that is set as a render state. "; - case D3DERR_INVALIDSTATEBLOCK: return "The state block handle is invalid. "; - case D3DERR_INVALIDVERTEXFORMAT: return "The combination of flexible vertex format flags specified by the application is not valid. "; - case D3DERR_INVALIDVERTEXTYPE: return "The vertex type specified by the application is invalid. "; - case D3DERR_LIGHT_SET_FAILED: return "The attempt to set lighting parameters for a light object failed. "; - case D3DERR_LIGHTHASVIEWPORT: return "The requested operation failed because the light object is associated with another viewport. "; - case D3DERR_LIGHTNOTINTHISVIEWPORT: return "The requested operation failed because the light object has not been associated with this viewport. "; - case D3DERR_MATERIAL_CREATE_FAILED: return "The material could not be created. This typically occurs when no memory is available to allocate for the material. "; - case D3DERR_MATERIAL_DESTROY_FAILED: return "The memory for the material could not be deallocated. "; - case D3DERR_MATERIAL_GETDATA_FAILED: return "The material parameters could not be retrieved. "; - case D3DERR_MATERIAL_SETDATA_FAILED: return "The material parameters could not be set. "; - case D3DERR_MATRIX_CREATE_FAILED: return "The matrix could not be created. This can occur when no memory is available to allocate for the matrix. "; - case D3DERR_MATRIX_DESTROY_FAILED: return "The memory for the matrix could not be deallocated. "; - case D3DERR_MATRIX_GETDATA_FAILED: return "The matrix data could not be retrieved. This can occur when the matrix was not created by the current device. "; - case D3DERR_MATRIX_SETDATA_FAILED: return "The matrix data could not be set. This can occur when the matrix was not created by the current device. "; - case D3DERR_NOCURRENTVIEWPORT: return "The viewport parameters could not be retrieved because none have been set. "; - case D3DERR_NOTINBEGIN: return "The requested rendering operation could not be completed because scene rendering has not begun. Call IDirect3DDevice7::BeginScene to begin rendering, and try again. "; - case D3DERR_NOTINBEGINSTATEBLOCK: return "The requested operation could not be completed because it is only valid while recording a state block. Call the IDirect3DDevice7::BeginStateBlock method, and try again. "; - case D3DERR_NOVIEWPORTS: return "The requested operation failed because the device currently has no viewports associated with it. "; - case D3DERR_SCENE_BEGIN_FAILED: return "Scene rendering could not begin. "; - case D3DERR_SCENE_END_FAILED: return "Scene rendering could not be completed. "; - case D3DERR_SCENE_IN_SCENE: return "Scene rendering could not begin because a previous scene was not completed by a call to the IDirect3DDevice7::EndScene method. "; - case D3DERR_SCENE_NOT_IN_SCENE: return "Scene rendering could not be completed because a scene was not started by a previous call to the IDirect3DDevice7::BeginScene method. "; - case D3DERR_SETVIEWPORTDATA_FAILED: return "The viewport parameters could not be set. "; - case D3DERR_STENCILBUFFER_NOTPRESENT: return "The requested stencil buffer operation could not be completed because there is no stencil buffer attached to the render target surface. "; - case D3DERR_SURFACENOTINVIDMEM: return "The device could not be created because the render target surface is not located in video memory. (Hardware-accelerated devices require video-memory render target surfaces.) "; - case D3DERR_TEXTURE_BADSIZE: return "The dimensions of a current texture are invalid. This can occur when an application attempts to use a texture that has dimensions that are not a power of 2 with a device that requires them. "; - case D3DERR_TEXTURE_CREATE_FAILED: return "The texture handle for the texture could not be retrieved from the driver. "; - case D3DERR_TEXTURE_DESTROY_FAILED: return "The device was unable to deallocate the texture memory. "; - case D3DERR_TEXTURE_GETSURF_FAILED: return "The DirectDraw surface used to create the texture could not be retrieved. "; - case D3DERR_TEXTURE_LOAD_FAILED: return "The texture could not be loaded. "; - case D3DERR_TEXTURE_LOCK_FAILED: return "The texture could not be locked. "; - case D3DERR_TEXTURE_LOCKED: return "The requested operation could not be completed because the texture surface is currently locked. "; - case D3DERR_TEXTURE_NO_SUPPORT: return "The device does not support texture mapping. "; - case D3DERR_TEXTURE_NOT_LOCKED: return "The requested operation could not be completed because the texture surface is not locked. "; - case D3DERR_TEXTURE_SWAP_FAILED: return "The texture handles could not be swapped. "; - case D3DERR_TEXTURE_UNLOCK_FAILED: return "The texture surface could not be unlocked. "; -#endif - case D3DERR_TOOMANYOPERATIONS: return "The application is requesting more texture-filtering operations than the device supports. "; -#if 0 - case D3DERR_TOOMANYPRIMITIVES: return "The device is unable to render the provided number of primitives in a single pass. "; -#endif - case D3DERR_UNSUPPORTEDALPHAARG: return "The device does not support one of the specified texture-blending arguments for the alpha channel. "; - case D3DERR_UNSUPPORTEDALPHAOPERATION: return "The device does not support one of the specified texture-blending operations for the alpha channel. "; - case D3DERR_UNSUPPORTEDCOLORARG: return "The device does not support one of the specified texture-blending arguments for color values. "; - case D3DERR_UNSUPPORTEDCOLOROPERATION: return "The device does not support one of the specified texture-blending operations for color values. "; - case D3DERR_UNSUPPORTEDFACTORVALUE: return "The specified texture factor value is not supported by the device. "; - case D3DERR_UNSUPPORTEDTEXTUREFILTER: return "The specified texture filter is not supported by the device. "; -#if 0 - case D3DERR_VBUF_CREATE_FAILED: return "The vertex buffer could not be created. This can happen when there is insufficient memory to allocate a vertex buffer. "; - case D3DERR_VERTEXBUFFERLOCKED: return "The requested operation could not be completed because the vertex buffer is locked. "; - case D3DERR_VERTEXBUFFEROPTIMIZED: return "The requested operation could not be completed because the vertex buffer is optimized. (The contents of optimized vertex buffers are driver-specific and considered private.) "; - case D3DERR_VERTEXBUFFERUNLOCKFAILED: return "The vertex buffer could not be unlocked because the vertex buffer memory was overrun. Be sure that your application does not write beyond the size of the vertex buffer. "; - case D3DERR_VIEWPORTDATANOTSET: return "The requested operation could not be completed because viewport parameters have not yet been set. Set the viewport parameters by calling the IDirect3DDevice7::SetViewport method, and try again. "; - case D3DERR_VIEWPORTHASNODEVICE: return "The requested operation could not be completed because the viewport has not yet been associated with a device. Associate the viewport with a rendering device by calling the IDirect3DDevice3::AddViewport method, and try again. "; -#endif - case D3DERR_WRONGTEXTUREFORMAT: return "The pixel format of the texture surface is not valid. "; -#if 0 - case D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY: return "The requested operation could not be completed because the specified device requires system-memory depth-buffer surfaces. (Software rendering devices require system-memory depth buffers.) "; - case D3DERR_ZBUFF_NEEDS_VIDEOMEMORY: return "The requested operation could not be completed because the specified device requires video-memory depth-buffer surfaces. (Hardware-accelerated devices require video-memory depth buffers.) "; - case D3DERR_ZBUFFER_NOTPRESENT: return "The requested operation could not be completed because the render target surface does not have an attached depth buffer. "; - case DD_OK: return "The request completed successfully."; -#endif - case DDERR_ALREADYINITIALIZED: return "The object has already been initialized."; - case DDERR_BLTFASTCANTCLIP: return "A DirectDrawClipper object is attached to a source surface that has passed into a call to the IDirectDrawSurface7::BltFast method."; - case DDERR_CANNOTATTACHSURFACE: return "A surface cannot be attached to another requested surface."; - case DDERR_CANNOTDETACHSURFACE: return "A surface cannot be detached from another requested surface."; - case DDERR_CANTCREATEDC: return "Windows cannot create any more device contexts (DCs), or a DC has requested a palette-indexed surface when the surface had no palette and the display mode was not palette-indexed (in this case DirectDraw cannot select a proper palette into the DC)."; - case DDERR_CANTDUPLICATE: return "Primary and 3-D surfaces, or surfaces that are implicitly created, cannot be duplicated."; - case DDERR_CANTLOCKSURFACE: return "Access to this surface is refused because an attempt was made to lock the primary surface without DCI support."; - case DDERR_CANTPAGELOCK: return "An attempt to page-lock a surface failed. Page lock does not work on a display-memory surface or an emulated primary surface."; - case DDERR_CANTPAGEUNLOCK: return "An attempt to page-unlock a surface failed. Page unlock does not work on a display-memory surface or an emulated primary surface."; - case DDERR_CLIPPERISUSINGHWND: return "An attempt was made to set a clip list for a DirectDrawClipper object that is already monitoring a window handle."; - case DDERR_COLORKEYNOTSET: return "No source color key is specified for this operation."; - case DDERR_CURRENTLYNOTAVAIL: return "No support is currently available."; - case DDERR_DDSCAPSCOMPLEXREQUIRED: return "New for DirectX 7.0. The surface requires the DDSCAPS_COMPLEX flag."; - case DDERR_DCALREADYCREATED: return "A device context (DC) has already been returned for this surface. Only one DC can be retrieved for each surface."; - case DDERR_DEVICEDOESNTOWNSURFACE: return "Surfaces created by one DirectDraw device cannot be used directly by another DirectDraw device."; - case DDERR_DIRECTDRAWALREADYCREATED: return "A DirectDraw object representing this driver has already been created for this process."; - case DDERR_EXCEPTION: return "An exception was encountered while performing the requested operation."; - case DDERR_EXCLUSIVEMODEALREADYSET: return "An attempt was made to set the cooperative level when it was already set to exclusive."; - case DDERR_EXPIRED: return "The data has expired and is therefore no longer valid."; - case DDERR_GENERIC: return "There is an undefined error condition."; - case DDERR_HEIGHTALIGN: return "The height of the provided rectangle is not a multiple of the required alignment."; - case DDERR_HWNDALREADYSET: return "The DirectDraw cooperative-level window handle has already been set. It cannot be reset while the process has surfaces or palettes created."; - case DDERR_HWNDSUBCLASSED: return "DirectDraw is prevented from restoring state because the DirectDraw cooperative-level window handle has been subclassed."; - case DDERR_IMPLICITLYCREATED: return "The surface cannot be restored because it is an implicitly created surface."; - case DDERR_INCOMPATIBLEPRIMARY: return "The primary surface creation request does not match the existing primary surface."; - case DDERR_INVALIDCAPS: return "One or more of the capability bits passed to the callback function are incorrect."; - case DDERR_INVALIDCLIPLIST: return "DirectDraw does not support the provided clip list."; - case DDERR_INVALIDDIRECTDRAWGUID: return "The globally unique identifier (GUID) passed to the DirectDrawCreate function is not a valid DirectDraw driver identifier."; - case DDERR_INVALIDMODE: return "DirectDraw does not support the requested mode."; - case DDERR_INVALIDOBJECT: return "DirectDraw received a pointer that was an invalid DirectDraw object."; - case DDERR_INVALIDPARAMS: return "One or more of the parameters passed to the method are incorrect."; - case DDERR_INVALIDPIXELFORMAT: return "The pixel format was invalid as specified."; - case DDERR_INVALIDPOSITION: return "The position of the overlay on the destination is no longer legal."; - case DDERR_INVALIDRECT: return "The provided rectangle was invalid."; - case DDERR_INVALIDSTREAM: return "The specified stream contains invalid data."; - case DDERR_INVALIDSURFACETYPE: return "The surface was of the wrong type."; - case DDERR_LOCKEDSURFACES: return "One or more surfaces are locked, causing the failure of the requested operation."; - case DDERR_MOREDATA: return "There is more data available than the specified buffer size can hold."; - case DDERR_NEWMODE: return "New for DirectX 7.0. When IDirectDraw7::StartModeTest is called with the DDSMT_ISTESTREQUIRED flag, it may return this value to denote that some or all of the resolutions can and should be tested. IDirectDraw7::EvaluateMode returns this value to indicate that the test has switched to a new display mode."; - case DDERR_NO3D: return "No 3-D hardware or emulation is present."; - case DDERR_NOALPHAHW: return "No alpha-acceleration hardware is present or available, causing the failure of the requested operation."; - case DDERR_NOBLTHW: return "No blitter hardware is present."; - case DDERR_NOCLIPLIST: return "No clip list is available."; - case DDERR_NOCLIPPERATTACHED: return "No DirectDrawClipper object is attached to the surface object."; - case DDERR_NOCOLORCONVHW: return "No color-conversion hardware is present or available."; - case DDERR_NOCOLORKEY: return "The surface does not currently have a color key."; - case DDERR_NOCOLORKEYHW: return "There is no hardware support for the destination color key."; - case DDERR_NOCOOPERATIVELEVELSET: return "A create function was called without the IDirectDraw7::SetCooperativeLevel method."; - case DDERR_NODC: return "No device context (DC) has ever been created for this surface."; - case DDERR_NODDROPSHW: return "No DirectDraw raster-operation (ROP) hardware is available."; - case DDERR_NODIRECTDRAWHW: return "Hardware-only DirectDraw object creation is not possible; the driver does not support any hardware."; - case DDERR_NODIRECTDRAWSUPPORT: return "DirectDraw support is not possible with the current display driver."; - case DDERR_NODRIVERSUPPORT: return "New for DirectX 7.0. Testing cannot proceed because the display adapter driver does not enumerate refresh rates."; - case DDERR_NOEMULATION: return "Software emulation is not available."; - case DDERR_NOEXCLUSIVEMODE: return "The operation requires the application to have exclusive mode, but the application does not have exclusive mode."; - case DDERR_NOFLIPHW: return "Flipping visible surfaces is not supported."; - case DDERR_NOFOCUSWINDOW: return "An attempt was made to create or set a device window without first setting the focus window."; - case DDERR_NOGDI: return "No GDI is present."; - case DDERR_NOHWND: return "Clipper notification requires a window handle, or no window handle has been previously set as the cooperative level window handle."; - case DDERR_NOMIPMAPHW: return "No mipmap-capable texture mapping hardware is present or available."; - case DDERR_NOMIRRORHW: return "No mirroring hardware is present or available."; - case DDERR_NOMONITORINFORMATION: return "New for DirectX 7.0. Testing cannot proceed because the monitor has no associated EDID data."; - case DDERR_NONONLOCALVIDMEM: return "An attempt was made to allocate nonlocal video memory from a device that does not support nonlocal video memory."; - case DDERR_NOOPTIMIZEHW: return "The device does not support optimized surfaces."; - case DDERR_NOOVERLAYDEST: return "The IDirectDrawSurface7::GetOverlayPosition method is called on an overlay that the IDirectDrawSurface7::UpdateOverlay method has not been called on to establish as a destination."; - case DDERR_NOOVERLAYHW: return "No overlay hardware is present or available."; - case DDERR_NOPALETTEATTACHED: return "No palette object is attached to this surface."; - case DDERR_NOPALETTEHW: return "There is no hardware support for 16- or 256-color palettes."; - case DDERR_NORASTEROPHW: return "No appropriate raster-operation hardware is present or available."; - case DDERR_NOROTATIONHW: return "No rotation hardware is present or available."; - case DDERR_NOSTEREOHARDWARE: return "There is no stereo hardware present or available."; - case DDERR_NOSTRETCHHW: return "There is no hardware support for stretching."; - case DDERR_NOSURFACELEFT: return "There is no hardware present that supports stereo surfaces."; - case DDERR_NOT4BITCOLOR: return "The DirectDrawSurface object is not using a 4-bit color palette, and the requested operation requires a 4-bit color palette."; - case DDERR_NOT4BITCOLORINDEX: return "The DirectDrawSurface object is not using a 4-bit color index palette, and the requested operation requires a 4-bit color index palette."; - case DDERR_NOT8BITCOLOR: return "The DirectDrawSurface object is not using an 8-bit color palette, and the requested operation requires an 8-bit color palette."; - case DDERR_NOTAOVERLAYSURFACE: return "An overlay component is called for a nonoverlay surface."; - case DDERR_NOTEXTUREHW: return "The operation cannot be carried out because no texture-mapping hardware is present or available."; - case DDERR_NOTFLIPPABLE: return "An attempt was made to flip a surface that cannot be flipped."; - case DDERR_NOTFOUND: return "The requested item was not found."; - case DDERR_NOTINITIALIZED: return "An attempt was made to call an interface method of a DirectDraw object created by CoCreateInstance before the object was initialized."; - case DDERR_NOTLOADED: return "The surface is an optimized surface, but it has not yet been allocated any memory."; - case DDERR_NOTLOCKED: return "An attempt was made to unlock a surface that was not locked."; - case DDERR_NOTPAGELOCKED: return "An attempt was made to page-unlock a surface with no outstanding page locks."; - case DDERR_NOTPALETTIZED: return "The surface being used is not a palette-based surface."; - case DDERR_NOVSYNCHW: return "There is no hardware support for vertical blanksynchronized operations."; - case DDERR_NOZBUFFERHW: return "The operation to create a z-buffer in display memory or to perform a blit, using a z-buffer cannot be carried out because there is no hardware support for z-buffers."; - case DDERR_NOZOVERLAYHW: return "The overlay surfaces cannot be z-layered, based on the z-order because the hardware does not support z-ordering of overlays."; - case DDERR_OUTOFCAPS: return "The hardware needed for the requested operation has already been allocated."; - case DDERR_OUTOFMEMORY: return "DirectDraw does not have enough memory to perform the operation."; - case DDERR_OUTOFVIDEOMEMORY: return "DirectDraw does not have enough display memory to perform the operation."; - case DDERR_OVERLAPPINGRECTS: return "The source and destination rectangles are on the same surface and overlap each other."; - case DDERR_OVERLAYCANTCLIP: return "The hardware does not support clipped overlays."; - case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: return "An attempt was made to have more than one color key active on an overlay."; - case DDERR_OVERLAYNOTVISIBLE: return "The IDirectDrawSurface7::GetOverlayPosition method was called on a hidden overlay."; - case DDERR_PALETTEBUSY: return "Access to this palette is refused because the palette is locked by another thread."; - case DDERR_PRIMARYSURFACEALREADYEXISTS: return "This process has already created a primary surface."; - case DDERR_REGIONTOOSMALL: return "The region passed to the IDirectDrawClipper::GetClipList method is too small."; - case DDERR_SURFACEALREADYATTACHED: return "An attempt was made to attach a surface to another surface to which it is already attached."; - case DDERR_SURFACEALREADYDEPENDENT: return "An attempt was made to make a surface a dependency of another surface on which it is already dependent."; - case DDERR_SURFACEBUSY: return "Access to the surface is refused because the surface is locked by another thread."; - case DDERR_SURFACEISOBSCURED: return "Access to the surface is refused because the surface is obscured."; - case DDERR_SURFACELOST: return "Access to the surface is refused because the surface memory is gone. Call the IDirectDrawSurface7::Restore method on this surface to restore the memory associated with it."; - case DDERR_SURFACENOTATTACHED: return "The requested surface is not attached."; - case DDERR_TESTFINISHED: return "New for DirectX 7.0. When returned by the IDirectDraw7::StartModeTest method, this value means that no test could be initiated because all the resolutions chosen for testing already have refresh rate information in the registry. When returned by IDirectDraw7::EvaluateMode, the value means that DirectDraw has completed a refresh rate test."; - case DDERR_TOOBIGHEIGHT: return "The height requested by DirectDraw is too large."; - case DDERR_TOOBIGSIZE: return "The size requested by DirectDraw is too large. However, the individual height and width are valid sizes."; - case DDERR_TOOBIGWIDTH: return "The width requested by DirectDraw is too large."; - case DDERR_UNSUPPORTED: return "The operation is not supported."; - case DDERR_UNSUPPORTEDFORMAT: return "The pixel format requested is not supported by DirectDraw."; - case DDERR_UNSUPPORTEDMASK: return "The bitmask in the pixel format requested is not supported by DirectDraw."; - case DDERR_UNSUPPORTEDMODE: return "The display is currently in an unsupported mode."; - case DDERR_VERTICALBLANKINPROGRESS: return "A vertical blank is in progress."; - case DDERR_VIDEONOTACTIVE: return "The video port is not active."; - case DDERR_WASSTILLDRAWING: return "The previous blit operation that is transferring information to or from this surface is incomplete."; - case DDERR_WRONGMODE: return "This surface cannot be restored because it was created in a different mode."; - case DDERR_XALIGN: return "The provided rectangle was not horizontally aligned on a required boundary."; - } - - return nullptr; -} - - -const char *D3DErrorString(HRESULT hResult) -{ - static char buffer[1024]; - buffer[0] = 0; // Reset static buffer! - - const char* errorCodeString = DXGetErrorString(hResult); - if (errorCodeString) - { - strcat(buffer, errorCodeString); - strcat(buffer, ": "); - } - - const char* errorDescription = CxbxGetErrorDescription(hResult); - if (errorDescription) - strcat(buffer, errorDescription); - else - strcat(buffer, "Unknown D3D error."); - - return buffer; -} - -VOID CxbxInitWindow(bool bFullInit) -{ - g_EmuShared->GetVideoSettings(&g_XBVideo); - - if(g_XBVideo.bFullScreen) - CxbxKrnl_hEmuParent = NULL; - - // create timing thread - if (bFullInit) - { - DWORD dwThreadId; - - HANDLE hThread = CreateThread(nullptr, 0, EmuUpdateTickCount, nullptr, 0, &dwThreadId); - // We set the priority of this thread a bit higher, to assure reliable timing : - SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); - - CxbxKrnlRegisterThread(hThread); - } - -/* TODO : Port this Dxbx code : - // create vblank handling thread - { - dwThreadId = 0; - {hThread :=} CreateThread(nullptr, 0, EmuThreadHandleVBlank, nullptr, 0, &dwThreadId); - } -*/ - // create window message processing thread - { - DWORD dwThreadId; - - g_bRenderWindowActive = false; - - HANDLE hRenderWindowThread = CreateThread(nullptr, 0, EmuRenderWindow, nullptr, 0, &dwThreadId); - - if (hRenderWindowThread == NULL) { - char szBuffer[1024] = { 0 }; - sprintf(szBuffer, "Creating EmuRenderWindowThread Failed: %08X", GetLastError()); - PopupFatal(nullptr, szBuffer); - EmuShared::Cleanup(); - ExitProcess(0); - } - - while(!g_bRenderWindowActive) - Sleep(0); - - Sleep(0); - } - - SetFocus(g_hEmuWindow); -} - -void DrawUEM(HWND hWnd) -{ - // Draw the universal error message (UEM) - // See https://xboxdevwiki.net/Fatal_Error - // Only call this from WM_PAINT message! - - PAINTSTRUCT ps; - - BeginPaint(hWnd, &ps); - - HDC hDC = GetDC(hWnd); - HDC hMemDC = CreateCompatibleDC(hDC); - HBITMAP hUEMBmp = CreateCompatibleBitmap(hDC, 640, 480); - HBITMAP hOriUEMBmp = (HBITMAP)SelectObject(hMemDC, hUEMBmp); - - - int nHeight = -MulDiv(8, GetDeviceCaps(hMemDC, LOGPIXELSY), 72); - - HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_ROMAN, "Verdana"); - - HGDIOBJ tmpObj = SelectObject(hMemDC, hFont); - - SetBkColor(hMemDC, RGB(0, 0, 0)); - - SetTextColor(hMemDC, RGB(0, 204, 0)); - - std::wstring utf16str = utf8_to_utf16(uem_str.c_str()); - - // Unfortunately, DrawTextW doesn't support vertical alignemnt, so we have to do the calculation - // ourselves. See here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/abd89aae-16a0-41c6-8db6-b119ea90b42a/win32-drawtext-how-center-in-vertical-with-new-lines-and-tabs?forum=vclanguage - - RECT rect = { 0, 0, 640, 480 }; - RECT textrect = { 0, 0, 640, 480 }; - DrawTextW(hMemDC, utf16str.c_str(), utf16str.length(), &textrect, DT_CALCRECT); - rect.top = (rect.bottom - textrect.bottom) / 2; - DrawTextW(hMemDC, utf16str.c_str(), utf16str.length(), &rect, DT_CENTER); - - - // Draw the Xbox error code - - SetTextColor(hMemDC, RGB(255, 255, 255)); - std::string err_str(std::to_string(g_CxbxFatalErrorCode)); - rect.left = 20; - DrawText(hMemDC, err_str.c_str(), err_str.length(), &rect, DT_LEFT); - - GetClientRect(hWnd, &rect); - SetStretchBltMode(hDC, COLORONCOLOR); - StretchBlt(hDC, rect.left, rect.top, rect.right, rect.bottom, hMemDC, 0, 0, 640, 480, SRCCOPY); - - SelectObject(hMemDC, hOriUEMBmp); - SelectObject(hDC, tmpObj); - - DeleteObject(hUEMBmp); - DeleteObject(hFont); - DeleteObject(hMemDC); - - if (hDC != NULL) - ReleaseDC(hWnd, hDC); - - EndPaint(hWnd, &ps); -} - -inline DWORD GetXboxCommonResourceType(const XTL::DWORD XboxResource_Common) -{ - DWORD dwCommonType = XboxResource_Common & X_D3DCOMMON_TYPE_MASK; - return dwCommonType; -} - -inline DWORD GetXboxCommonResourceType(const XTL::X_D3DResource* pXboxResource) -{ - // Don't pass in unassigned Xbox resources - assert(pXboxResource != xbnullptr); - - return GetXboxCommonResourceType(pXboxResource->Common); -} - -XTL::X_D3DFORMAT GetXboxPixelContainerFormat(const XTL::DWORD XboxPixelContainer_Format) -{ - XTL::X_D3DFORMAT d3d_format = (XTL::X_D3DFORMAT)((XboxPixelContainer_Format & X_D3DFORMAT_FORMAT_MASK) >> X_D3DFORMAT_FORMAT_SHIFT); - return d3d_format; -} - -XTL::X_D3DFORMAT GetXboxPixelContainerFormat(const XTL::X_D3DPixelContainer *pXboxPixelContainer) -{ - // Don't pass in unassigned Xbox pixel container - assert(pXboxPixelContainer != xbnullptr); - - return GetXboxPixelContainerFormat(pXboxPixelContainer->Format); -} - -inline int GetXboxPixelContainerDimensionCount(const XTL::X_D3DPixelContainer *pXboxPixelContainer) -{ - // Don't pass in unassigned Xbox pixel container - assert(pXboxPixelContainer != xbnullptr); - - return (XTL::X_D3DFORMAT)((pXboxPixelContainer->Format & X_D3DFORMAT_DIMENSION_MASK) >> X_D3DFORMAT_DIMENSION_SHIFT); -} - -XTL::X_D3DRESOURCETYPE GetXboxD3DResourceType(const XTL::X_D3DResource *pXboxResource) -{ - DWORD Type = GetXboxCommonResourceType(pXboxResource); - switch (Type) - { - case X_D3DCOMMON_TYPE_VERTEXBUFFER: - return XTL::X_D3DRTYPE_VERTEXBUFFER; - case X_D3DCOMMON_TYPE_INDEXBUFFER: - return XTL::X_D3DRTYPE_INDEXBUFFER; - case X_D3DCOMMON_TYPE_PUSHBUFFER: - return XTL::X_D3DRTYPE_PUSHBUFFER; - case X_D3DCOMMON_TYPE_PALETTE: - return XTL::X_D3DRTYPE_PALETTE; - case X_D3DCOMMON_TYPE_TEXTURE: - { - DWORD Format = ((XTL::X_D3DPixelContainer *)pXboxResource)->Format; - if (Format & X_D3DFORMAT_CUBEMAP) - return XTL::X_D3DRTYPE_CUBETEXTURE; - - if (GetXboxPixelContainerDimensionCount((XTL::X_D3DPixelContainer *)pXboxResource) > 2) - return XTL::X_D3DRTYPE_VOLUMETEXTURE; - - return XTL::X_D3DRTYPE_TEXTURE; - } - case X_D3DCOMMON_TYPE_SURFACE: - { - if (GetXboxPixelContainerDimensionCount((XTL::X_D3DPixelContainer *)pXboxResource) > 2) - return XTL::X_D3DRTYPE_VOLUME; - - return XTL::X_D3DRTYPE_SURFACE; - } - case X_D3DCOMMON_TYPE_FIXUP: - return XTL::X_D3DRTYPE_FIXUP; - } - - return XTL::X_D3DRTYPE_NONE; -} - -// This can be used to determine if resource Data adddresses -// need the PHYSICAL_MAP_BASE bit set or cleared -inline bool IsResourceTypeGPUReadable(const DWORD ResourceType) -{ - switch (ResourceType) { - case X_D3DCOMMON_TYPE_VERTEXBUFFER: - return true; - case X_D3DCOMMON_TYPE_INDEXBUFFER: - /// assert(false); // Index buffers are not allowed to be registered - break; - case X_D3DCOMMON_TYPE_PUSHBUFFER: - return false; - case X_D3DCOMMON_TYPE_PALETTE: - return true; - case X_D3DCOMMON_TYPE_TEXTURE: - return true; - case X_D3DCOMMON_TYPE_SURFACE: - return true; - case X_D3DCOMMON_TYPE_FIXUP: - // assert(false); // Fixup's are not allowed to be registered - break; - default: - CxbxKrnlCleanup("Unhandled resource type"); - } - - return false; -} - -inline bool IsPaletizedTexture(const XTL::DWORD XboxPixelContainer_Format) -{ - return GetXboxPixelContainerFormat(XboxPixelContainer_Format) == XTL::X_D3DFMT_P8; -} - -#if 0 // unused -inline bool IsYuvSurfaceOrTexture(const XTL::X_D3DResource* pXboxResource) -{ - if (GetXboxPixelContainerFormat((XTL::X_D3DPixelContainer *)pXboxResource) == XTL::X_D3DFMT_YUY2) - return true; - - return false; -} -#endif - -#if 0 // unused -inline bool IsXboxResourceLocked(const XTL::X_D3DResource *pXboxResource) -{ - bool result = !!(pXboxResource->Common & X_D3DCOMMON_ISLOCKED); - return result; -} -#endif - -#if 0 // unused -inline bool IsXboxResourceD3DCreated(const XTL::X_D3DResource *pXboxResource) -{ - bool result = !!(pXboxResource->Common & X_D3DCOMMON_D3DCREATED); - return result; -} -#endif - -void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource) -{ - // Don't pass in unassigned Xbox resources - if (pXboxResource == xbnullptr) - return nullptr; - - xbaddr pData = pXboxResource->Data; - if (pData == xbnull) - return nullptr; - - DWORD dwCommonType = GetXboxCommonResourceType(pXboxResource); - if (IsResourceTypeGPUReadable(dwCommonType)) - pData |= PHYSICAL_MAP_BASE; - - return (uint8_t*)pData; -} - -typedef struct { - IDirect3DResource* pHostResource = nullptr; - XTL::X_D3DResource* pXboxResource = xbnullptr; - DWORD dwXboxResourceType = 0; - void* pXboxData = xbnullptr; - size_t szXboxDataSize = 0; - uint64_t hash = 0; - bool forceRehash = false; - std::chrono::time_point nextHashTime; - std::chrono::milliseconds hashLifeTime = 1ms; - std::chrono::time_point lastUpdate; -} resource_info_t; - -typedef std::unordered_map resource_cache_t; -resource_cache_t g_Cxbx_Cached_Direct3DResources; -resource_cache_t g_Cxbx_Cached_PaletizedTextures; - -bool IsResourceAPixelContainer(XTL::DWORD XboxResource_Common) -{ - DWORD Type = GetXboxCommonResourceType(XboxResource_Common); - switch (Type) - { - case X_D3DCOMMON_TYPE_TEXTURE: - case X_D3DCOMMON_TYPE_SURFACE: - return true; - } - - return false; -} - -bool IsResourceAPixelContainer(XTL::X_D3DResource* pXboxResource) -{ - // Don't pass in unassigned Xbox resources - assert(pXboxResource != xbnullptr); - - return IsResourceAPixelContainer(pXboxResource->Common); -} - -resource_cache_t& GetResourceCache(resource_key_t& key) -{ - return IsResourceAPixelContainer(key.Common) && IsPaletizedTexture(key.Format) - ? g_Cxbx_Cached_PaletizedTextures : g_Cxbx_Cached_Direct3DResources; -} - -resource_key_t GetHostResourceKey(XTL::X_D3DResource* pXboxResource, int iTextureStage = -1) -{ - resource_key_t key = {}; - if (pXboxResource != xbnullptr) { - // Initially, don't base the key on the address of the resource, but on it's uniquely identifying values - key.Data = pXboxResource->Data; - key.Common = pXboxResource->Common & CXBX_D3DCOMMON_IDENTIFYING_MASK; - if (IsResourceAPixelContainer(pXboxResource)) { - // Pixel containers have more values they must be identified by: - auto pPixelContainer = (XTL::X_D3DPixelContainer*)pXboxResource; - key.Format = pPixelContainer->Format; - key.Size = pPixelContainer->Size; - // For paletized textures, include the current palette hash as well - if (IsPaletizedTexture(pPixelContainer->Format)) { - if (iTextureStage < 0) { - // ForceResourceRehash (called by Lock[23]DSurface) could hit this (not knowing the texture-stage) - LOG_TEST_CASE("Unknown texture stage!"); - } else { - assert(iTextureStage < XTL::X_D3DTS_STAGECOUNT); - // Protect for when this gets hit before an actual palette is set - if (g_Xbox_Palette_Size[iTextureStage] > 0) { - // This caters for palette changes (only the active one will be used, - // any intermediate changes have no effect). Obsolete palette texture - // conversions will be pruned from g_Cxbx_Cached_PaletizedTextures - key.PaletteHash = ComputeHash(g_pXbox_Palette_Data[iTextureStage], g_Xbox_Palette_Size[iTextureStage]); - } - } - } - } else { - // For other resource types, do include their Xbox resource address (TODO : come up with something better) - key.ResourceAddr = (xbaddr)pXboxResource; - } - } - - return key; -} - -void FreeHostResource(resource_key_t key) -{ - // Release the host resource and remove it from the list - auto& ResourceCache = GetResourceCache(key); - auto hostResourceIterator = ResourceCache.find(key); - if (hostResourceIterator != ResourceCache.end()) { - if (hostResourceIterator->second.pHostResource) { - (hostResourceIterator->second.pHostResource)->Release(); - } - - ResourceCache.erase(hostResourceIterator); - } -} - -void ClearResourceCache(resource_cache_t& ResourceCache) -{ - for (auto& hostResourceIterator : ResourceCache) { - if (hostResourceIterator.second.pHostResource) { - (hostResourceIterator.second.pHostResource)->Release(); - } - } - - ResourceCache.clear(); -} - -void PrunePaletizedTexturesCache() -{ - // TODO : Implement a better cache eviction algorithm (like least-recently used, or just at-random) - // Poor mans cache eviction policy: just clear it once it overflows - if (g_Cxbx_Cached_PaletizedTextures.size() >= 1500) { - ClearResourceCache(g_Cxbx_Cached_PaletizedTextures); - } -} - -void ForceResourceRehash(XTL::X_D3DResource* pXboxResource) -{ - auto key = GetHostResourceKey(pXboxResource); // Note : iTextureStage is unknown here! - auto& ResourceCache = GetResourceCache(key); - auto it = ResourceCache.find(key); - if (it != ResourceCache.end() && it->second.pHostResource) { - it->second.forceRehash = true; - } -} - -IDirect3DResource *GetHostResource(XTL::X_D3DResource *pXboxResource, DWORD D3DUsage = 0, int iTextureStage = -1) -{ - if (pXboxResource == xbnullptr || pXboxResource->Data == xbnull) - return nullptr; - - EmuVerifyResourceIsRegistered(pXboxResource, D3DUsage, iTextureStage, /*dwSize=*/0); - - auto key = GetHostResourceKey(pXboxResource, iTextureStage); - auto& ResourceCache = GetResourceCache(key); - auto it = ResourceCache.find(key); - if (it == ResourceCache.end() || !it->second.pHostResource) { - EmuLog(LOG_LEVEL::WARNING, "GetHostResource: Resource not registered or does not have a host counterpart!"); - return nullptr; - } - - return it->second.pHostResource; -} - -// Forward declaration of CxbxGetPixelContainerMeasures to prevent -// polluting the diff too much by reshuffling functions around -VOID CxbxGetPixelContainerMeasures -( - XTL::X_D3DPixelContainer *pPixelContainer, - DWORD dwMipMapLevel, - UINT *pWidth, - UINT *pHeight, - UINT *pDepth, - UINT *pRowPitch, - UINT *pSlicePitch -); - -size_t GetXboxResourceSize(XTL::X_D3DResource* pXboxResource) -{ - // TODO: Smart size calculation based around format of resource - if (IsResourceAPixelContainer(pXboxResource)) { - unsigned int Width, Height, Depth, RowPitch, SlicePitch; - // TODO : Accumulate all mipmap levels!!! - CxbxGetPixelContainerMeasures( - (XTL::X_D3DPixelContainer*)pXboxResource, - 0, // dwMipMapLevel - &Width, - &Height, - &Depth, - &RowPitch, - &SlicePitch - ); - - return SlicePitch * Depth; - } else { - // Fallback to querying the allocation size, if no other calculation was present - return xboxkrnl::MmQueryAllocationSize(GetDataFromXboxResource(pXboxResource)); - } - -} - -bool HostResourceRequiresUpdate(resource_key_t key, DWORD dwSize) -{ - auto& ResourceCache = GetResourceCache(key); - auto it = ResourceCache.find(key); - if (it == ResourceCache.end() || !it->second.pXboxResource) { - return false; - } - - // Currently, we only dynamically update Textures and Surfaces, so if our resource - // isn't of these types, do nothing - if (!IsResourceAPixelContainer(it->second.pXboxResource)) { - return false; - } - - // If the resource size got bigger, we need to re-create it - // if it got smaller, just hashing will suffice - if (dwSize > it->second.szXboxDataSize) { - return true; - } - - // If the resource type changed, we need to re-create it - if (it->second.dwXboxResourceType != GetXboxCommonResourceType(it->second.pXboxResource)) { - return true; - } - - bool modified = false; - - auto now = std::chrono::steady_clock::now(); - if (now > it->second.nextHashTime || it->second.forceRehash) { - uint64_t oldHash = it->second.hash; - it->second.hash = ComputeHash(it->second.pXboxData, it->second.szXboxDataSize); - - if (it->second.hash != oldHash) { - // The data changed, so reset the hash lifetime - it->second.hashLifeTime = 1ms; - it->second.lastUpdate = now; - modified = true; - } else if (it->second.lastUpdate + 1000ms < now) { - // The data did not change, so increase the hash lifetime - // TODO: choose a sensible upper limit - if (it->second.hashLifeTime < 1000ms) { - it->second.hashLifeTime += 10ms; - } - } - - it->second.forceRehash = false; - } - - // Update the next hash time based on the hash lifetime - it->second.nextHashTime = now + it->second.hashLifeTime; - - return modified; -} - -void SetHostResource(XTL::X_D3DResource* pXboxResource, IDirect3DResource* pHostResource, int iTextureStage = -1, DWORD dwSize = 0) -{ - auto key = GetHostResourceKey(pXboxResource, iTextureStage); - auto& ResourceCache = GetResourceCache(key); - auto& resourceInfo = ResourceCache[key]; // Implicitely inserts a new entry if not already existing - - if (resourceInfo.pHostResource) { - EmuLog(LOG_LEVEL::WARNING, "SetHostResource: Overwriting an existing host resource"); - } - - resourceInfo.pHostResource = pHostResource; - resourceInfo.pXboxResource = pXboxResource; - resourceInfo.dwXboxResourceType = GetXboxCommonResourceType(pXboxResource); - resourceInfo.pXboxData = GetDataFromXboxResource(pXboxResource); - resourceInfo.szXboxDataSize = dwSize > 0 ? dwSize : GetXboxResourceSize(pXboxResource); - resourceInfo.hash = ComputeHash(resourceInfo.pXboxData, resourceInfo.szXboxDataSize); - resourceInfo.hashLifeTime = 1ms; - resourceInfo.lastUpdate = std::chrono::steady_clock::now(); - resourceInfo.nextHashTime = resourceInfo.lastUpdate + resourceInfo.hashLifeTime; - resourceInfo.forceRehash = false; -} - -IDirect3DSurface *GetHostSurface(XTL::X_D3DResource *pXboxResource, DWORD D3DUsage = 0) -{ - if (pXboxResource == xbnullptr) - return nullptr; - - if (GetXboxCommonResourceType(pXboxResource) != X_D3DCOMMON_TYPE_SURFACE) // Allows breakpoint below - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_SURFACE); - - return (IDirect3DSurface*) GetHostResource(pXboxResource, D3DUsage); -} - -IDirect3DBaseTexture *GetHostBaseTexture(XTL::X_D3DResource *pXboxResource, DWORD D3DUsage = 0, int iTextureStage = 0) -{ - if (pXboxResource == xbnullptr) - return nullptr; - - if (GetXboxCommonResourceType(pXboxResource) != X_D3DCOMMON_TYPE_TEXTURE) { // Allows breakpoint below - // test-case : Burnout and Outrun 2006 hit this case (retrieving a surface instead of a texture) - // TODO : Surfaces can be set in the texture stages, instead of textures - see preparations in CxbxConvertXboxSurfaceToHostTexture - // We'll need to wrap the surface somehow before using it as a texture - LOG_TEST_CASE("GetHostBaseTexture called on a non-texture object"); - return nullptr; - // Note : We'd like to remove the above and do the following instead, - // but we can't yet since that seems to cause a "CreateCubeTexture Failed!" - // regression. The root cause for that seems to stem from the X_D3DRTYPE_SURFACE - // handling in CreateHostResource. - //assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); - } - - return (IDirect3DBaseTexture*)GetHostResource(pXboxResource, D3DUsage, iTextureStage); -} - -#if 0 // unused -IDirect3DTexture *GetHostTexture(XTL::X_D3DResource *pXboxResource, int iTextureStage = 0) -{ - if (pXboxResource == xbnullptr) - return nullptr; - - return (IDirect3DTexture *)GetHostBaseTexture(pXboxResource, 0, iTextureStage); - - // TODO : Check for 1 face (and 2 dimensions)? -} -#endif - -IDirect3DVolumeTexture *GetHostVolumeTexture(XTL::X_D3DResource *pXboxResource, int iTextureStage = 0) -{ - return (IDirect3DVolumeTexture *)GetHostBaseTexture(pXboxResource, 0, iTextureStage); - - // TODO : Check for 1 face (and 2 dimensions)? -} - -#if 0 // unused -IDirect3DIndexBuffer *GetHostIndexBuffer(XTL::X_D3DResource *pXboxResource) -{ - if (pXboxResource == xbnullptr) - return nullptr; - - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_INDEXBUFFER); - - return (IDirect3DIndexBuffer*)GetHostResource(pXboxResource); -} -#endif - -void SetHostSurface(XTL::X_D3DResource *pXboxResource, IDirect3DSurface *pHostSurface, int iTextureStage) -{ - assert(pXboxResource != xbnullptr); - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_SURFACE); - - SetHostResource(pXboxResource, (IDirect3DResource*)pHostSurface, iTextureStage); -} - -void SetHostTexture(XTL::X_D3DResource *pXboxResource, IDirect3DTexture *pHostTexture, int iTextureStage) -{ - assert(pXboxResource != xbnullptr); - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); - - SetHostResource(pXboxResource, (IDirect3DResource*)pHostTexture, iTextureStage); -} - -void SetHostCubeTexture(XTL::X_D3DResource *pXboxResource, IDirect3DCubeTexture *pHostCubeTexture, int iTextureStage) -{ - assert(pXboxResource != xbnullptr); - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); - - SetHostResource(pXboxResource, (IDirect3DResource*)pHostCubeTexture, iTextureStage); -} - -void SetHostVolumeTexture(XTL::X_D3DResource *pXboxResource, IDirect3DVolumeTexture *pHostVolumeTexture, int iTextureStage) -{ - assert(pXboxResource != xbnullptr); - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); - - SetHostResource(pXboxResource, (IDirect3DResource*)pHostVolumeTexture, iTextureStage); -} - -void SetHostVolume(XTL::X_D3DResource *pXboxResource, IDirect3DVolume *pHostVolume, int iTextureStage) -{ - assert(pXboxResource != xbnullptr); - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); - - SetHostResource(pXboxResource, (IDirect3DResource*)pHostVolume, iTextureStage); -} - -void SetHostIndexBuffer(XTL::X_D3DResource *pXboxResource, IDirect3DIndexBuffer *pHostIndexBuffer) -{ - assert(pXboxResource != xbnullptr); - assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_INDEXBUFFER); - - SetHostResource(pXboxResource, (IDirect3DResource*)pHostIndexBuffer); -} - -int XboxD3DPaletteSizeToBytes(const XTL::X_D3DPALETTESIZE Size) -{ -/* - static int lk[4] = - { - 256 * sizeof(D3DCOLOR), // D3DPALETTE_256 - 128 * sizeof(D3DCOLOR), // D3DPALETTE_128 - 64 * sizeof(D3DCOLOR), // D3DPALETTE_64 - 32 * sizeof(D3DCOLOR) // D3DPALETTE_32 - }; - - return lk[Size]; -*/ - return (256 * sizeof(D3DCOLOR)) >> (unsigned)Size; -} - -inline XTL::X_D3DPALETTESIZE GetXboxPaletteSize(const XTL::X_D3DPalette *pPalette) -{ - XTL::X_D3DPALETTESIZE PaletteSize = (XTL::X_D3DPALETTESIZE) - ((pPalette->Common & X_D3DPALETTE_COMMON_PALETTESIZE_MASK) >> X_D3DPALETTE_COMMON_PALETTESIZE_SHIFT); - - return PaletteSize; -} - -int GetD3DResourceRefCount(IDirect3DResource *EmuResource) -{ - if (EmuResource != nullptr) - { - // Get actual reference count by increasing it using AddRef, - // and relying on the return value of Release (which is - // probably more reliable than AddRef) - EmuResource->AddRef(); - return EmuResource->Release(); - } - - return 0; -} - -/* -XTL::X_D3DSurface *EmuNewD3DSurface() -{ - XTL::X_D3DSurface *result = (XTL::X_D3DSurface *)g_VMManager.AllocateZeroed(sizeof(XTL::X_D3DSurface)); - result->Common = X_D3DCOMMON_D3DCREATED | X_D3DCOMMON_TYPE_SURFACE | 1; // Set refcount to 1 - return result; -} -*/ - -VOID CxbxSetPixelContainerHeader -( - XTL::X_D3DPixelContainer* pPixelContainer, - DWORD Common, - UINT Width, - UINT Height, - UINT Levels, - XTL::X_D3DFORMAT Format, - UINT Dimensions, - UINT Pitch -) -{ - // Set X_D3DResource field(s) : - pPixelContainer->Common = Common; - // DON'T SET pPixelContainer->Data - // DON'T SET pPixelContainer->Lock - - // Are Width and Height both a power of two? - DWORD l2w; _BitScanReverse(&l2w, Width); // MSVC intrinsic; GCC has __builtin_clz - DWORD l2h; _BitScanReverse(&l2h, Height); - DWORD l2d = 0; // TODO : Set this via input argument - if (((1 << l2w) == Width) && ((1 << l2h) == Height)) { - Width = Height = Pitch = 1; // When setting Format, clear Size field - } - else { - l2w = l2h = l2d = 0; // When setting Size, clear D3DFORMAT_USIZE, VSIZE and PSIZE - } - - // Set X_D3DPixelContainer field(s) : - pPixelContainer->Format = 0 - | ((Dimensions << X_D3DFORMAT_DIMENSION_SHIFT) & X_D3DFORMAT_DIMENSION_MASK) - | (((DWORD)Format << X_D3DFORMAT_FORMAT_SHIFT) & X_D3DFORMAT_FORMAT_MASK) - | ((Levels << X_D3DFORMAT_MIPMAP_SHIFT) & X_D3DFORMAT_MIPMAP_MASK) - | ((l2w << X_D3DFORMAT_USIZE_SHIFT) & X_D3DFORMAT_USIZE_MASK) - | ((l2h << X_D3DFORMAT_VSIZE_SHIFT) & X_D3DFORMAT_VSIZE_MASK) - | ((l2d << X_D3DFORMAT_PSIZE_SHIFT) & X_D3DFORMAT_PSIZE_MASK) - ; - pPixelContainer->Size = 0 - | (((Width - 1) /*X_D3DSIZE_WIDTH_SHIFT*/) & X_D3DSIZE_WIDTH_MASK) - | (((Height - 1) << X_D3DSIZE_HEIGHT_SHIFT) & X_D3DSIZE_HEIGHT_MASK) - | (((Pitch - 1) << X_D3DSIZE_PITCH_SHIFT) & X_D3DSIZE_PITCH_MASK) - ; -} - -unsigned int CxbxGetPixelContainerDepth -( - XTL::X_D3DPixelContainer *pPixelContainer -) -{ - if (pPixelContainer->Size == 0) { - DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT; - return 1 << l2d; - } - - return 1; -} - -unsigned int CxbxGetPixelContainerMipMapLevels -( - XTL::X_D3DPixelContainer *pPixelContainer -) -{ - if (pPixelContainer->Size == 0) { - return (pPixelContainer->Format & X_D3DFORMAT_MIPMAP_MASK) >> X_D3DFORMAT_MIPMAP_SHIFT; - } - - return 1; -} - -uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer *pPixelContainer) -{ - DWORD Size = pPixelContainer->Size; - uint32_t Result; - - if (Size != 0) { - Result = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1; - } - else { - DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT; - - Result = 1 << l2w; - } - - return Result; -} - -uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer *pPixelContainer) -{ - DWORD Size = pPixelContainer->Size; - uint32_t Result; - - if (Size != 0) { - Result = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1; - } - else { - DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT; - - Result = 1 << l2h; - } - - return Result; -} - -VOID CxbxGetPixelContainerMeasures -( - XTL::X_D3DPixelContainer *pPixelContainer, - // TODO : Add X_D3DCUBEMAP_FACES argument - DWORD dwMipMapLevel, // unused - TODO : Use - UINT *pWidth, - UINT *pHeight, - UINT *pDepth, - UINT *pRowPitch, - UINT *pSlicePitch -) -{ - DWORD Size = pPixelContainer->Size; - XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer); - - if (Size != 0) - { - *pDepth = 1; - *pWidth = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1; - *pHeight = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1; - *pRowPitch = (((Size & X_D3DSIZE_PITCH_MASK) >> X_D3DSIZE_PITCH_SHIFT) + 1) * X_D3DTEXTURE_PITCH_ALIGNMENT; - } - else - { - DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT; - DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT; - DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT; - DWORD dwBPP = EmuXBFormatBitsPerPixel(X_Format); - - *pDepth = 1 << l2d; - *pHeight = 1 << l2h; - *pWidth = 1 << l2w; - *pRowPitch = (*pWidth) * dwBPP / 8; - } - - *pSlicePitch = (*pRowPitch) * (*pHeight); - - if (EmuXBFormatIsCompressed(X_Format)) { - *pRowPitch *= 4; - } -} - -void GetSurfaceFaceAndLevelWithinTexture(XTL::X_D3DSurface* pSurface, XTL::X_D3DBaseTexture* pTexture, UINT& Level, D3DCUBEMAP_FACES& Face) -{ - auto pSurfaceData = (uintptr_t)GetDataFromXboxResource(pSurface); - auto pTextureData = (uintptr_t)GetDataFromXboxResource(pTexture); - - // Fast path: If the data pointers match, this must be the first surface within the texture - if (pSurfaceData == pTextureData) { - Level = 0; - Face = D3DCUBEMAP_FACE_POSITIVE_X; - return; - } - - int numLevels = CxbxGetPixelContainerMipMapLevels(pTexture); - int numFaces = pTexture->Format & X_D3DFORMAT_CUBEMAP ? 6 : 1; - - CxbxGetPixelContainerMipMapLevels(pTexture); - - // First, we need to fetch the dimensions of both the surface and the texture, for use within our calculations - UINT textureWidth, textureHeight, textureDepth, textureRowPitch, textureSlicePitch; - CxbxGetPixelContainerMeasures(pTexture, 0, &textureWidth, &textureHeight, &textureDepth, &textureRowPitch, &textureSlicePitch); - - UINT surfaceWidth, surfaceHeight, surfaceDepth, surfaceRowPitch, surfaceSlicePitch; - CxbxGetPixelContainerMeasures(pSurface, 0, &surfaceWidth, &surfaceHeight, &surfaceDepth, &surfaceRowPitch, &surfaceSlicePitch); - - // Iterate through all faces and levels, until we find a matching pointer - bool isCompressed = EmuXBFormatIsCompressed(GetXboxPixelContainerFormat(pTexture)); - int minSize = (isCompressed) ? 4 : 1; - int cubeFaceOffset = 0; int cubeFaceSize = 0; - auto pData = pTextureData; - - for (int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= numFaces; face++) { - int mipWidth = textureWidth; - int mipHeight = textureHeight; - int mipDepth = textureDepth; - int mipRowPitch = textureRowPitch; - int mipDataOffset = 0; - - for (int level = 0; level < numLevels; level++) { - if (pData == pSurfaceData) { - Level = level; - Face = (D3DCUBEMAP_FACES)face; - return; - } - - // Calculate size of this mipmap level - UINT dwMipSize = mipRowPitch * mipHeight; - if (isCompressed) { - dwMipSize /= 4; - } - - // If this is the first face, set the cube face size - if (face == D3DCUBEMAP_FACE_POSITIVE_X) { - cubeFaceSize = ROUND_UP(textureDepth * dwMipSize, X_D3DTEXTURE_CUBEFACE_ALIGNMENT); - } - - // Move to the next mip-map and calculate dimensions for the next iteration - mipDataOffset += dwMipSize; - - if (mipWidth > minSize) { - mipWidth /= 2; - mipRowPitch /= 2; - } - - if (mipHeight > minSize) { - mipHeight /= 2; - } - - if (mipDepth > 1) { - mipDepth /= 2; - } - } - - // Move to the next face - pData += cubeFaceSize; - } - - LOG_TEST_CASE("Could not find Surface within Texture, falling back to Level = 0, Face = D3DCUBEMAP_FACE_POSITIVE_X"); - Level = 0; - Face = D3DCUBEMAP_FACE_POSITIVE_X; -} - -// Wrapper function to allow calling without passing a face -void GetSurfaceFaceAndLevelWithinTexture(XTL::X_D3DSurface* pSurface, XTL::X_D3DBaseTexture* pBaseTexture, UINT& Level) -{ - D3DCUBEMAP_FACES face; - GetSurfaceFaceAndLevelWithinTexture(pSurface, pBaseTexture, Level, face); -} - -bool ConvertD3DTextureToARGBBuffer( - XTL::X_D3DFORMAT X_Format, - uint8_t *pSrc, - int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch, - uint8_t *pDst, int DstRowPitch, int DstSlicePitch, - unsigned int uiDepth = 1, - int iTextureStage = 0 -) -{ - const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format); - if (ConvertRowToARGB == nullptr) - return false; // Unhandled conversion - - uint8_t *unswizleBuffer = nullptr; - if (EmuXBFormatIsSwizzled(X_Format)) { - unswizleBuffer = (uint8_t*)malloc(SrcSlicePitch * uiDepth); // TODO : Reuse buffer when performance is important - // First we need to unswizzle the texture data - EmuUnswizzleBox( - pSrc, SrcWidth, SrcHeight, uiDepth, - EmuXBFormatBytesPerPixel(X_Format), - // Note : use src pitch on dest, because this is an intermediate step : - unswizleBuffer, SrcRowPitch, SrcSlicePitch - ); - // Convert colors from the unswizzled buffer - pSrc = unswizleBuffer; - } - - int AdditionalArgument; - if (X_Format == XTL::X_D3DFMT_P8) - AdditionalArgument = (int)g_pXbox_Palette_Data[iTextureStage]; - else - AdditionalArgument = DstRowPitch; - - if (EmuXBFormatIsCompressed(X_Format)) { - // All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines - SrcHeight = (SrcHeight + 3) / 4; - DstRowPitch *= 4; - } - - uint8_t *pSrcSlice = pSrc; - uint8_t *pDstSlice = pDst; - for (unsigned int z = 0; z < uiDepth; z++) { - uint8_t *pSrcRow = pSrcSlice; - uint8_t *pDstRow = pDstSlice; - for (int y = 0; y < SrcHeight; y++) { - *(int*)pDstRow = AdditionalArgument; // Dirty hack, to avoid an extra parameter to all conversion callbacks - ConvertRowToARGB(pSrcRow, pDstRow, SrcWidth); - pSrcRow += SrcRowPitch; - pDstRow += DstRowPitch; - } - - pSrcSlice += SrcSlicePitch; - pDstSlice += DstSlicePitch; - } - - if (unswizleBuffer) - free(unswizleBuffer); - - return true; -} - -// Called by WndMain::LoadGameLogo() to load game logo bitmap -uint8_t *ConvertD3DTextureToARGB( - XTL::X_D3DPixelContainer *pXboxPixelContainer, - uint8_t *pSrc, - int *pWidth, int *pHeight, - int TextureStage // default = 0 -) -{ - // Avoid allocating pDest when ConvertD3DTextureToARGBBuffer will fail anyway - XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pXboxPixelContainer); - const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format); - if (ConvertRowToARGB == nullptr) - return nullptr; // Unhandled conversion - - unsigned int SrcDepth, SrcRowPitch, SrcSlicePitch; - CxbxGetPixelContainerMeasures( - pXboxPixelContainer, - 0, // dwMipMapLevel - (UINT*)pWidth, - (UINT*)pHeight, - &SrcDepth, - &SrcRowPitch, - &SrcSlicePitch - ); - - // Now we know ConvertD3DTextureToARGBBuffer will do it's thing, allocate the resulting buffer - int DstDepth = 1; // for now TODO : Use SrcDepth when supporting volume textures - int DstRowPitch = (*pWidth) * sizeof(DWORD); // = sizeof ARGB pixel. TODO : Is this correct? - int DstSlicePitch = DstRowPitch * (*pHeight); // TODO : Is this correct? - int DstSize = DstSlicePitch * DstDepth; - uint8_t *pDst = (uint8_t *)malloc(DstSize); - - // And convert the source towards that buffer - /*ignore result*/ConvertD3DTextureToARGBBuffer( - X_Format, - pSrc, *pWidth, *pHeight, SrcRowPitch, SrcSlicePitch, - pDst, DstRowPitch, DstSlicePitch, - DstDepth, - TextureStage); - - // NOTE : Caller must take ownership! - return pDst; -} - -// Direct3D initialization (called before emulation begins) -VOID EmuD3DInit() -{ - // create the create device proxy thread - { - DWORD dwThreadId; - - CreateThread(nullptr, 0, EmuCreateDeviceProxy, nullptr, 0, &dwThreadId); - // Ported from Dxbx : - // If possible, assign this thread to another core than the one that runs Xbox1 code : - SetThreadAffinityMask(&dwThreadId, g_CPUOthers); - } - - // Initialise CreateDevice Proxy Data struct - { - g_EmuCDPD = {0}; - g_EmuCDPD.Adapter = g_XBVideo.adapter; - g_EmuCDPD.DeviceType = (g_XBVideo.direct3DDevice == 0) ? D3DDEVTYPE_HAL : D3DDEVTYPE_REF; - g_EmuCDPD.hFocusWindow = g_hEmuWindow; - } - - // create Direct3D8 and retrieve caps - { - // xbox Direct3DCreate8 returns "1" always, so we need our own ptr - g_pDirect3D = Direct3DCreate(D3D_SDK_VERSION); - if(g_pDirect3D == nullptr) - CxbxKrnlCleanup("Could not initialize Direct3D8!"); - - g_pDirect3D->GetDeviceCaps(g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, &g_D3DCaps); - - // Dump Host D3DCaps to log unconditionally - std::cout << "----------------------------------------\n"; - std::cout << "Host D3DCaps : " << g_D3DCaps << "\n"; - std::cout << "----------------------------------------\n"; - } - - // AMD compatibility workaround since VS model 3.0 doesn't work as intended with Direct3D9. - { - D3DADAPTER_IDENTIFIER9 adapter_info; - HRESULT status = g_pDirect3D->GetAdapterIdentifier(g_EmuCDPD.Adapter, 0, &adapter_info); - // 1002 and 1022 are vendor ids of AMD gpus - if (status == D3D_OK && (adapter_info.VendorId == 0x1002 || adapter_info.VendorId == 0x1022)) { - g_vs_model = vs_model_2_a; - EmuLogInit(LOG_LEVEL::WARNING, "AMD GPU Detected, falling back to shader model 2.X to prevent missing polygons"); - } - } -} - -// cleanup Direct3D -VOID EmuD3DCleanup() {} - -// enumeration procedure for locating display device GUIDs -static BOOL WINAPI EmuEnumDisplayDevices(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) -{ - static DWORD dwEnumCount = 0; - - if(dwEnumCount++ == g_EmuCDPD.Adapter + 1) - { - g_hMonitor = hm; - dwEnumCount = 0; - if(lpGUID != 0) - { - memcpy(&g_ddguid, lpGUID, sizeof(GUID)); - } - else - { - memset(&g_ddguid, 0, sizeof(GUID)); - } - - return FALSE; - } - - return TRUE; -} - -// window message processing thread -static DWORD WINAPI EmuRenderWindow(LPVOID lpVoid) -{ - CxbxSetThreadName("Cxbx Render Window"); - SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); - - // register window class - { - LOGBRUSH logBrush = {BS_SOLID, RGB(0,0,0)}; - - g_hBgBrush = CreateBrushIndirect(&logBrush); - - WNDCLASSEX wc = - { - sizeof(WNDCLASSEX), - CS_CLASSDC, - EmuMsgProc, - 0, 0, hActiveModule, // Was GetModuleHandle(nullptr), - 0, // TODO : LoadIcon(hmodule, ?) - LoadCursor(NULL, IDC_ARROW), - (HBRUSH)(g_hBgBrush), NULL, - "CxbxRender", - nullptr - }; - - RegisterClassEx(&wc); - } - - // create the window - { - // Peform selection if running in GUI or kernel mode first. - HWND hwndParent = (!CxbxKrnl_hEmuParent ? GetDesktopWindow() : CxbxKrnl_hEmuParent); - DWORD dwStyle = WS_POPUP; - RECT windowRect = { 0 }; - - // Obtain the selected resolution from GUI or full desktop screen in kernel mode. - if (!GetWindowRect(hwndParent, &windowRect)) { - // Fall back resolution if failed - windowRect = { 0, 0, 640, 480 }; - } - - // Then perform additional checks if not running in full screen. - if (!g_XBVideo.bFullScreen) { - - // If running as kernel mode, force use the xbox's default resolution. - if (!CxbxKrnl_hEmuParent) { - // Xbox default resolution (standalone window is resizable by the way) - windowRect.right = 640; - windowRect.bottom = 480; - dwStyle = WS_OVERLAPPEDWINDOW; - } - else { - dwStyle = WS_CHILD; - } - } - - g_hEmuWindow = CreateWindow - ( - "CxbxRender", "Cxbx-Reloaded", - dwStyle, - windowRect.left, - windowRect.top, - windowRect.right - windowRect.left, - windowRect.bottom - windowRect.top, - hwndParent, nullptr, hActiveModule, // Was GetModuleHandle(nullptr), - nullptr - ); - } - - ShowWindow(g_hEmuWindow, ((CxbxKrnl_hEmuParent == 0) || g_XBVideo.bFullScreen) ? SW_SHOWDEFAULT : SW_SHOWMAXIMIZED); - UpdateWindow(g_hEmuWindow); - - if(!g_XBVideo.bFullScreen && (CxbxKrnl_hEmuParent != NULL)) - { - SetFocus(CxbxKrnl_hEmuParent); - } - - EmuLog(LOG_LEVEL::DEBUG, "Message-Pump thread is running."); - - SetFocus(g_hEmuWindow); - -#ifdef INCLUDE_DBG_CONSOLE - DbgConsole *dbgConsole = new DbgConsole(); -#endif - - // message processing loop - { - MSG msg; - - ZeroMemory(&msg, sizeof(msg)); - - bool lPrintfOn = g_bPrintfOn; - - g_bRenderWindowActive = true; - - while(msg.message != WM_QUIT) - { - if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - else - { - Sleep(0); - -#ifdef INCLUDE_DBG_CONSOLE - // if we've just switched back to display off, clear buffer & display prompt - if(!g_bPrintfOn && lPrintfOn) - { - dbgConsole->Reset(); - } -#endif - - lPrintfOn = g_bPrintfOn; - -#ifdef INCLUDE_DBG_CONSOLE - dbgConsole->Process(); -#endif - } - } - - g_bRenderWindowActive = false; - -#ifdef INCLUDE_DBG_CONSOLE - delete dbgConsole; -#endif - - CxbxKrnlCleanup(nullptr); - } - - return 0; -} - -// simple helper function -void ToggleFauxFullscreen(HWND hWnd) -{ - if(g_XBVideo.bFullScreen) - return; - - static LONG lRestore = 0, lRestoreEx = 0; - static RECT lRect = {0}; - - if(!g_bIsFauxFullscreen) - { - if(CxbxKrnl_hEmuParent != NULL) - { - SetParent(hWnd, NULL); - } - else - { - lRestore = GetWindowLong(hWnd, GWL_STYLE); - lRestoreEx = GetWindowLong(hWnd, GWL_EXSTYLE); - - GetWindowRect(hWnd, &lRect); - } - - SetWindowLong(hWnd, GWL_STYLE, WS_POPUP); - ShowWindow(hWnd, SW_MAXIMIZE); - SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - } - else - { - if(CxbxKrnl_hEmuParent != NULL) - { - SetParent(hWnd, CxbxKrnl_hEmuParent); - SetWindowLong(hWnd, GWL_STYLE, WS_CHILD); - ShowWindow(hWnd, SW_MAXIMIZE); - SetFocus(CxbxKrnl_hEmuParent); - } - else - { - SetWindowLong(hWnd, GWL_STYLE, lRestore); - SetWindowLong(hWnd, GWL_EXSTYLE, lRestoreEx); - ShowWindow(hWnd, SW_RESTORE); - SetWindowPos(hWnd, HWND_NOTOPMOST, lRect.left, lRect.top, lRect.right - lRect.left, lRect.bottom - lRect.top, 0); - SetFocus(hWnd); - } - } - - g_bIsFauxFullscreen = !g_bIsFauxFullscreen; -} - -// rendering window message procedure -static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - static bool bAutoPaused = false; - - switch(msg) - { - case WM_DESTROY: - { - DeleteObject(g_hBgBrush); - PostQuitMessage(0); - return D3D_OK; // = 0 - } - break; - - case WM_PAINT: - { - if (g_CxbxPrintUEM) - { - DrawUEM(hWnd); - } - } - break; - - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_SYNC_CONFIG_LOGGING: - { - // Sync run-time config log settings from GUI process. - log_sync_config(); - log_generate_active_filter_output(CXBXR_MODULE::CXBXR); - } - break; - - case ID_SYNC_CONFIG_INPUT: - { - SDL_Event UpdateInputEvent; - SDL_memset(&UpdateInputEvent, 0, sizeof(SDL_Event)); - UpdateInputEvent.type = Sdl::UpdateInputEvent_t; - UpdateInputEvent.user.data1 = new int(lParam); - SDL_PushEvent(&UpdateInputEvent); - } - break; - - default: - break; - } - } - break; - - case WM_SYSKEYDOWN: - { - if(wParam == VK_RETURN) - { - ToggleFauxFullscreen(hWnd); - } - else if(wParam == VK_F4) - { - PostMessage(hWnd, WM_CLOSE, 0, 0); - } - // NOTE: Windows does not send F10 key message to WM_KEYDOWN. - // Source: https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-syskeydown - else if(wParam == VK_F10) - { - ToggleFauxFullscreen(hWnd); - } - else - { - return DefWindowProc(hWnd, msg, wParam, lParam); - } - } - break; - - case WM_KEYDOWN: - { - /*! disable fullscreen if we are set to faux mode, and faux fullscreen is active */ - if(wParam == VK_ESCAPE) - { - if(g_XBVideo.bFullScreen) - { - SendMessage(hWnd, WM_CLOSE, 0, 0); - } - else if(g_bIsFauxFullscreen) - { - ToggleFauxFullscreen(hWnd); - } - } - else if (wParam == VK_F1) - { - VertexBufferConverter.PrintStats(); - } - else if (wParam == VK_F6) - { - // For some unknown reason, F6 isn't handled in WndMain::WndProc - // sometimes, so detect it and stop emulation from here too : - SendMessage(hWnd, WM_CLOSE, 0, 0); // See StopEmulation(); - } - else if(wParam == VK_F8) - { - g_bPrintfOn = !g_bPrintfOn; - g_EmuShared->SetIsKrnlLogEnabled(g_bPrintfOn); - LOG_THREAD_INIT; - std::cout << _logThreadPrefix << g_EnumModules2String[to_underlying(CXBXR_MODULE::CXBXR)] << "Enable log is " << g_bPrintfOn << std::endl; - ipc_send_gui_update(IPC_UPDATE_GUI::LOG_ENABLED, static_cast(g_bPrintfOn)); - } - else if (wParam == VK_F9) - { - // Toggle frame-limiting - g_bHack_UnlockFramerate = !g_bHack_UnlockFramerate; - } - else if(wParam == VK_F11) - { - if (g_iWireframe++ == 2) { - g_iWireframe = 0; - } - - XboxRenderStates.SetWireFrameMode(g_iWireframe); - } - else - { - return DefWindowProc(hWnd, msg, wParam, lParam); - } - } - break; - - case WM_SIZE: - { - switch(wParam) - { - case SIZE_RESTORED: - case SIZE_MAXIMIZED: - { - if(bAutoPaused) - { - bAutoPaused = false; - CxbxKrnlResume(); - } - } - break; - - case SIZE_MINIMIZED: - { - if(g_XBVideo.bFullScreen) - CxbxKrnlCleanup(nullptr); - - if(!g_bEmuSuspended) - { - bAutoPaused = true; - CxbxKrnlSuspend(); - } - } - break; - } - } - break; - - case WM_CLOSE: - DestroyWindow(hWnd); - CxbxKrnlShutDown(); - break; - - case WM_SETFOCUS: - { - if(CxbxKrnl_hEmuParent != NULL) - { - SetFocus(CxbxKrnl_hEmuParent); - } - } - break; - - case WM_SETCURSOR: - { - if(g_XBVideo.bFullScreen || g_bIsFauxFullscreen) - { - SetCursor(NULL); - return S_OK; // = Is not part of D3D8 handling. - } - - return DefWindowProc(hWnd, msg, wParam, lParam); - } - break; - - default: - return DefWindowProc(hWnd, msg, wParam, lParam); - } - - return S_OK; // = Is not part of D3D8 handling. -} - -std::chrono::time_point> GetNextVBlankTime() -{ - // TODO: Read display frequency from Xbox Display Adapter - // This is accessed by calling CMiniport::GetRefreshRate(); - // This reads from the structure located at CMinpPort::m_CurrentAvInfo - // This will require at least Direct3D_CreateDevice being unpatched - // otherwise, m_CurrentAvInfo will never be initialised! - // 20ms should be used in the case of 50hz - return std::chrono::steady_clock::now() + 16.6666666667ms; -} - - -// timing thread procedure -static DWORD WINAPI EmuUpdateTickCount(LPVOID) -{ - CxbxSetThreadName("Cxbx Timing Thread"); - - // since callbacks come from here - InitXboxThread(g_CPUOthers); // avoid Xbox1 core for lowest possible latency - - EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running."); - - // current vertical blank count - int curvb = 0; - - // Calculate Next VBlank time - auto nextVBlankTime = GetNextVBlankTime(); - - while(true) - { - SwitchToThread(); - - // If VBlank Interval has passed, trigger VBlank callback - // Note: This whole code block can be removed once NV2A interrupts are implemented - // And Both Swap and Present can be ran unpatched - // Once that is in place, MiniPort + Direct3D will handle this on it's own! - // We check for LLE flag as NV2A handles it's own VBLANK if LLE is enabled! - if (!(bLLE_GPU) && std::chrono::steady_clock::now() > nextVBlankTime) - { - nextVBlankTime = GetNextVBlankTime(); - - // Increment the VBlank Counter and Wake all threads there were waiting for the VBlank to occur - std::unique_lock lk(g_VBConditionMutex); - g_Xbox_VBlankData.VBlank++; - g_VBConditionVariable.notify_all(); - - // TODO: Fixme. This may not be right... - g_Xbox_SwapData.SwapVBlank = 1; - - if(g_pXbox_VerticalBlankCallback != xbnullptr) - { - - g_pXbox_VerticalBlankCallback(&g_Xbox_VBlankData); - - } - - g_Xbox_VBlankData.Swap = 0; - - // TODO: This can't be accurate... - g_Xbox_SwapData.TimeUntilSwapVBlank = 0; - - // TODO: Recalculate this for PAL version if necessary. - // Also, we should check the D3DPRESENT_INTERVAL value for accurracy. - // g_Xbox_SwapData.TimeBetweenSwapVBlanks = 1/60; - g_Xbox_SwapData.TimeBetweenSwapVBlanks = 0; - } - } -} - -void UpdateDepthStencilFlags(IDirect3DSurface *pDepthStencilSurface) -{ - g_bHasDepth = false; - g_bHasStencil = false; - if (pDepthStencilSurface != nullptr) { - D3DSURFACE_DESC Desc; - pDepthStencilSurface->GetDesc(&Desc); - - switch (Desc.Format) { - case D3DFMT_D16: - g_bHasDepth = true; - break; - case D3DFMT_D15S1: - g_bHasDepth = true; - g_bHasStencil = true; - break; - case D3DFMT_D24X8: - g_bHasDepth = true; - break; - case D3DFMT_D24S8: - g_bHasDepth = true; - g_bHasStencil = true; - break; - case D3DFMT_D24X4S4: - g_bHasDepth = true; - g_bHasStencil = true; - break; - case D3DFMT_D32: - g_bHasDepth = true; - break; - } - } -} -// thread dedicated to create devices -static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) -{ - LOG_FUNC(); - - CxbxSetThreadName("Cxbx CreateDevice Proxy"); - - EmuLog(LOG_LEVEL::DEBUG, "CreateDevice proxy thread is running."); - - while(true) - { - // if we have been signalled, create the device with cached parameters - if(g_EmuCDPD.bReady) - { - EmuLog(LOG_LEVEL::DEBUG, "CreateDevice proxy thread received request."); - - // only one device should be created at once - if (g_pD3DDevice != nullptr) { - EmuLog(LOG_LEVEL::DEBUG, "CreateDevice proxy thread releasing old Device."); - - g_pD3DDevice->EndScene(); - - ClearResourceCache(g_Cxbx_Cached_PaletizedTextures); - ClearResourceCache(g_Cxbx_Cached_Direct3DResources); - - // TODO: ensure all other resources are cleaned up too - - g_EmuCDPD.hRet = g_pD3DDevice->Release(); - g_pD3DDevice = nullptr; - - // cleanup overlay clipper - if (g_pDDClipper != nullptr) { - g_pDDClipper->Release(); - g_pDDClipper = nullptr; - } - - // cleanup directdraw surface - if (g_pDDSPrimary != nullptr) { - g_pDDSPrimary->Release(); - g_pDDSPrimary = nullptr; - } - - // cleanup directdraw - if (g_pDD7 != nullptr) { - g_pDD7->Release(); - g_pDD7 = nullptr; - } - } - - if (g_EmuCDPD.bCreate) { - // Apply render scale factor for high-resolution rendering - g_RenderScaleFactor = g_XBVideo.renderScaleFactor; - - // Setup the HostPresentationParameters - { - g_EmuCDPD.HostPresentationParameters = {}; - g_EmuCDPD.HostPresentationParameters.Windowed = !g_XBVideo.bFullScreen; - - // TODO: Investigate the best option for this - g_EmuCDPD.HostPresentationParameters.SwapEffect = D3DSWAPEFFECT_COPY; - - // Attempt to match backbuffer format, this is not *required*, but leads to faster blitting/swapping - g_EmuCDPD.HostPresentationParameters.BackBufferFormat = EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.BackBufferFormat); - - g_EmuCDPD.HostPresentationParameters.PresentationInterval = g_XBVideo.bVSync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; - g_Xbox_PresentationInterval_Default = g_EmuCDPD.XboxPresentationParameters.PresentationInterval; - - // We only want *one* backbuffer on the host, triple buffering, etc should be handled by our Present/Swap impl - g_EmuCDPD.HostPresentationParameters.BackBufferCount = 1; - - // We don't want multisampling on the host backbuffer, it should be applied to Xbox surfaces if required - g_EmuCDPD.HostPresentationParameters.MultiSampleType = D3DMULTISAMPLE_NONE; - g_EmuCDPD.HostPresentationParameters.MultiSampleQuality = 0; - - // We want a lockable backbuffer for swapping/blitting purposes - g_EmuCDPD.HostPresentationParameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; - - // retrieve resolution from configuration - char szBackBufferFormat[16] = {}; - const char* resolution = g_XBVideo.szVideoResolution; - if (4 != sscanf(resolution, "%u x %u %*dbit %s (%u hz)", - &g_EmuCDPD.HostPresentationParameters.BackBufferWidth, - &g_EmuCDPD.HostPresentationParameters.BackBufferHeight, - szBackBufferFormat, - &g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz)) { - EmuLog(LOG_LEVEL::DEBUG, "EmuCreateDeviceProxy: Couldn't parse resolution : %s. Using Xbox Default (%d, %d @ %uhz)", resolution, - g_EmuCDPD.XboxPresentationParameters.BackBufferWidth, g_EmuCDPD.XboxPresentationParameters.BackBufferHeight, - g_EmuCDPD.XboxPresentationParameters.FullScreen_RefreshRateInHz); - g_EmuCDPD.HostPresentationParameters.BackBufferWidth = g_EmuCDPD.XboxPresentationParameters.BackBufferWidth; - g_EmuCDPD.HostPresentationParameters.BackBufferHeight = g_EmuCDPD.XboxPresentationParameters.BackBufferHeight; - g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz = g_EmuCDPD.XboxPresentationParameters.FullScreen_RefreshRateInHz; - } - - if(g_EmuCDPD.HostPresentationParameters.Windowed) - { - D3DDISPLAYMODE D3DDisplayMode; - g_pDirect3D->GetAdapterDisplayMode(g_EmuCDPD.Adapter, &D3DDisplayMode); - - g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DDisplayMode.Format; - g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz = 0; - } - else - { - // In exclusive fullscreen mode, make *sure* to use the info that was in the resolution string - if (strcmp(szBackBufferFormat, "x1r5g5b5") == 0) - g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_X1R5G5B5; - else if (strcmp(szBackBufferFormat, "r5g6r5") == 0) - g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_R5G6B5; - else if (strcmp(szBackBufferFormat, "x8r8g8b8") == 0) - g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_X8R8G8B8; - else if (strcmp(szBackBufferFormat, "a8r8g8b8") == 0) - g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_A8R8G8B8; - } - } - - // detect vertex processing capabilities - if((g_D3DCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && g_EmuCDPD.DeviceType == D3DDEVTYPE_HAL) - { - EmuLog(LOG_LEVEL::DEBUG, "Using hardware vertex processing"); - - g_EmuCDPD.BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; - // Unused : g_dwVertexShaderUsage = 0; - } - else - { - EmuLog(LOG_LEVEL::DEBUG, "Using software vertex processing"); - - g_EmuCDPD.BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; - // Unused : g_dwVertexShaderUsage = D3DUSAGE_SOFTWAREPROCESSING; - } - - // Dxbx addition : Prevent Direct3D from changing the FPU Control word : - g_EmuCDPD.BehaviorFlags |= D3DCREATE_FPU_PRESERVE; - - // Direct3D8: (WARN) :Device that was created without D3DCREATE_MULTITHREADED is being used by a thread other than the creation thread. - g_EmuCDPD.BehaviorFlags |= D3DCREATE_MULTITHREADED; - - // We never want auto-depth stencil on the host, Xbox D3D will handle this for us - g_EmuCDPD.HostPresentationParameters.EnableAutoDepthStencil = FALSE; - - // redirect to windows Direct3D - g_EmuCDPD.hRet = g_pDirect3D->CreateDevice( - g_EmuCDPD.Adapter, - g_EmuCDPD.DeviceType, - g_EmuCDPD.hFocusWindow, - g_EmuCDPD.BehaviorFlags, - &g_EmuCDPD.HostPresentationParameters, - &g_pD3DDevice - ); - DEBUG_D3DRESULT(g_EmuCDPD.hRet, "IDirect3D::CreateDevice"); - - if(FAILED(g_EmuCDPD.hRet)) - CxbxKrnlCleanup("IDirect3D::CreateDevice failed"); - - // Which texture formats does this device support? - memset(g_bSupportsFormatSurface, false, sizeof(g_bSupportsFormatSurface)); - memset(g_bSupportsFormatSurfaceRenderTarget, false, sizeof(g_bSupportsFormatSurfaceRenderTarget)); - memset(g_bSupportsFormatSurfaceDepthStencil, false, sizeof(g_bSupportsFormatSurfaceDepthStencil)); - memset(g_bSupportsFormatTexture, false, sizeof(g_bSupportsFormatTexture)); - memset(g_bSupportsFormatTextureRenderTarget, false, sizeof(g_bSupportsFormatTextureRenderTarget)); - memset(g_bSupportsFormatTextureDepthStencil, false, sizeof(g_bSupportsFormatTextureDepthStencil)); - memset(g_bSupportsFormatVolumeTexture, false, sizeof(g_bSupportsFormatVolumeTexture)); - memset(g_bSupportsFormatCubeTexture, false, sizeof(g_bSupportsFormatCubeTexture)); - for (int X_Format = XTL::X_D3DFMT_L8; X_Format <= XTL::X_D3DFMT_LIN_R8G8B8A8; X_Format++) { - // Only process Xbox formats that are directly mappable to host - if (!EmuXBFormatRequiresConversionToARGB((XTL::X_D3DFORMAT)X_Format)) { - // Convert the Xbox format into host format (without warning, thanks to the above restriction) - D3DFORMAT PCFormat = EmuXB2PC_D3DFormat((XTL::X_D3DFORMAT)X_Format); - if (PCFormat != D3DFMT_UNKNOWN) { - // Index with Xbox D3DFormat, because host FourCC codes are too big to be used as indices - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, - D3DRTYPE_SURFACE, PCFormat)) - g_bSupportsFormatSurface[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_RENDERTARGET, - D3DRTYPE_SURFACE, PCFormat)) - g_bSupportsFormatSurfaceRenderTarget[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, - D3DRTYPE_SURFACE, PCFormat)) - g_bSupportsFormatSurfaceDepthStencil[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, - D3DRTYPE_TEXTURE, PCFormat)) - g_bSupportsFormatTexture[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_RENDERTARGET, - D3DRTYPE_TEXTURE, PCFormat)) - g_bSupportsFormatTextureRenderTarget[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, - D3DRTYPE_TEXTURE, PCFormat)) - g_bSupportsFormatTextureDepthStencil[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, - D3DRTYPE_VOLUMETEXTURE, PCFormat)) - g_bSupportsFormatVolumeTexture[X_Format] = true; - if (D3D_OK == g_pDirect3D->CheckDeviceFormat( - g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, - g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, - D3DRTYPE_CUBETEXTURE, PCFormat)) - g_bSupportsFormatCubeTexture[X_Format] = true; - } - } - } - - // default NULL guid - ZeroMemory(&g_ddguid, sizeof(GUID)); - - HRESULT hRet; - - // enumerate device guid for this monitor, for directdraw - hRet = DirectDrawEnumerateExA(EmuEnumDisplayDevices, nullptr, DDENUM_ATTACHEDSECONDARYDEVICES); - DEBUG_D3DRESULT(hRet, "DirectDrawEnumerateExA"); - - // create DirectDraw7 - { - if(FAILED(hRet)) { - hRet = DirectDrawCreateEx(nullptr, (void**)&g_pDD7, IID_IDirectDraw7, nullptr); - DEBUG_D3DRESULT(hRet, "DirectDrawCreateEx(NULL)"); - } else { - hRet = DirectDrawCreateEx(&g_ddguid, (void**)&g_pDD7, IID_IDirectDraw7, nullptr); - DEBUG_D3DRESULT(hRet, "DirectDrawCreateEx(&g_ddguid)"); - } - - if(FAILED(hRet)) - CxbxKrnlCleanup("Could not initialize DirectDraw7"); - - hRet = g_pDD7->GetCaps(&g_DriverCaps, nullptr); - DEBUG_D3DRESULT(hRet, "g_pDD7->GetCaps"); - - hRet = g_pDD7->SetCooperativeLevel(0, DDSCL_NORMAL); - DEBUG_D3DRESULT(hRet, "g_pDD7->SetCooperativeLevel"); - - if(FAILED(hRet)) - CxbxKrnlCleanup("Could not set cooperative level"); - } - - // Dump all supported DirectDraw FourCC format codes - { - DWORD dwCodes = 0; - DWORD *lpCodes = nullptr; - - g_pDD7->GetFourCCCodes(&dwCodes, lpCodes); - lpCodes = (DWORD*)malloc(dwCodes*sizeof(DWORD)); - g_pDD7->GetFourCCCodes(&dwCodes, lpCodes); - for(DWORD v=0;vCreateQuery(D3DQUERYTYPE_EVENT, nullptr))) { - // Is host GPU query creation enabled? - if (!g_bHack_DisableHostGPUQueries) { - // Create a D3D event query to handle "wait-for-idle" with - hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &g_pHostQueryWaitForIdle); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (wait for idle)"); - - // Create a D3D event query to handle "callback events" with - hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &g_pHostQueryCallbackEvent); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (callback event)"); - } - } else { - LOG_TEST_CASE("Can't CreateQuery(D3DQUERYTYPE_EVENT) on host!"); - } - - // Can host driver create occlusion queries? - g_bEnableHostQueryVisibilityTest = false; - if (SUCCEEDED(g_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, nullptr))) { - // Is host GPU query creation enabled? - if (!g_bHack_DisableHostGPUQueries) { - g_bEnableHostQueryVisibilityTest = true; - } else { - LOG_TEST_CASE("Disabled D3DQUERYTYPE_OCCLUSION on host!"); - } - } else { - LOG_TEST_CASE("Can't CreateQuery(D3DQUERYTYPE_OCCLUSION) on host!"); - } - - hRet = g_pD3DDevice->CreateVertexBuffer - ( - 1, 0, 0, D3DPOOL_MANAGED, - &g_pDummyBuffer - , nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateVertexBuffer"); - - for(int Streams = 0; Streams < 16; Streams++) - { - hRet = g_pD3DDevice->SetStreamSource(Streams, g_pDummyBuffer, - 0, // OffsetInBytes - 1); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource"); - } - - // initially, show a black screen - // Only clear depth buffer and stencil if present - // - // Avoids following DirectX Debug Runtime error report - // [424] Direct3D8: (ERROR) :Invalid flag D3DCLEAR_ZBUFFER: no zbuffer is associated with device. Clear failed. - // - hRet = g_pD3DDevice->Clear( - /*Count=*/0, - /*pRects=*/nullptr, - D3DCLEAR_TARGET | (g_bHasDepth ? D3DCLEAR_ZBUFFER : 0) | (g_bHasStencil ? D3DCLEAR_STENCIL : 0), - /*Color=*/0xFF000000, // TODO : Use constant for this - /*Z=*/g_bHasDepth ? 1.0f : 0.0f, - /*Stencil=*/0); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Clear"); - - hRet = g_pD3DDevice->BeginScene(); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->BeginScene"); - - hRet = g_pD3DDevice->EndScene(); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->EndScene"); - - hRet = g_pD3DDevice->Present(0, 0, 0, 0); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Present"); - - // begin scene - hRet = g_pD3DDevice->BeginScene(); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->BeginScene(2nd)"); - - // Set up cache - g_VertexShaderSource.ResetD3DDevice(g_pD3DDevice); - } - - // signal completion - g_EmuCDPD.bReady = false; - } - - Sleep(1); - } - - return 0; -} - -// check if a resource has been registered yet (if not, register it) -void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize); // Forward declartion to prevent restructure of code -static void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize) -{ - // Skip resources without data - if (pResource->Data == xbnull) - return; - - auto key = GetHostResourceKey(pResource, iTextureStage); - auto& ResourceCache = GetResourceCache(key); - auto it = ResourceCache.find(key); - if (it != ResourceCache.end()) { - if (D3DUsage == D3DUSAGE_RENDERTARGET && IsResourceAPixelContainer(pResource) && EmuXBFormatIsRenderTarget(GetXboxPixelContainerFormat((XTL::X_D3DPixelContainer*)pResource))) { - // Render targets have special behavior: We can't trash them on guest modification - // this fixes an issue where CubeMaps were broken because the surface Set in GetCubeMapSurface - // would be overwritten by the surface created in SetRenderTarget - // However, if a non-rendertarget surface is used here, we'll need to recreate it as a render target! - auto hostResource = it->second.pHostResource; - auto xboxSurface = (XTL::X_D3DSurface*)pResource; - auto xboxTexture = (XTL::X_D3DTexture*)pResource; - auto xboxResourceType = GetXboxD3DResourceType(pResource); - - // Determine if the associated host resource is a render target already, if so, do nothing - HRESULT hRet = STATUS_INVALID_PARAMETER; // Default to an error condition, so we can use D3D_OK to check for success - D3DSURFACE_DESC surfaceDesc; - if (xboxResourceType == XTL::X_D3DRTYPE_SURFACE) { - hRet = ((IDirect3DSurface*)hostResource)->GetDesc(&surfaceDesc); - } else if (xboxResourceType == XTL::X_D3DRTYPE_TEXTURE) { - hRet = ((IDirect3DTexture*)hostResource)->GetLevelDesc(0, &surfaceDesc); - } - - // Only continue checking if we were able to get the surface desc, if it failed, we fall-through - // to previous resource management behavior - if (SUCCEEDED(hRet)) { - // If this resource is already created as a render target on the host, simply return - if (surfaceDesc.Usage & D3DUSAGE_RENDERTARGET) { - return; - } - - // The host resource is not a render target, but the Xbox surface is - // We need to re-create it as a render target - switch (xboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: { - // Free the host surface - FreeHostResource(key); - - // Free the parent texture, if present - XTL::X_D3DTexture* pParentXboxTexture = (pResource) ? (XTL::X_D3DTexture*)xboxSurface->Parent : xbnullptr; - - if (pParentXboxTexture) { - // Re-create the texture with D3DUSAGE_RENDERTARGET, this will automatically create any child-surfaces - FreeHostResource(GetHostResourceKey(pParentXboxTexture, iTextureStage)); - CreateHostResource(pParentXboxTexture, D3DUsage, iTextureStage, dwSize); - } - - // Re-create the surface with D3DUSAGE_RENDERTARGET - CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); - } break; - case XTL::X_D3DRTYPE_TEXTURE: { - auto xboxTexture = (XTL::X_D3DTexture*)pResource; - - // Free the host texture - FreeHostResource(key); - - // And re-create the texture with D3DUSAGE_RENDERTARGET - CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); - } break; - default: - LOG_TEST_CASE("Unimplemented rendertarget type"); - } - - return; - } - } - - //check if the same key existed in the HostResource map already. if there is a old pXboxResource in the map with the same key but different resource address, it must be freed first. - - if (it->second.pXboxResource != pResource) { - //printf("EmuVerifyResourceIsRegistered passed in XboxResource collides HostResource map!! key : %llX , map pXboxResource : %08X , passed in pResource : %08X \n", key, it->second.pXboxResource, pResource); - } - else { - if (!HostResourceRequiresUpdate(key, dwSize)) { - return; - } - } - - FreeHostResource(key); - } else { - resource_info_t newResource; - ResourceCache[key] = newResource; - } - - CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); -} - -// TODO : Move to own file -constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) -{ - return (NrOfQuadVertices * VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) / VERTICES_PER_QUAD; -} - -// TODO : Move to own file (or argument of all users) -bool bUseClockWiseWindingOrder = true; // TODO : Should this be fetched from X_D3DRS_FRONTFACE (or X_D3DRS_CULLMODE)? - -// TODO : Move to own file -// This function convertes quad to triangle indices. -// When pXboxQuadIndexData is set, original quad indices are read from this buffer -// (this use-case is for when an indexed quad draw is to be emulated). -// When pXboxQuadIndexData is null, quad-emulating indices are generated -// (this use-case is for when a non-indexed quad draw is to be emulated). -// The number of indices to generate is specified through uNrOfTriangleIndices. -// Resulting triangle indices are written to pTriangleIndexData, which must -// be pre-allocated to fit the output data. -// (Note, this function is marked 'constexpr' to allow the compiler to optimize -// the case when pXboxQuadIndexData is null) -constexpr void CxbxConvertQuadListToTriangleListIndices( - INDEX16* pXboxQuadIndexData, - unsigned uNrOfTriangleIndices, - INDEX16* pTriangleIndexData) -{ - assert(uNrOfTriangleIndices > 0); - assert(pTriangleIndexData); - - unsigned i = 0; - unsigned j = 0; - while (i + (VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) <= uNrOfTriangleIndices) { - if (bUseClockWiseWindingOrder) { - // ABCD becomes ABC+CDA, so this is triangle 1 : - pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A - pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 1] : j + 1; // B - pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C - i += VERTICES_PER_TRIANGLE; - // And this is triangle 2 : - pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C - pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 3] : j + 3; // D - pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A - i += VERTICES_PER_TRIANGLE; - } else { - // ABCD becomes ADC+CBA, so this is triangle 1 : - pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A - pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 3] : j + 3; // D - pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C - i += VERTICES_PER_TRIANGLE; - // And this is triangle 2 : - pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C - pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 1] : j + 1; // B - pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A - i += VERTICES_PER_TRIANGLE; - } - - // Next quad, please : - j += VERTICES_PER_QUAD; - } -} - -// TODO : Move to own file -// Called from EMUPATCH(D3DDevice_DrawIndexedVerticesUP) when PrimitiveType == X_D3DPT_QUADLIST. -// This API receives the number of vertices to draw (VertexCount), the index data that references -// vertices and a single stream of vertex data. The number of vertices to draw indicates the number -// of indices that are going to be fetched. The vertex data is referenced up to the highest index -// number present in the index data. -// To emulate drawing indexed quads, g_pD3DDevice->DrawIndexedPrimitiveUP is called on host, -// whereby the quad indices are converted to triangle indices. This implies for every four -// quad indices, we have to generate (two times three is) six triangle indices. (Note, that -// vertex data undergoes it's own Xbox-to-host conversion, independent from these indices.) -INDEX16* CxbxCreateQuadListToTriangleListIndexData(INDEX16* pXboxQuadIndexData, unsigned QuadVertexCount) -{ - UINT NrOfTriangleIndices = QuadToTriangleVertexCount(QuadVertexCount); - INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(NrOfTriangleIndices * sizeof(INDEX16)); - CxbxConvertQuadListToTriangleListIndices(pXboxQuadIndexData, NrOfTriangleIndices, pQuadToTriangleIndexBuffer); - return pQuadToTriangleIndexBuffer; -} - -// TODO : Move to own file -void CxbxReleaseQuadListToTriangleListIndexData(void* pHostIndexData) -{ - free(pHostIndexData); -} - -class ConvertedIndexBuffer { -public: - uint64_t Hash = 0; - DWORD IndexCount = 0; - IDirect3DIndexBuffer* pHostIndexBuffer = nullptr; - INDEX16 LowIndex = 0; - INDEX16 HighIndex = 0; - - ~ConvertedIndexBuffer() - { - if (pHostIndexBuffer != nullptr) { - pHostIndexBuffer->Release(); - } - } -}; - -std::unordered_map g_IndexBufferCache; - -void CxbxRemoveIndexBuffer(PWORD pData) -{ - // HACK: Never Free -} - -IDirect3DIndexBuffer* CxbxCreateIndexBuffer(unsigned IndexCount) -{ - LOG_INIT; // Allows use of DEBUG_D3DRESULT - - IDirect3DIndexBuffer* Result = nullptr; - - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb147168(v=vs.85).aspx - // "Managing Resources (Direct3D 9)" - // suggests "for resources which change with high frequency" [...] - // "D3DPOOL_DEFAULT along with D3DUSAGE_DYNAMIC should be used." - const D3DPOOL D3DPool = D3DPOOL_DEFAULT; // Was D3DPOOL_MANAGED - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb172625(v=vs.85).aspx - // "Buffers created with D3DPOOL_DEFAULT that do not specify D3DUSAGE_WRITEONLY may suffer a severe performance penalty." - const DWORD D3DUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; // Was D3DUSAGE_WRITEONLY - - // Create a new native index buffer of the requested size : - UINT uiIndexBufferSize = IndexCount * sizeof(INDEX16); - HRESULT hRet = g_pD3DDevice->CreateIndexBuffer( - uiIndexBufferSize, // Size of the index buffer, in bytes. - D3DUsage, - /*Format=*/D3DFMT_INDEX16, - D3DPool, - &Result, - nullptr // pSharedHandle - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateIndexBuffer"); - if (FAILED(hRet)) { - assert(Result == nullptr); - } - - return Result; -} - -ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer -( - INDEX16* pXboxIndexData, - unsigned XboxIndexCount, - bool bConvertQuadListToTriangleList -) -{ - LOG_INIT; // Allows use of DEBUG_D3DRESULT - - uint32_t LookupKey = (uint32_t)pXboxIndexData; - unsigned RequiredIndexCount = XboxIndexCount; - - if (bConvertQuadListToTriangleList) { - LOG_TEST_CASE("bConvertQuadListToTriangleList"); - RequiredIndexCount = QuadToTriangleVertexCount(XboxIndexCount); - // For now, indicate the quad-to-triangles case using the otherwise - // (due to alignment) always-zero least significant bit : - LookupKey |= 1; - } - - // Poor-mans eviction policy : when exceeding treshold, clear entire cache : - if (g_IndexBufferCache.size() > 256) { - g_IndexBufferCache.clear(); // Note : ConvertedIndexBuffer destructor will release any assigned pHostIndexBuffer - } - - // Create a reference to the active buffer - ConvertedIndexBuffer& CacheEntry = g_IndexBufferCache[LookupKey]; - - // If the current index buffer size is too small, free it, so it'll get re-created - bool bNeedRepopulation = CacheEntry.IndexCount < RequiredIndexCount; - if (bNeedRepopulation) { - if (CacheEntry.pHostIndexBuffer != nullptr) - CacheEntry.pHostIndexBuffer->Release(); - - CacheEntry = {}; - } - - // If we need to create an index buffer, do so. - if (CacheEntry.pHostIndexBuffer == nullptr) { - CacheEntry.pHostIndexBuffer = CxbxCreateIndexBuffer(RequiredIndexCount); - if (!CacheEntry.pHostIndexBuffer) - CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: IndexBuffer Create Failed!"); - } - - // TODO : Speeds this up, perhaps by hashing less often, and/or by - // doing two hashes : a small subset regularly, all data less frequently. - uint64_t uiHash = ComputeHash(pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); - - // If the data needs updating, do so - bNeedRepopulation |= (uiHash != CacheEntry.Hash); - if (bNeedRepopulation) { - // Update the Index Count and the hash - CacheEntry.IndexCount = RequiredIndexCount; - CacheEntry.Hash = uiHash; - - // Update the host index buffer - INDEX16* pHostIndexBufferData = nullptr; - HRESULT hRet = CacheEntry.pHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); - DEBUG_D3DRESULT(hRet, "CacheEntry.pHostIndexBuffer->Lock"); - if (pHostIndexBufferData == nullptr) { - CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: Could not lock index buffer!"); - } - - // Determine highest and lowest index in use : - WalkIndexBuffer(CacheEntry.LowIndex, CacheEntry.HighIndex, pXboxIndexData, XboxIndexCount); - if (bConvertQuadListToTriangleList) { - // Note, that LowIndex and HighIndex won't change due to any quad-to-triangle conversion, - // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. - EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); - CxbxConvertQuadListToTriangleListIndices(pXboxIndexData, RequiredIndexCount, pHostIndexBufferData); - } else { - EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", XboxIndexCount); - memcpy(pHostIndexBufferData, pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); - } - - CacheEntry.pHostIndexBuffer->Unlock(); - } - - // Activate the new native index buffer : - HRESULT hRet = g_pD3DDevice->SetIndices(CacheEntry.pHostIndexBuffer); - // Note : Under Direct3D 9, the BaseVertexIndex argument is moved towards DrawIndexedPrimitive - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); - - if (FAILED(hRet)) - CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: SetIndices Failed!"); - - return CacheEntry; -} - -#define CXBX_D3DMULTISAMPLE_XSCALE(type) (((type) & XTL::X_D3DMULTISAMPLE_XSCALE_MASK) >> XTL::X_D3DMULTISAMPLE_XSCALE_SHIFT) -#define CXBX_D3DMULTISAMPLE_YSCALE(type) (((type) & XTL::X_D3DMULTISAMPLE_YSCALE_MASK) >> XTL::X_D3DMULTISAMPLE_YSCALE_SHIFT) - -void SetXboxMultiSampleType(XTL::X_D3DMULTISAMPLE_TYPE value) -{ - // Validate & correct input, to detect test cases and avoid trouble when using g_Xbox_MultiSampleType : - if (value == 0) { - // Correcting zero to X_D3DMULTISAMPLE_NONE - value = XTL::X_D3DMULTISAMPLE_NONE; - } - if (value & ~XTL::X_D3DMULTISAMPLE_KNOWN_MASK) { - LOG_TEST_CASE("Removing unknown X_D3DMULTISAMPLE bits"); - value &= XTL::X_D3DMULTISAMPLE_KNOWN_MASK; - } - if (CXBX_D3DMULTISAMPLE_XSCALE(value) == 0) { - LOG_TEST_CASE("Correcting XSCALE 0 to 1"); - value |= 1 << XTL::X_D3DMULTISAMPLE_XSCALE_SHIFT; - } - if (CXBX_D3DMULTISAMPLE_YSCALE(value) == 0) { - LOG_TEST_CASE("Correcting YSCALE 0 to 1"); - value |= 1 << XTL::X_D3DMULTISAMPLE_YSCALE_SHIFT; - } - if ((CXBX_D3DMULTISAMPLE_XSCALE(value) == 1) && (CXBX_D3DMULTISAMPLE_YSCALE(value) == 1)) { - if (value & XTL::X_D3DMULTISAMPLE_ALGO_MASK) { - LOG_TEST_CASE("Removing algorithm for 1 by 1 scale"); - value &= ~XTL::X_D3DMULTISAMPLE_ALGO_MASK; - } - if (value & XTL::X_D3DMULTISAMPLE_SAMPLING_MASK) { - LOG_TEST_CASE("Removing sampling for 1 by 1 scale"); - value &= ~XTL::X_D3DMULTISAMPLE_SAMPLING_MASK; - } - } - if (value == XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN) { - LOG_TEST_CASE("X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN"); // This used to use scale 1.5f - } - // Set the now-validated g_Xbox_MultiSampleType value : - g_Xbox_MultiSampleType = value; -} - -static inline float GetMultiSampleOffsetDelta() -{ - // TODO : What impact does X_D3DMULTISAMPLE_SAMPLING_SUPER have on offset? - return (g_Xbox_MultiSampleType & XTL::X_D3DMULTISAMPLE_SAMPLING_MULTI) ? 0.0f : 0.5f; -} - -static inline void GetMultiSampleOffset(float& xOffset, float& yOffset) -{ - xOffset = yOffset = GetMultiSampleOffsetDelta(); -} - -static inline void GetMultiSampleScale(float& xScale, float& yScale) -{ - xScale = (float)CXBX_D3DMULTISAMPLE_XSCALE(g_Xbox_MultiSampleType); - yScale = (float)CXBX_D3DMULTISAMPLE_YSCALE(g_Xbox_MultiSampleType); -} - -void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset) -{ - GetMultiSampleScale(xScale, yScale); - GetMultiSampleOffset(xOffset, yOffset); -} - -static void ApplyXboxMultiSampleOffset(float& x, float& y) -{ - float d = GetMultiSampleOffsetDelta(); - x += d; - y += d; -} - -static void ApplyXboxMultiSampleScale(float& x, float& y) -{ - float xs, ys; - GetMultiSampleScale(xs, ys); - x *= xs; - y *= ys; -} - -void ApplyXboxMultiSampleOffsetAndScale(float& x, float& y) -{ - ApplyXboxMultiSampleScale(x, y); - ApplyXboxMultiSampleOffset(x, y); -} - -void Direct3D_CreateDevice_Start -( - XTL::X_D3DPRESENT_PARAMETERS *pPresentationParameters -) -{ - if (!XboxRenderStates.Init()) { - CxbxKrnlCleanup("Failed to init XboxRenderStates"); - } - - if (!XboxTextureStates.Init(&XboxRenderStates)) { - CxbxKrnlCleanup("Failed to init XboxTextureStates"); - } - - SetXboxMultiSampleType(pPresentationParameters->MultiSampleType); - - // create default device *before* calling Xbox Direct3D_CreateDevice trampline - // to avoid hitting EMUPATCH'es that need a valid g_pD3DDevice - { - // Wait until proxy is done with an existing call (i highly doubt this situation will come up) - while (g_EmuCDPD.bReady) - Sleep(10); - - // Cache parameters - memcpy(&(g_EmuCDPD.XboxPresentationParameters), pPresentationParameters, sizeof(XTL::X_D3DPRESENT_PARAMETERS)); - - // Signal proxy thread (this will trigger EmuCreateDeviceProxy to call CreateDevice) - g_EmuCDPD.bCreate = true; - g_EmuCDPD.bReady = true; - - // Wait until host proxy is completed (otherwise, Xbox code could hit patches that need an assigned g_pD3DDevice) - while (g_EmuCDPD.bReady) - Sleep(10); - } -} - -void Direct3D_CreateDevice_End() -{ -#if 0 // Unused : - // Set g_Xbox_D3DDevice to point to the Xbox D3D Device - auto it = g_SymbolAddresses.find("D3DDEVICE"); - if (it != g_SymbolAddresses.end()) { - g_Xbox_D3DDevice = (DWORD*)it->second; - } -#endif - // If the Xbox version of CreateDevice didn't call SetRenderTarget, we must derive the default backbuffer ourselves - // This works because CreateDevice always sets the current render target to the Xbox Backbuffer - // In later XDKs, it does this inline rather than by calling D3DDevice_SetRenderTarget - // meaning our patch doesn't always get called in these cases. - // We fix the situation by calling the Xbox GetRenderTarget function, which immediately after CreateDevice - // WILL always return the Backbuffer! - // Test Case: Shin Megami Tensei: Nine - if (g_pXbox_BackBufferSurface == xbnullptr && g_pXbox_DefaultDepthStencilSurface == xbnullptr) { - // First, log the test case - LOG_TEST_CASE("Xbox CreateDevice did not call SetRenderTarget"); - } - - if (g_pXbox_BackBufferSurface == xbnullptr) { - if (XB_TRMP(D3DDevice_GetRenderTarget)) { - XB_TRMP(D3DDevice_GetRenderTarget)(&g_pXbox_BackBufferSurface); - } - else if (XB_TRMP(D3DDevice_GetRenderTarget2)) { - g_pXbox_BackBufferSurface = XB_TRMP(D3DDevice_GetRenderTarget2)(); - } - - // At this point, pRenderTarget should now point to a valid render target - // if it still doesn't, we cannot continue without crashing at draw time - if (g_pXbox_BackBufferSurface == xbnullptr) { - CxbxKrnlCleanup("Unable to determine default Xbox backbuffer"); - } - - // We must also call our SetRenderTarget patch to properly setup the host state - // Update only the Back buffer - XTL::EMUPATCH(D3DDevice_SetRenderTarget)(g_pXbox_BackBufferSurface, xbnullptr); - } - - // Now do the same, but for the default depth stencil surface - if (g_pXbox_DefaultDepthStencilSurface == xbnullptr) { - if (XB_TRMP(D3DDevice_GetDepthStencilSurface)) { - XB_TRMP(D3DDevice_GetDepthStencilSurface)(&g_pXbox_DefaultDepthStencilSurface); - } - else if (XB_TRMP(D3DDevice_GetDepthStencilSurface2)) { - g_pXbox_DefaultDepthStencilSurface = XB_TRMP(D3DDevice_GetDepthStencilSurface2)(); - } - - // At this point, g_pXbox_DefaultDepthStencilSurface should now point to a valid depth stencil - // If it doesn't, just log and carry on: Unlike RenderTarget, this situation is not fatal - if (g_pXbox_DefaultDepthStencilSurface == xbnullptr) { - LOG_TEST_CASE("Unable to determine default Xbox depth stencil"); - } else { - // Update only the depth stencil - XTL::EMUPATCH(D3DDevice_SetRenderTarget)(xbnullptr, g_pXbox_DefaultDepthStencilSurface); - } - } -} - -// LTCG specific Direct3D_CreateDevice function... -// This uses a custom calling with parameters passed in eax, ecx and the stack -// Test-case: Ninja Gaiden, Halo 2 -HRESULT WINAPI XTL::EMUPATCH(Direct3D_CreateDevice_4) -( - X_D3DPRESENT_PARAMETERS *pPresentationParameters -) -{ - DWORD BehaviorFlags; - IDirect3DDevice **ppReturnedDeviceInterface; - - __asm { - mov BehaviorFlags, eax - mov ppReturnedDeviceInterface, ecx - } - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pPresentationParameters) - LOG_FUNC_END; - - Direct3D_CreateDevice_Start(pPresentationParameters); - - HRESULT hRet = 0; - - // Only then call Xbox CreateDevice function - __asm { - mov eax, BehaviorFlags - mov ecx, ppReturnedDeviceInterface - push pPresentationParameters - call XB_TRMP(Direct3D_CreateDevice_4) - mov hRet, eax - } - - Direct3D_CreateDevice_End(); - - return hRet; -} - -// LTCG specific Direct3D_CreateDevice function... -// This uses a custom calling convention passed unknown parameters -// Test-case: Battle Engine Aquila -HRESULT WINAPI XTL::EMUPATCH(Direct3D_CreateDevice_16) -( - UINT Adapter, - D3DDEVTYPE DeviceType, - HWND hFocusWindow, - X_D3DPRESENT_PARAMETERS *pPresentationParameters -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Adapter) - LOG_FUNC_ARG(DeviceType) - LOG_FUNC_ARG(hFocusWindow) - LOG_FUNC_ARG(pPresentationParameters) - LOG_FUNC_END; - - Direct3D_CreateDevice_Start(pPresentationParameters); - - // Only then call Xbox CreateDevice function - HRESULT hRet = XB_TRMP(Direct3D_CreateDevice_16)(Adapter, DeviceType, hFocusWindow, pPresentationParameters); - - Direct3D_CreateDevice_End(); - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_SetIndices_4 -// LTCG specific D3DDevice_SetIndices function... -// This uses a custom calling convention where parameter is passed in EBX and Stack -// Test Case: Conker -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetIndices_4) -( - UINT BaseVertexIndex -) -{ - X_D3DIndexBuffer *pIndexData; - - __asm { - mov pIndexData, ebx - } - // Cache the base vertex index - g_Xbox_BaseVertexIndex = BaseVertexIndex; - - // Call LTCG-specific trampoline - __asm { - mov ebx, pIndexData - push BaseVertexIndex - call XB_TRMP(D3DDevice_SetIndices_4); - } -} - - -// ****************************************************************** -// * patch: D3DDevice_SetIndices -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetIndices) -( - X_D3DIndexBuffer *pIndexData, - UINT BaseVertexIndex -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pIndexData) - LOG_FUNC_ARG(BaseVertexIndex) - LOG_FUNC_END; - - // Cache the base vertex index then call the Xbox function - g_Xbox_BaseVertexIndex = BaseVertexIndex; - - XB_TRMP(D3DDevice_SetIndices)(pIndexData, BaseVertexIndex); -} - -// ****************************************************************** -// * patch: Direct3D_CreateDevice -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(Direct3D_CreateDevice) -( - UINT Adapter, - D3DDEVTYPE DeviceType, - HWND hFocusWindow, - DWORD BehaviorFlags, - X_D3DPRESENT_PARAMETERS *pPresentationParameters, - IDirect3DDevice **ppReturnedDeviceInterface -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Adapter) - LOG_FUNC_ARG(DeviceType) - LOG_FUNC_ARG(hFocusWindow) - LOG_FUNC_ARG(BehaviorFlags) // Xbox ignores BehaviorFlags - LOG_FUNC_ARG(pPresentationParameters) - LOG_FUNC_ARG(ppReturnedDeviceInterface) - LOG_FUNC_END; - - Direct3D_CreateDevice_Start(pPresentationParameters); - - // Only then call Xbox CreateDevice function - HRESULT hRet = XB_TRMP(Direct3D_CreateDevice)(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); - - Direct3D_CreateDevice_End(); - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_Reset -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_Reset) -( - X_D3DPRESENT_PARAMETERS* pPresentationParameters -) -{ - LOG_FUNC_ONE_ARG(pPresentationParameters) - - // Unlike the host version of Reset, The Xbox version does not actually reset the entire device - // Instead, it simply re-creates the backbuffer with a new configuration - - // Store the new multisampling configuration - SetXboxMultiSampleType(pPresentationParameters->MultiSampleType); - - // Since Reset will call create a new backbuffer surface, we can clear our current association - // NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below. - // We must not reset the values to nullptr, since the XDK will re-use the same addresses for the data headers - // (they are members of the Direct3DDevice object). if we overwrite then, the reference to the xbox backbuffer will be lost - // and we'll get a black screen. - FreeHostResource(GetHostResourceKey(g_pXbox_BackBufferSurface)); - FreeHostResource(GetHostResourceKey(g_pXbox_DefaultDepthStencilSurface)); - - // Call the Xbox Reset function to do the rest of the work for us - HRESULT hRet = XB_TRMP(D3DDevice_Reset)(pPresentationParameters); - - // Refresh the current render target and depth stencil, to apply changes made within D3DDevice_Reset - // Some XDKs do this for us, but not all do! - EMUPATCH(D3DDevice_SetRenderTarget)(g_pXbox_RenderTarget, g_pXbox_DepthStencil); - - return hRet; -} - - -// ****************************************************************** -// * patch: D3DDevice_GetDisplayFieldStatus -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetDisplayFieldStatus)(X_D3DFIELD_STATUS *pFieldStatus) -{ - // NOTE: This can be unpatched only when NV2A does it's own VBlank and HLE _Swap function is unpatched - - - LOG_FUNC_ONE_ARG(pFieldStatus); - - // Read AV Flags to determine if Progressive or Interlaced - // The xbox does this by reading from pDevice->m_Miniport.m_CurrentAvInfo - // but we don't have an OOVPA for that. Instead, we call the Xbox implementation of - // D3DDevice_GetDisplayMode and read the result - - X_D3DDISPLAYMODE displayMode; - - // If we can find the Xbox version of GetDisplayMode, use the real data returned, otherwise - // use a sensible default - if (XB_TRMP(D3DDevice_GetDisplayMode) != nullptr) { - XB_TRMP(D3DDevice_GetDisplayMode)(&displayMode); - } else { - // We don't show a warning because doing so pollutes the kernel debug log as this function gets called every frame - displayMode.Flags = X_D3DPRESENTFLAG_INTERLACED; - } - - // Set the VBlank count - pFieldStatus->VBlankCount = g_Xbox_VBlankData.VBlank; - - // If we are interlaced, return the current field, otherwise, return progressive scan - if (displayMode.Flags & X_D3DPRESENTFLAG_INTERLACED) { - pFieldStatus->Field = (g_Xbox_VBlankData.VBlank % 2 == 0) ? X_D3DFIELD_ODD : X_D3DFIELD_EVEN; - } else { - pFieldStatus->Field = X_D3DFIELD_PROGRESSIVE; - } -} - -// ****************************************************************** -// * patch: D3DDevice_BeginPush -// TODO: Find a test case and verify this -// Starting from XDK 4531, this changed to 1 parameter only. -// Is this definition incorrect, or did it change at some point? -// ****************************************************************** -PDWORD WINAPI XTL::EMUPATCH(D3DDevice_BeginPush)(DWORD Count) -{ - LOG_FUNC_ONE_ARG(Count); - - if (g_pXbox_BeginPush_Buffer != nullptr) - { - EmuLog(LOG_LEVEL::WARNING, "D3DDevice_BeginPush called without D3DDevice_EndPush in between?!"); - delete[] g_pXbox_BeginPush_Buffer; // prevent a memory leak - } - - DWORD *pRet = new DWORD[Count]; - - g_pXbox_BeginPush_Buffer = pRet; - - return pRet; -} - -// ****************************************************************** -// * patch: D3DDevice_BeginPush2 -// TODO: Find a test case and verify this: RalliSport Challenge XDK 4134 -// For XDK before 4531 -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_BeginPush2)(DWORD Count, DWORD** ppPush) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Count) - LOG_FUNC_ARG(ppPush) - LOG_FUNC_END; - - if (g_pXbox_BeginPush_Buffer != nullptr) - { - EmuLog(LOG_LEVEL::WARNING, "D3DDevice_BeginPush2 called without D3DDevice_EndPush in between?!"); - delete[] g_pXbox_BeginPush_Buffer; // prevent a memory leak - } - - DWORD *pRet = new DWORD[Count]; - - g_pXbox_BeginPush_Buffer = pRet; - - *ppPush=pRet; -} - -// ****************************************************************** -// * patch: D3DDevice_EndPush -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_EndPush)(DWORD *pPush) -{ - LOG_FUNC_ONE_ARG(pPush); - - if (g_pXbox_BeginPush_Buffer == nullptr) - EmuLog(LOG_LEVEL::WARNING, "D3DDevice_EndPush called without preceding D3DDevice_BeginPush?!"); - else - { - // Note: We don't use the count from BeginPush because that specifies the *maximum* count - // rather than the count actually in the pushbuffer. - EmuExecutePushBufferRaw(g_pXbox_BeginPush_Buffer, (uintptr_t)pPush - (uintptr_t)g_pXbox_BeginPush_Buffer); - - delete[] g_pXbox_BeginPush_Buffer; - g_pXbox_BeginPush_Buffer = nullptr; - } -} - -// ****************************************************************** -// * patch: D3DDevice_BeginVisibilityTest -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_BeginVisibilityTest)() -{ - LOG_FUNC(); - - if (g_bEnableHostQueryVisibilityTest) { - // Create a D3D occlusion query to handle "visibility test" with - IDirect3DQuery* pHostQueryVisibilityTest = nullptr; - HRESULT hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pHostQueryVisibilityTest); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (visibility test)"); - if (pHostQueryVisibilityTest != nullptr) { - hRet = pHostQueryVisibilityTest->Issue(D3DISSUE_BEGIN); - DEBUG_D3DRESULT(hRet, "g_pHostQueryVisibilityTest->Issue(D3DISSUE_BEGIN)"); - if (SUCCEEDED(hRet)) { - g_HostQueryVisibilityTests.push(pHostQueryVisibilityTest); - } else { - LOG_TEST_CASE("Failed to issue query"); - pHostQueryVisibilityTest->Release(); - } - - pHostQueryVisibilityTest = nullptr; - } - } - - return D3D_OK; -} - -// LTCG specific D3DDevice_EndVisibilityTest function... -// This uses a custom calling convention where parameter is passed in EAX -// Test-case: Test Drive: Eve of Destruction -HRESULT __stdcall XTL::EMUPATCH(D3DDevice_EndVisibilityTest_0) -( -) -{ - DWORD Index; - - __asm { - mov Index, eax - } - - return EMUPATCH(D3DDevice_EndVisibilityTest)(Index); -} - -// ****************************************************************** -// * patch: D3DDevice_EndVisibilityTest -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_EndVisibilityTest) -( - DWORD Index -) -{ - LOG_FUNC_ONE_ARG(Index); - - if (g_bEnableHostQueryVisibilityTest) { - // Check that the dedicated storage for the given Index isn't in use - if (g_HostVisibilityTestMap[Index] != nullptr) { - return E_OUTOFMEMORY; - } - - if (g_HostQueryVisibilityTests.empty()) { - return 2088; // visibility test incomplete (a prior BeginVisibilityTest call is needed) - } - - IDirect3DQuery* pHostQueryVisibilityTest = g_HostQueryVisibilityTests.top(); - g_HostQueryVisibilityTests.pop(); - assert(pHostQueryVisibilityTest != nullptr); - - HRESULT hRet = pHostQueryVisibilityTest->Issue(D3DISSUE_END); - DEBUG_D3DRESULT(hRet, "g_pHostQueryVisibilityTest->Issue(D3DISSUE_END)"); - if (hRet == D3D_OK) { - // Associate the result of this call with the given Index - g_HostVisibilityTestMap[Index] = pHostQueryVisibilityTest; - } else { - LOG_TEST_CASE("Failed to issue query"); - pHostQueryVisibilityTest->Release(); - } - } - - return D3D_OK; -} - -// ****************************************************************** -// * patch: D3DDevice_SetBackBufferScale -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetBackBufferScale)(FLOAT x, FLOAT y) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(x) - LOG_FUNC_ARG(y) - LOG_FUNC_END; - - LOG_IGNORED(); -} - -// ****************************************************************** -// * patch: D3DDevice_GetVisibilityTestResult -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVisibilityTestResult) -( - DWORD Index, - UINT *pResult, - ULONGLONG *pTimeStamp -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Index) - LOG_FUNC_ARG(pResult) - LOG_FUNC_ARG(pTimeStamp) - LOG_FUNC_END; - - if (g_bEnableHostQueryVisibilityTest) { - IDirect3DQuery* pHostQueryVisibilityTest = g_HostVisibilityTestMap[Index]; - if (pHostQueryVisibilityTest == nullptr) { - return E_OUTOFMEMORY; - } - - // In order to prevent an endless loop if the D3D device becomes lost, we pass - // the D3DGETDATA_FLUSH flag. This tells GetData to return D3DERR_DEVICELOST if - // such a situation occurs, and break out of the loop as a result. - // Note: By Cxbx's design, we cannot do drawing within this while loop in order - // to further prevent any other endless loop situations. - while (S_FALSE == pHostQueryVisibilityTest->GetData(pResult, sizeof(DWORD), D3DGETDATA_FLUSH)); - - g_HostVisibilityTestMap[Index] = nullptr; - pHostQueryVisibilityTest->Release(); - } else { - // Fallback to old faked result when there's no host occlusion query : - if (pResult != xbnullptr) { - *pResult = 640 * 480; // TODO : Use actual backbuffer dimensions - } - } - - if (pTimeStamp != xbnullptr) { - LOG_TEST_CASE("requested value for pTimeStamp"); - *pTimeStamp = sizeof(DWORD); // TODO : This should be an incrementing GPU-memory based DWORD-aligned memory address - } - - return D3D_OK; -} - -// LTCG specific D3DDevice_LoadVertexShader function... -// This uses a custom calling convention where parameter is passed in EAX, ECX -// Test-case: Aggressive Inline -VOID __stdcall XTL::EMUPATCH(D3DDevice_LoadVertexShader_0) -( -) -{ - DWORD Handle; - DWORD Address; - - __asm { - mov Address, eax - mov Handle, ecx - } - - return EMUPATCH(D3DDevice_LoadVertexShader)(Handle, Address); -} - -// This uses a custom calling convention where parameter is passed in EAX -// Test-case: Ninja Gaiden -VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShader_4) -( - DWORD Address -) -{ - DWORD Handle; - __asm mov Handle, eax; - - LOG_FORWARD("D3DDevice_LoadVertexShader"); - return EMUPATCH(D3DDevice_LoadVertexShader)(Handle, Address); -} - -// ****************************************************************** -// * patch: D3DDevice_LoadVertexShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShader) -( - DWORD Handle, - DWORD Address -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(Address) - LOG_FUNC_END; - - CxbxImpl_LoadVertexShader(Handle, Address); -} - -// LTCG specific D3DDevice_SelectVertexShader function... -// This uses a custom calling convention where parameter is passed in EAX, EBX -// Test-case: Star Wars - Battlefront -VOID __stdcall XTL::EMUPATCH(D3DDevice_SelectVertexShader_0) -( -) -{ - DWORD Handle; - DWORD Address; - - __asm { - mov Handle, eax - mov Address, ebx - } - - return EMUPATCH(D3DDevice_SelectVertexShader)(Handle, Address); -} - -// LTCG specific D3DDevice_SelectVertexShader function... -// This uses a custom calling convention where parameter is passed in EAX -// Test-case: Aggressive Inline -VOID __stdcall XTL::EMUPATCH(D3DDevice_SelectVertexShader_4) -( - DWORD Address -) -{ - DWORD Handle; - __asm mov Handle, eax; - - return EMUPATCH(D3DDevice_SelectVertexShader)(Handle, Address); -} - -// ****************************************************************** -// * patch: D3DDevice_SelectVertexShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SelectVertexShader) -( - DWORD Handle, - DWORD Address -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(Address) - LOG_FUNC_END; - - CxbxImpl_SelectVertexShader(Handle, Address); -} - -// ****************************************************************** -// * patch: D3DDevice_SetGammaRamp -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetGammaRamp) -( - DWORD dwFlags, - CONST X_D3DGAMMARAMP *pRamp -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(dwFlags) - LOG_FUNC_ARG(pRamp) - LOG_FUNC_END; - - // remove D3DSGR_IMMEDIATE - DWORD dwPCFlags = dwFlags & (~0x00000002); - D3DGAMMARAMP PCRamp; - - for(int v=0;v<255;v++) - { - PCRamp.red[v] = pRamp->red[v]; - PCRamp.green[v] = pRamp->green[v]; - PCRamp.blue[v] = pRamp->blue[v]; - } - -#if 0 // TODO : Why is this disabled? - g_pD3DDevice->SetGammaRamp( - 0, // iSwapChain - dwPCFlags, &PCRamp); -#endif -} - -// ****************************************************************** -// * patch: D3DDevice_GetGammaRamp -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetGammaRamp) -( - X_D3DGAMMARAMP *pRamp -) -{ - LOG_FUNC_ONE_ARG(pRamp); - - D3DGAMMARAMP *pGammaRamp = (D3DGAMMARAMP *)malloc(sizeof(D3DGAMMARAMP)); - - g_pD3DDevice->GetGammaRamp( - 0, // iSwapChain - pGammaRamp); - - for(int v=0;v<256;v++) - { - pRamp->red[v] = (BYTE)pGammaRamp->red[v]; - pRamp->green[v] = (BYTE)pGammaRamp->green[v]; - pRamp->blue[v] = (BYTE)pGammaRamp->blue[v]; - } - - free(pGammaRamp); -} - -// ****************************************************************** -// * patch: D3DDevice_GetBackBuffer2 -// ****************************************************************** -#define COPY_BACKBUFFER_TO_XBOX_SURFACE // Uncomment to enable writing Host Backbuffers back to Xbox surfaces -XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2) -( - INT BackBuffer -) -{ - LOG_FUNC_ONE_ARG(BackBuffer); - - X_D3DSurface* pXboxBackBuffer = nullptr; - - -#ifndef COPY_BACKBUFFER_TO_XBOX_SURFACE - /** unsafe, somehow - HRESULT hRet = D3D_OK; - - X_D3DSurface *pXboxBackBuffer = EmuNewD3DSurface(); - - if(BackBuffer == -1) { - static IDirect3DSurface *pCachedPrimarySurface = nullptr; - - if(pCachedPrimarySurface == nullptr) { - // create a buffer to return - // TODO: Verify the surface is always 640x480 - hRet = g_pD3DDevice->CreateOffscreenPlainSurface(640, 480, D3DFMT_A8R8G8B8, /*D3DPool=* /0, &pCachedPrimarySurface, nullptr); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateOffscreenPlainSurface"); - } - - SetHostSurface(pXboxBackBuffer, pCachedPrimarySurface); // No iTextureStage! - - hRet = g_pD3DDevice->GetFrontBuffer(pCachedPrimarySurface); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetFrontBuffer"); - - if (FAILED(hRet)) { - EmuLog(LOG_LEVEL::WARNING, "Could not retrieve primary surface, using backbuffer"); - SetHostSurface(pXboxBackBuffer, nullptr); // No iTextureStage! - pCachedPrimarySurface->Release(); - pCachedPrimarySurface = nullptr; - BackBuffer = 0; - } - - // Debug: Save this image temporarily - //D3DXSaveSurfaceToFile("C:\\Aaron\\Textures\\FrontBuffer.bmp", D3DXIFF_BMP, GetHostSurface(pXboxBackBuffer), nullptr, nullptr); - } - - if(BackBuffer != -1) { - hRet = g_pD3DDevice->GetBackBuffer( - 0, // iSwapChain - BackBuffer, D3DBACKBUFFER_TYPE_MONO, &pCachedPrimarySurface); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer"); - } - //*/ - - static X_D3DSurface *pXboxBackBuffer = EmuNewD3DSurface(); - IDirect3DSurface *pCurrentHostBackBuffer = nullptr; - - STATUS_SUCCESS; - - if (BackBuffer == -1) { - BackBuffer = 0; - } - - HRESULT hRet = g_pD3DDevice->GetBackBuffer( - 0, // iSwapChain - BackBuffer, D3DBACKBUFFER_TYPE_MONO, &pCurrentHostBackBuffer); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer"); - - if (FAILED(hRet)) - CxbxKrnlCleanup("Unable to retrieve back buffer"); - - SetHostSurface(pXboxBackBuffer, pCurrentHostBackBuffer); // No iTextureStage! - - // Increment reference count - pXboxBackBuffer->Common++; // EMUPATCH(D3DResource_AddRef)(pXboxBackBuffer); - - return pXboxBackBuffer; -#else // COPY_BACKBUFFER_TO_XBOX_SURFACE - // Rather than create a new surface, we should forward to the Xbox version of GetBackBuffer, - // This gives us the correct Xbox surface to update. - // We get signatures for both backbuffer functions as it changed in later XDKs - - // This also updates the reference count, so we don't need to do this ourselves - if (XB_TRMP(D3DDevice_GetBackBuffer) != nullptr) { - XB_TRMP(D3DDevice_GetBackBuffer)(BackBuffer, D3DBACKBUFFER_TYPE_MONO, &pXboxBackBuffer); - } - else { - pXboxBackBuffer = XB_TRMP(D3DDevice_GetBackBuffer2)(BackBuffer); - } - - // Now pXboxBackbuffer points to the requested Xbox backbuffer - if (pXboxBackBuffer == nullptr) { - CxbxKrnlCleanup("D3DDevice_GetBackBuffer2: Could not get Xbox backbuffer"); - } - - - // HACK: Disabled: Enabling this breaks DOA3 at native res/without hacks+ - // Also likely to effect Other games, but it has no known benefit at this point in time - // There are currently no known games that depend on backbuffer readback on the CPU! -#if 0 - // TODO: Downscale the host surface to the same size as the Xbox surface during copy - // Otherwise, we will overflow memory and crash - // HACK: For now, when using a non-zero scale factor, we can just skip the copy to prevent a crash - if (g_RenderScaleFactor == 1) { - auto pCopySrcSurface = GetHostSurface(pXboxBackBuffer, D3DUSAGE_RENDERTARGET); - if (pCopySrcSurface == nullptr) { - EmuLog(LOG_LEVEL::WARNING, "Failed to get Host Resource for Xbox Back Buffer"); - return pXboxBackBuffer; - } - - D3DLOCKED_RECT copyLockedRect; - HRESULT hRet = pCopySrcSurface->LockRect(©LockedRect, NULL, D3DLOCK_READONLY); - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Could not lock Host Resource for Xbox Back Buffer"); - return pXboxBackBuffer; - } - - D3DSURFACE_DESC copySurfaceDesc; - hRet = pCopySrcSurface->GetDesc(©SurfaceDesc); - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Could not get Xbox Back Buffer Host Surface Desc"); - } - else { - DWORD Size = copyLockedRect.Pitch * copySurfaceDesc.Height; // TODO : What about mipmap levels? (Backbuffer does not support mipmap) - // Finally, do the copy from the converted host resource to the xbox resource - memcpy((void*)GetDataFromXboxResource(pXboxBackBuffer), copyLockedRect.pBits, Size); - } - - pCopySrcSurface->UnlockRect(); - } -#endif - - return pXboxBackBuffer; -#endif // COPY_BACKBUFFER_TO_XBOX_SURFACE -} - -// ****************************************************************** -// * patch: D3DDevice_GetBackBuffer -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer) -( - INT BackBuffer, - D3DBACKBUFFER_TYPE Type, - X_D3DSurface **ppBackBuffer -) -{ - LOG_FORWARD("D3DDevice_GetBackBuffer2"); - - *ppBackBuffer = EMUPATCH(D3DDevice_GetBackBuffer2)(BackBuffer); -} - -bool GetHostRenderTargetDimensions(DWORD *pHostWidth, DWORD *pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr) -{ - bool shouldRelease = false; - if (pHostRenderTarget == nullptr) { - g_pD3DDevice->GetRenderTarget( - 0, // RenderTargetIndex - &pHostRenderTarget); - - shouldRelease = true; - } - - // The following can only work if we could retrieve a host render target - if (!pHostRenderTarget) { - return false; - } - - // Get current host render target dimensions - D3DSURFACE_DESC HostRenderTarget_Desc; - pHostRenderTarget->GetDesc(&HostRenderTarget_Desc); - - if (shouldRelease) { - pHostRenderTarget->Release(); - } - - *pHostWidth = HostRenderTarget_Desc.Width; - *pHostHeight = HostRenderTarget_Desc.Height; - - return true; -} - -DWORD ScaleDWORD(DWORD Value, DWORD FromMax, DWORD ToMax) -{ - uint64_t tmp = Value; - tmp *= ToMax; - tmp /= FromMax; - return (DWORD)tmp; -} - -void ValidateRenderTargetDimensions(DWORD HostRenderTarget_Width, DWORD HostRenderTarget_Height, DWORD XboxRenderTarget_Width, DWORD XboxRenderTarget_Height) -{ - // This operation is often used to change the display resolution without calling SetRenderTarget! - // This works by updating the underlying Width & Height of the Xbox surface, without reallocating the data - // Because of this, we need to validate that the associated host resource still matches the dimensions of the Xbox Render Target - // If not, we must force them to be re-created - // TEST CASE: Chihiro Factory Test Program - DWORD XboxRenderTarget_Width_Scaled = XboxRenderTarget_Width * g_RenderScaleFactor; - DWORD XboxRenderTarget_Height_Scaled = XboxRenderTarget_Height * g_RenderScaleFactor; - if (HostRenderTarget_Width != XboxRenderTarget_Width_Scaled || HostRenderTarget_Height != XboxRenderTarget_Height_Scaled) { - LOG_TEST_CASE("Existing RenderTarget width/height changed"); - - if (g_pXbox_RenderTarget == g_pXbox_BackBufferSurface) { - FreeHostResource(GetHostResourceKey(g_pXbox_RenderTarget)); g_pD3DDevice->SetRenderTarget(0, GetHostSurface(g_pXbox_RenderTarget, D3DUSAGE_RENDERTARGET)); - FreeHostResource(GetHostResourceKey(g_pXbox_DepthStencil)); g_pD3DDevice->SetDepthStencilSurface(GetHostSurface(g_pXbox_DepthStencil, D3DUSAGE_DEPTHSTENCIL)); - } - } -} - -float GetZScaleForSurface(XTL::X_D3DSurface* pSurface) -{ - // If no surface was present, fallback to 1 - if (pSurface == xbnullptr) { - return 1; - } - - auto format = GetXboxPixelContainerFormat(pSurface); - switch (format) { - case XTL::X_D3DFMT_D16: - case XTL::X_D3DFMT_LIN_D16: - return 65535.0f; - - case XTL::X_D3DFMT_D24S8: - case XTL::X_D3DFMT_LIN_D24S8: - return 16777215.0f; - - case XTL::X_D3DFMT_F16: - case XTL::X_D3DFMT_LIN_F16: - return 511.9375f; - - case XTL::X_D3DFMT_F24S8: - case XTL::X_D3DFMT_LIN_F24S8: - // 24bit floating point is close to precision maximum, so a lower value is used - // We can't use a double here since the vertex shader is only at float precision - return 1.0e30f; - } - - // Default to 1 if unknown depth format - LOG_TEST_CASE("GetZScaleForSurface: Unknown Xbox Depth Format"); - return 1; -} - -void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) -{ - // Store viewport offset and scale in constant registers - // used in shaders to transform back from screen space (Xbox Shader Output) to Clip space (Host Shader Output) - D3DVIEWPORT ViewPort; - g_pD3DDevice->GetViewport(&ViewPort); - - // NOTE: Due to how our GPU emulation works, we need to account for MSAA here, by adjusting the ViewPort dimensions - // This fixes the 'offset' models in GTA3 - float xScale, yScale; - float xOffset, yOffset; - GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); - // Since Width and Height are DWORD, subtracting MultiSampleOffset 0.0f or 0.5f makes no sense - //ViewPort.Width -= xOffset; - //ViewPort.Height -= yOffset; - ViewPort.Width /= (DWORD)xScale; - ViewPort.Height /= (DWORD)yScale; - - // Calculate Width/Height scale & offset - float scaleWidth = (2.0f / ViewPort.Width) * g_RenderScaleFactor; - float scaleHeight = (2.0f / ViewPort.Height) * g_RenderScaleFactor; - float offsetWidth = scaleWidth; - float offsetHeight = scaleHeight; - - // Calculate Z scale & offset - float zScale = GetZScaleForSurface(g_pXbox_DepthStencil); - float scaleZ = zScale * (ViewPort.MaxZ - ViewPort.MinZ); - float offsetZ = zScale * ViewPort.MinZ; - - // TODO will we need to do something here to support upscaling? - // TODO remove the code above as required - - // Reset to default scale (as we accounted for MSAA scale above) - // But don't reset the offset - xScale = 1.0f; - yScale = 1.0f; - - // Xbox correct values? - xOffset = xOffset + (1.0f / 32.0f); - yOffset = yOffset + (1.0f / 32.0f); - xScale = xScale * ViewPort.Width; - yScale = yScale * ViewPort.Height; - - // HACK: Add a host correction factor to these values - // So that after we reverse the screenspace transformation - // Pre-transformed 2d geometry is in the same space as the 3d geometry...? - - // Offset with a host correction - vOffset[0] = xOffset + (0.5f * (float)ViewPort.Width / (float)g_RenderScaleFactor); - vOffset[1] = yOffset + (0.5f * (float)ViewPort.Height / (float)g_RenderScaleFactor); - vOffset[2] = 0.0f; //offsetZ; - vOffset[3] = 0.0f; - - // Scale with a host correction - vScale[0] = xScale * (1.0f / ( 2.0f * (float)g_RenderScaleFactor)); - vScale[1] = yScale * (1.0f / (-2.0f * (float)g_RenderScaleFactor)); - vScale[2] = scaleZ; // ? - vScale[3] = 1.0f; // ? -} - -void UpdateViewPortOffsetAndScaleConstants() -{ - float vOffset[4], vScale[4]; - GetViewPortOffsetAndScale(vOffset, vScale); - - g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_VIEWPORT_SCALE_MIRROR, vScale, 1); - g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR, vOffset, 1); - - // Store viewport offset and scale in constant registers 58 (c-38) and - // 59 (c-37) used for screen space transformation. - // We only do this if X_D3DSCM_NORESERVEDCONSTANTS is not set, since enabling this flag frees up these registers for shader used - // Treat this as a flag - // Test Case: GTA III, Soldier of Fortune II - if (!(g_Xbox_VertexShaderConstantMode & X_D3DSCM_NORESERVEDCONSTANTS)) { - g_pD3DDevice->SetVertexShaderConstantF(X_D3DSCM_RESERVED_CONSTANT_SCALE + X_D3DSCM_CORRECTION, vScale, 1); - g_pD3DDevice->SetVertexShaderConstantF(X_D3DSCM_RESERVED_CONSTANT_OFFSET + X_D3DSCM_CORRECTION, vOffset, 1); - } -} - -// ****************************************************************** -// * patch: D3DDevice_SetViewport -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetViewport) -( - CONST X_D3DVIEWPORT8 *pViewport -) -{ - LOG_FUNC_ONE_ARG(pViewport); - - // Always call the Xbox SetViewPort to update D3D Internal State - XB_TRMP(D3DDevice_SetViewport)(pViewport); - - // Host does not support pViewPort = nullptr - if (pViewport == nullptr) { - LOG_TEST_CASE("pViewport = null"); - return; - } - - D3DVIEWPORT XboxViewPort = *pViewport; - D3DVIEWPORT HostViewPort = *pViewport; - - if (g_pXbox_RenderTarget) { - // Clip the Xbox Viewport to the render target dimensions - // This is required because during SetRenderTarget, Xbox calls SetViewPort with impossibly large values - DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXbox_RenderTarget); - DWORD XboxRenderTarget_Height = GetPixelContainerHeight(g_pXbox_RenderTarget); - - DWORD left = std::max((int)pViewport->X, 0); - DWORD top = std::max((int)pViewport->Y, 0); - DWORD right = std::min((int)pViewport->X + (int)pViewport->Width, (int)XboxRenderTarget_Width); - DWORD bottom = std::min((int)pViewport->Y + (int)pViewport->Height, (int)XboxRenderTarget_Height); - DWORD width = right - left; - DWORD height = bottom - top; - - XboxViewPort.X = left; - XboxViewPort.Y = top; - XboxViewPort.Width = width; - XboxViewPort.Height = height; - XboxViewPort.MinZ = pViewport->MinZ; - XboxViewPort.MaxZ = pViewport->MaxZ; - - - // Store the updated viewport data ready to pass to host SetViewPort - HostViewPort = XboxViewPort; - - DWORD HostRenderTarget_Width = 0, HostRenderTarget_Height = 0; - if (GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) { - ValidateRenderTargetDimensions(HostRenderTarget_Width, HostRenderTarget_Height, XboxRenderTarget_Width, XboxRenderTarget_Height); - - // We *must* always scale the viewport to the associated host surface - // Otherwise, we only get partial screen updates, or broken upscaling - // Scale Xbox to host dimensions (avoiding hard-coding 640 x 480) - HostViewPort.X = ScaleDWORD(XboxViewPort.X, XboxRenderTarget_Width, HostRenderTarget_Width); - HostViewPort.Y = ScaleDWORD(XboxViewPort.Y, XboxRenderTarget_Height, HostRenderTarget_Height); - HostViewPort.Width = ScaleDWORD(XboxViewPort.Width, XboxRenderTarget_Width, HostRenderTarget_Width); - HostViewPort.Height = ScaleDWORD(XboxViewPort.Height, XboxRenderTarget_Height, HostRenderTarget_Height); - // TODO : Fix test-case Shenmue 2 (which halves height, leaving the bottom half unused) - HostViewPort.MinZ = XboxViewPort.MinZ; // No need scale Z for now - HostViewPort.MaxZ = XboxViewPort.MaxZ; - } else { - LOG_TEST_CASE("SetViewPort: Unable to fetch host render target dimensions"); - } - } - - // Apply MSAA scale and offset - float xScale, yScale; - GetMultiSampleScale(xScale, yScale); - HostViewPort.Width *= (DWORD)xScale; - HostViewPort.Height *= (DWORD)yScale; - // Since Width and Height are DWORD, adding GetMultiSampleOffset 0.0f or 0.5f makes no sense - - HRESULT hRet = g_pD3DDevice->SetViewport(&HostViewPort); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetViewport"); - - UpdateViewPortOffsetAndScaleConstants(); -} - -// LTCG specific D3DDevice_SetShaderConstantMode function... -// This uses a custom calling convention where parameter is passed in EAX -VOID __stdcall XTL::EMUPATCH(D3DDevice_SetShaderConstantMode_0) -( -) -{ - XTL::X_VERTEXSHADERCONSTANTMODE param; - __asm { - mov param, eax; - } - - return EMUPATCH(D3DDevice_SetShaderConstantMode)(param); -} - -// ****************************************************************** -// * patch: D3DDevice_SetShaderConstantMode -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetShaderConstantMode) -( - XTL::X_VERTEXSHADERCONSTANTMODE Mode -) -{ - LOG_FUNC_ONE_ARG(Mode); - - g_Xbox_VertexShaderConstantMode = Mode; -} - -// ****************************************************************** -// * patch: D3DDevice_CreateVertexShader -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_CreateVertexShader) -( - CONST DWORD *pDeclaration, - CONST DWORD *pFunction, - DWORD *pHandle, - DWORD Usage -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pDeclaration) - LOG_FUNC_ARG(pFunction) - LOG_FUNC_ARG(pHandle) - LOG_FUNC_ARG_TYPE(X_D3DUSAGE, Usage) - LOG_FUNC_END; - - // First, we must call the Xbox CreateVertexShader function and check for success - // This does the following: - // Allocates an Xbox VertexShader struct - // Sets reference count to 1 - // Puts Usage in VertexShader->Flags - // If pFunction is not null, it points to DWORDS shader type, length and a binary compiled xbox vertex shader - // If pDeclaration is not null, it's parsed, resulting in a number of constants - // Parse results are pushed to the push buffer - // Sets other fields - // pHandle recieves the addres of the new shader, or-ed with 1 (D3DFVF_RESERVED0) - - HRESULT hRet = D3D_OK; - - if (XB_TRMP(D3DDevice_CreateVertexShader)) { - HRESULT hRet = XB_TRMP(D3DDevice_CreateVertexShader)(pDeclaration, pFunction, pHandle, Usage); - if (FAILED(hRet)) { - LOG_TEST_CASE("D3DDevice_CreateVertexShader trampoline call returned failure"); - RETURN(hRet); - } - } else { - // Due to how our LoadVertexShader patch is implemented, it may call this function without the Xbox version existing - // As a result, we have to build our own vertex shader handle if the trampoline was not found - // We don't do the full steps listed above intentionally so: If this situation is reached, the game - // does not have a CreateVertexShader function, so those actions should not happen anyway! - LOG_TEST_CASE("CreateVertexShader with no trampoline"); - *pHandle = ((DWORD)malloc(sizeof(X_D3DVertexShader)) & D3DFVF_RESERVED0); - } - - return CxbxImpl_CreateVertexShader(pDeclaration, pFunction, pHandle, Usage); -} - -// LTCG specific D3DDevice_SetVertexShaderConstant function... -// This uses a custom calling convention where parameter is passed in EDX -// Test-case: Murakumo -VOID __stdcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant_8) -( -) -{ - static uint32_t returnAddr; - -#ifdef _DEBUG_TRACE - __asm add esp, 4 -#endif - - __asm { - pop returnAddr - push edx - call EmuPatch_D3DDevice_SetVertexShaderConstant - mov eax, 0 - push returnAddr - ret - } -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderConstant -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant) -( - INT Register, - CONST PVOID pConstantData, - DWORD ConstantCount -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Register) - LOG_FUNC_ARG(pConstantData) - LOG_FUNC_ARG(ConstantCount) - LOG_FUNC_END; - - CxbxImpl_SetVertexShaderConstant(Register, pConstantData, ConstantCount); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderConstant1 -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant1) -( - INT Register, - CONST PVOID pConstantData -) -{ - LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); - - // The XDK uses a macro to automatically adjust to 0..191 range - // but D3DDevice_SetVertexShaderConstant expects -96..95 range - // so we adjust before forwarding - EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, 1); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderConstant1Fast -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant1Fast) -( - INT Register, - CONST PVOID pConstantData -) -{ - LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); - - // The XDK uses a macro to automatically adjust to 0..191 range - // but D3DDevice_SetVertexShaderConstant expects -96..95 range - // so we adjust before forwarding - EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, 1); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderConstant4 -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant4) -( - INT Register, - CONST PVOID pConstantData -) -{ - LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); - - // The XDK uses a macro to automatically adjust to 0..191 range - // but D3DDevice_SetVertexShaderConstant expects -96..95 range - // so we adjust before forwarding - EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, 4); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderConstantNotInline -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline) -( - INT Register, - CONST PVOID pConstantData, - DWORD ConstantCount -) -{ - LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); - - // The XDK uses a macro to automatically adjust to 0..191 range - // but D3DDevice_SetVertexShaderConstant expects -96..95 range - // so we adjust before forwarding - EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, ConstantCount / 4); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderConstantNotInlineFast -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInlineFast) -( - INT Register, - CONST PVOID pConstantData, - DWORD ConstantCount -) -{ - LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); - - // The XDK uses a macro to automatically adjust to 0..191 range - // but D3DDevice_SetVertexShaderConstant expects -96..95 range - // so we adjust before forwarding - EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, ConstantCount / 4); -} - -// LTCG specific D3DDevice_SetTexture function... -// This uses a custom calling convention where parameter is passed in EAX -// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX. -// Test-case: Metal Wolf Chaos -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetTexture_4) -( - X_D3DBaseTexture *pTexture -) -{ - DWORD Stage; - __asm mov Stage, eax; - - //LOG_FUNC_BEGIN - // LOG_FUNC_ARG(Stage) - // LOG_FUNC_ARG(pTexture) - // LOG_FUNC_END; - EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetTexture_4(Stage : %d pTexture : %08x);", Stage, pTexture); - - // Call the Xbox implementation of this function, to properly handle reference counting for us - //XB_TRMP(D3DDevice_SetTexture_4)(pTexture); - - g_pXbox_SetTexture[Stage] = pTexture; -} - -// ****************************************************************** -// * patch: D3DDevice_SetTexture -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetTexture) -( - DWORD Stage, - X_D3DBaseTexture *pTexture -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Stage) - LOG_FUNC_ARG(pTexture) - LOG_FUNC_END; - - // Call the Xbox implementation of this function, to properly handle reference counting for us - XB_TRMP(D3DDevice_SetTexture)(Stage, pTexture); - - g_pXbox_SetTexture[Stage] = pTexture; -} - -// ****************************************************************** -// * patch: D3DDevice_SwitchTexture -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SwitchTexture) -( - DWORD Method, - DWORD Data, - DWORD Format -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Method) - LOG_FUNC_ARG(Data) - LOG_FUNC_ARG(Format) - LOG_FUNC_END; - - DWORD StageLookup[XTL::X_D3DTS_STAGECOUNT] = { 0x00081b00, 0x00081b40, 0x00081b80, 0x00081bc0 }; - // This array contains D3DPUSH_ENCODE(NV2A_TX_OFFSET(v), 2) = 2 DWORD's, shifted left PUSH_COUNT_SHIFT (18) left - DWORD Stage = -1; - - for (int v = 0; v < XTL::X_D3DTS_STAGECOUNT; v++) { - if (StageLookup[v] == Method) { - Stage = v; - break; - } - } - - if (Stage == -1) { - LOG_TEST_CASE("D3DDevice_SwitchTexture Unknown Method"); - EmuLog(LOG_LEVEL::WARNING, "Unknown Method (0x%.08X)", Method); - } - else { - // Switch Texture updates the data pointer of an active texture using pushbuffer commands - if (g_pXbox_SetTexture[Stage] == xbnullptr) { - LOG_TEST_CASE("D3DDevice_SwitchTexture without an active texture"); - } - else { - //LOG_TEST_CASE("Using CxbxActiveTextureCopies"); - // See https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/1159 - // Test-case : Arena Football - // Test-case : Call of Duty 2: Big Red One - // Test-case : Crimson Skies - // Test-case : Freedom Fighters - see https://www.youtube.com/watch?v=_NDCoLY8V3I - // Test-case : Freestyle MetalX - // Test-case : GENMA ONIMUSHA - // Test-case : Gun - // Test-case : Harry Potter : Quidditch World Cup - // Test-case : King Arthur - // Test-case : Madden NFL 2002 - // Test-case : Madden NFL 2005 - // Test-case : Madden NFL 07 - // Test-case : Need For Speed Most Wanted - // Test-case : Need For Speed Underground - // Test-case : PocketBike Racer - // Test-case : Project Gotham Racing 2 - // Test-case : Richard Burns Rally - // Test-case : Spider - Man 2 - - // Update data and format separately, instead of via GetDataFromXboxResource() - CxbxActiveTextureCopies[Stage].Common = g_pXbox_SetTexture[Stage]->Common; - CxbxActiveTextureCopies[Stage].Data = Data; - CxbxActiveTextureCopies[Stage].Format = Format; - CxbxActiveTextureCopies[Stage].Lock = 0; - CxbxActiveTextureCopies[Stage].Size = g_pXbox_SetTexture[Stage]->Size; - - // Use the above modified copy, instead of altering the active Xbox texture - g_pXbox_SetTexture[Stage] = &CxbxActiveTextureCopies[Stage]; - // Note : Since g_pXbox_SetTexture and CxbxActiveTextureCopies are host-managed, - // Xbox code should never alter these members (so : no reference counting, etc). - // As long as that's guaranteed, this is a safe way to emulate SwitchTexture. - // (GetHostResourceKey also avoids using any Xbox texture resource memory address.) - } - } -} - -// ****************************************************************** -// * patch: D3DDevice_Begin -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_Begin) -( - X_D3DPRIMITIVETYPE PrimitiveType -) -{ - LOG_FUNC_ONE_ARG(PrimitiveType); - - g_InlineVertexBuffer_PrimitiveType = PrimitiveType; - g_InlineVertexBuffer_TableOffset = 0; - g_InlineVertexBuffer_FVF = 0; -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexData2f -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData2f) -( - int Register, - FLOAT a, - FLOAT b -) -{ - LOG_FORWARD("D3DDevice_SetVertexData4f"); - - EMUPATCH(D3DDevice_SetVertexData4f)(Register, a, b, 0.0f, 1.0f); -} - -static inline DWORD FtoDW(FLOAT f) { return *((DWORD*)&f); } -static inline FLOAT DWtoF(DWORD f) { return *((FLOAT*)&f); } - -// ****************************************************************** -// * patch: D3DDevice_SetVertexData2s -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData2s) -( - int Register, - SHORT a, - SHORT b -) -{ - LOG_FORWARD("D3DDevice_SetVertexData4f"); - - float fa, fb; - - // Test case: Halo - // Note : XQEMU verified that the int16_t arguments - // must be mapped to floats in the range [-32768.0, 32767.0] - // (See https://github.com/xqemu/xqemu/pull/176) - fa = (float)a; - fb = (float)b; - - EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, 0.0f, 1.0f); -} - -extern uint32_t HLE_read_NV2A_pgraph_register(const int reg); // Declared in PushBuffer.cpp -extern void HLE_write_NV2A_vertex_attribute_slot(unsigned slot, uint32_t parameter); // Declared in PushBuffer.cpp -extern uint32_t HLE_read_NV2A_vertex_attribute_slot(unsigned VertexSlot); // Declared in PushBuffer.cpp - -extern NV2ADevice* g_NV2A; - -// ****************************************************************** -// * patch: D3DDevice_SetVertexData4f_16 -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4f_16) -( - FLOAT a, - FLOAT b, - FLOAT c, - FLOAT d -) -{ - // This is an LTCG specific version of SetVertexData4f where the first param is passed in edi - int Register = 0; - __asm{ - mov Register, edi - } - - LOG_FORWARD("D3DDevice_SetVertexData4f"); - - EMUPATCH(D3DDevice_SetVertexData4f)(Register, a, b, c, d); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexData4f -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4f) -( - int Register, - FLOAT a, - FLOAT b, - FLOAT c, - FLOAT d -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Register) - LOG_FUNC_ARG(a) - LOG_FUNC_ARG(b) - LOG_FUNC_ARG(c) - LOG_FUNC_ARG(d) - LOG_FUNC_END; - - HRESULT hRet = D3D_OK; - - // Get the vertex shader flags (if any is active) : - uint32_t ActiveVertexAttributeFlags = 0; - if (VshHandleIsVertexShader(g_Xbox_VertexShader_Handle)) { - LOG_TEST_CASE("D3DDevice_SetVertexData4f with active VertexShader"); - X_D3DVertexShader *pXboxVertexShader = VshHandleToXboxVertexShader(g_Xbox_VertexShader_Handle); - if (!(pXboxVertexShader->Flags & 0x10/*=X_VERTEXSHADER_PROGRAM*/)) { - ActiveVertexAttributeFlags = pXboxVertexShader->Flags; - } - - // If we have an active vertex shader, we also write the input to a vertex shader constant - // This allows us to implement Xbox functionality where SetVertexData4f can be used to specify attributes - // not present in the vertex declaration. - // We use range 193 and up to store these values, as Xbox shaders stop at c192! - FLOAT values[] = {a,b,c,d}; - if (Register < 0) LOG_TEST_CASE("Register < 0"); - if (Register >= 16) LOG_TEST_CASE("Register >= 16"); - g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + Register, values, 1); - } - - // Grow g_InlineVertexBuffer_Table to contain at least current, and a potentially next vertex - if (g_InlineVertexBuffer_TableLength <= g_InlineVertexBuffer_TableOffset + 1) { - if (g_InlineVertexBuffer_TableLength == 0) { - g_InlineVertexBuffer_TableLength = PAGE_SIZE / sizeof(struct _D3DIVB); - } else { - g_InlineVertexBuffer_TableLength *= 2; - } - - g_InlineVertexBuffer_Table = (struct _D3DIVB*)realloc(g_InlineVertexBuffer_Table, sizeof(struct _D3DIVB) * g_InlineVertexBuffer_TableLength); - EmuLog(LOG_LEVEL::DEBUG, "Reallocated g_InlineVertexBuffer_Table to %d entries", g_InlineVertexBuffer_TableLength); - } - - // Is this the initial call after D3DDevice_Begin() ? - if (g_InlineVertexBuffer_FVF == 0) { - // Set first vertex to zero (preventing leaks from prior Begin/End calls) - g_InlineVertexBuffer_Table[0] = {}; - - // Handle persistent vertex attribute flags, by resetting non-persistent colors - // to their default value (and leaving the persistent colors alone - see the - // "Copy all attributes of the previous vertex" comment below) : - static const uint32_t ColorBlack = D3DCOLOR_ARGB(0, 0, 0, 0); - static const uint32_t ColorWhite = D3DCOLOR_ARGB(255, 255, 255, 255); - - // If needed, write default vertex colors to HLE NV2A pgraph : - if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTDIFFUSE)) { - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_DIFFUSE, ColorWhite); - } - - if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTSPECULAR)) { - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_SPECULAR, ColorBlack); - } - - if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTBACKDIFFUSE)) { - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKDIFFUSE, ColorWhite); - } - - if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTBACKSPECULAR)) { - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKSPECULAR, ColorBlack); - } - - // Read starting vertex colors from HLE NV2A pgraph : - g_InlineVertexBuffer_Table[0].Diffuse = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_DIFFUSE); - g_InlineVertexBuffer_Table[0].Specular = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_SPECULAR); - g_InlineVertexBuffer_Table[0].BackDiffuse = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKDIFFUSE); - g_InlineVertexBuffer_Table[0].BackSpecular = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKSPECULAR); - } - - int o = g_InlineVertexBuffer_TableOffset; - unsigned int FVFPosType = g_InlineVertexBuffer_FVF & D3DFVF_POSITION_MASK; - - switch(Register) - { - case X_D3DVSDE_VERTEX: - case X_D3DVSDE_POSITION: - { - // Note : Setting position signals completion of a vertex - g_InlineVertexBuffer_Table[o].Position.x = a; - g_InlineVertexBuffer_Table[o].Position.y = b; - g_InlineVertexBuffer_Table[o].Position.z = c; - g_InlineVertexBuffer_Table[o].Rhw = d; // Was : 1.0f; // Dxbx note : Why set Rhw to 1.0? And why ignore d? - - switch (g_InlineVertexBuffer_FVF & D3DFVF_POSITION_MASK) { - case 0: - // No position mask given yet, set it now : - if (g_InlineVertexBuffer_FVF & D3DFVF_NORMAL) { - // See https://msdn.microsoft.com/ru-ru/library/windows/desktop/bb172559(v=vs.85).aspx and DxbxFVFToVertexSizeInBytes - // D3DFVF_NORMAL cannot be combined with D3DFVF_XYZRHW : - g_InlineVertexBuffer_FVF |= D3DFVF_XYZ; - g_InlineVertexBuffer_Table[o].Rhw = 1.0f; // This, just to stay close to prior behaviour - } - else { - // Without D3DFVF_NORMAL, assume D3DFVF_XYZRHW - g_InlineVertexBuffer_FVF |= D3DFVF_XYZRHW; - } - break; - case D3DFVF_XYZ: - case D3DFVF_XYZRHW: - case D3DFVF_XYZB1: - // These are alright - break; - default: - EmuLog(LOG_LEVEL::WARNING, "D3DDevice_SetVertexData4f unexpected FVF when selecting D3DFVF_XYZ(RHW) : %x", g_InlineVertexBuffer_FVF); - // TODO : How to resolve this? - } - - // Start a new vertex - g_InlineVertexBuffer_TableOffset++; - // Copy all attributes of the previous vertex (if any) to the new vertex - g_InlineVertexBuffer_Table[g_InlineVertexBuffer_TableOffset] = g_InlineVertexBuffer_Table[o]; - - break; - } - - case X_D3DVSDE_BLENDWEIGHT: - { - g_InlineVertexBuffer_Table[o].Blend[0] = a; - g_InlineVertexBuffer_Table[o].Blend[1] = b; - g_InlineVertexBuffer_Table[o].Blend[2] = c; - g_InlineVertexBuffer_Table[o].Blend[3] = d; - // TODO: Test the above. - // Xbox supports up to 4 blendweights - - switch (g_InlineVertexBuffer_FVF & D3DFVF_POSITION_MASK) { - case 0: - // No position mask given yet, set it now : - g_InlineVertexBuffer_FVF |= D3DFVF_XYZB1; - // TODO: How to select blendweight D3DFVF_XYZB2 or up? - break; - case D3DFVF_XYZB1: - // These are alright - break; - default: - EmuLog(LOG_LEVEL::WARNING, "D3DDevice_SetVertexData4f unexpected FVF when processing X_D3DVSDE_BLENDWEIGHT : %x", g_InlineVertexBuffer_FVF); - g_InlineVertexBuffer_FVF &= ~D3DFVF_POSITION_MASK; // for now, remove prior position mask, leading to blending below - g_InlineVertexBuffer_FVF |= D3DFVF_XYZB1; - // TODO: How to select blendweight D3DFVF_XYZB2 or up? - // TODO : How to resolve this? - } - - break; - } - - case X_D3DVSDE_NORMAL: - { - g_InlineVertexBuffer_Table[o].Normal.x = a; - g_InlineVertexBuffer_Table[o].Normal.y = b; - g_InlineVertexBuffer_Table[o].Normal.z = c; - g_InlineVertexBuffer_FVF |= D3DFVF_NORMAL; - break; - } - - case X_D3DVSDE_DIFFUSE: - { - g_InlineVertexBuffer_Table[o].Diffuse = D3DCOLOR_COLORVALUE(a, b, c, d); - g_InlineVertexBuffer_FVF |= D3DFVF_DIFFUSE; - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_DIFFUSE, g_InlineVertexBuffer_Table[o].Diffuse); - break; - } - - case X_D3DVSDE_SPECULAR: - { - g_InlineVertexBuffer_Table[o].Specular = D3DCOLOR_COLORVALUE(a, b, c, d); - g_InlineVertexBuffer_FVF |= D3DFVF_SPECULAR; - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_SPECULAR, g_InlineVertexBuffer_Table[o].Specular); - break; - } - - case X_D3DVSDE_FOG: // Xbox extension - { - g_InlineVertexBuffer_Table[o].Fog = a; // TODO : What about the other (b, c and d) arguments? - //EmuLog(LOG_LEVEL::WARNING, "Host Direct3D8 doesn''t support FVF FOG"); - break; - } - - // Note : X_D3DVSDE_POINTSIZE: Maps to D3DFVF_PSIZE, which is not available on Xbox FVF's - - case X_D3DVSDE_BACKDIFFUSE: // Xbox extension - { - g_InlineVertexBuffer_Table[o].BackDiffuse = D3DCOLOR_COLORVALUE(a, b, c, d); - //EmuLog(LOG_LEVEL::WARNING, "Host Direct3D8 doesn''t support FVF BACKDIFFUSE"); - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKDIFFUSE, g_InlineVertexBuffer_Table[o].BackDiffuse); - break; - } - - case X_D3DVSDE_BACKSPECULAR: // Xbox extension - { - g_InlineVertexBuffer_Table[o].BackSpecular = D3DCOLOR_COLORVALUE(a, b, c, d); - //EmuLog(LOG_LEVEL::WARNING, "Host Direct3D8 doesn''t support FVF BACKSPECULAR"); - HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKSPECULAR, g_InlineVertexBuffer_Table[o].BackSpecular); - break; - } - - case X_D3DVSDE_TEXCOORD0: - { - g_InlineVertexBuffer_Table[o].TexCoord[0].x = a; - g_InlineVertexBuffer_Table[o].TexCoord[0].y = b; - g_InlineVertexBuffer_Table[o].TexCoord[0].z = c; - g_InlineVertexBuffer_Table[o].TexCoord[0].w = d; - if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX1) { - // Dxbx fix : Use mask, else the format might get expanded incorrectly : - g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; - g_InlineVertexBuffer_FVF |= D3DFVF_TEX1; - // Dxbx note : Correct usage of D3DFVF_TEX1 (and the other cases below) - // can be tested with "Daphne Xbox" (the Laserdisc Arcade Game Emulator). - } - - break; - } - - case X_D3DVSDE_TEXCOORD1: - { - g_InlineVertexBuffer_Table[o].TexCoord[1].x = a; - g_InlineVertexBuffer_Table[o].TexCoord[1].y = b; - g_InlineVertexBuffer_Table[o].TexCoord[1].z = c; - g_InlineVertexBuffer_Table[o].TexCoord[1].w = d; - if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX2) { - g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; - g_InlineVertexBuffer_FVF |= D3DFVF_TEX2; - } - - break; - } - - case X_D3DVSDE_TEXCOORD2: - { - g_InlineVertexBuffer_Table[o].TexCoord[2].x = a; - g_InlineVertexBuffer_Table[o].TexCoord[2].y = b; - g_InlineVertexBuffer_Table[o].TexCoord[2].z = c; - g_InlineVertexBuffer_Table[o].TexCoord[2].w = d; - if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX3) { - g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; - g_InlineVertexBuffer_FVF |= D3DFVF_TEX3; - } - - break; - } - - case X_D3DVSDE_TEXCOORD3: - { - g_InlineVertexBuffer_Table[o].TexCoord[3].x = a; - g_InlineVertexBuffer_Table[o].TexCoord[3].y = b; - g_InlineVertexBuffer_Table[o].TexCoord[3].z = c; - g_InlineVertexBuffer_Table[o].TexCoord[3].w = d; - if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX4) { - g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; - g_InlineVertexBuffer_FVF |= D3DFVF_TEX4; - } - - break; - } - - default: - EmuLog(LOG_LEVEL::WARNING, "Unknown IVB Register : %d", Register); - } -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexData4ub -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4ub) -( - INT Register, - BYTE a, - BYTE b, - BYTE c, - BYTE d -) -{ - LOG_FORWARD("D3DDevice_SetVertexData4f"); - - float fa = a / 255.0f; - float fb = b / 255.0f; - float fc = c / 255.0f; - float fd = d / 255.0f; - - EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, fc, fd); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexData4s -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4s) -( - INT Register, - SHORT a, - SHORT b, - SHORT c, - SHORT d -) -{ - LOG_FORWARD("D3DDevice_SetVertexData4f"); - - float fa, fb, fc, fd; - - // Test case: Halo - // Note : XQEMU verified that the int16_t arguments - // must be mapped to floats in the range [-32768.0, 32767.0] - // (See https://github.com/xqemu/xqemu/pull/176) - fa = (float)a; - fb = (float)b; - fc = (float)c; - fd = (float)d; - - EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, fc, fd); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexDataColor -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexDataColor) -( - int Register, - D3DCOLOR Color -) -{ - LOG_FORWARD("D3DDevice_SetVertexData4f"); - - D3DXCOLOR XColor = Color; - - EMUPATCH(D3DDevice_SetVertexData4f)(Register, XColor.r, XColor.g, XColor.b, XColor.a); -} - -// ****************************************************************** -// * patch: D3DDevice_End -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_End)() -{ - LOG_FUNC(); - - if(g_InlineVertexBuffer_TableOffset > 0) - EmuFlushIVB(); - - // TODO: Should technically clean this up at some point..but on XP doesnt matter much -// g_VMManager.Deallocate((VAddr)g_InlineVertexBuffer_pData); -// g_VMManager.Deallocate((VAddr)g_InlineVertexBuffer_Table); -} - -// ****************************************************************** -// * patch: D3DDevice_RunPushBuffer -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_RunPushBuffer) -( - X_D3DPushBuffer *pPushBuffer, - X_D3DFixup *pFixup -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pPushBuffer) - LOG_FUNC_ARG(pFixup) - LOG_FUNC_END; - - EmuExecutePushBuffer(pPushBuffer, pFixup); -} - -// ****************************************************************** -// * patch: D3DDevice_Clear -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_Clear) -( - DWORD Count, - CONST D3DRECT *pRects, - DWORD Flags, - D3DCOLOR Color, - float Z, - DWORD Stencil -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Count) - LOG_FUNC_ARG(pRects) - LOG_FUNC_ARG(Flags) - LOG_FUNC_ARG(Color) - LOG_FUNC_ARG(Z) - LOG_FUNC_ARG(Stencil) - LOG_FUNC_END; - - DWORD HostFlags = 0; - - // make adjustments to parameters to make sense with windows d3d - { - if (Flags & X_D3DCLEAR_TARGET) { - // TODO: D3DCLEAR_TARGET_A, *R, *G, *B don't exist on windows - if ((Flags & X_D3DCLEAR_TARGET) != X_D3DCLEAR_TARGET) - EmuLog(LOG_LEVEL::WARNING, "Unsupported : Partial D3DCLEAR_TARGET flag(s) for D3DDevice_Clear : 0x%.08X", Flags & X_D3DCLEAR_TARGET); - - HostFlags |= D3DCLEAR_TARGET; - } - - // Do not needlessly clear Z Buffer - if (Flags & X_D3DCLEAR_ZBUFFER) { - if (g_bHasDepth) - HostFlags |= D3DCLEAR_ZBUFFER; - else - EmuLog(LOG_LEVEL::WARNING, "Unsupported : D3DCLEAR_ZBUFFER flag for D3DDevice_Clear without ZBuffer"); - } - - // Only clear depth buffer and stencil if present - // - // Avoids following DirectX Debug Runtime error report - // [424] Direct3D8: (ERROR) :Invalid flag D3DCLEAR_ZBUFFER: no zbuffer is associated with device. Clear failed. - if (Flags & X_D3DCLEAR_STENCIL) { - if (g_bHasStencil) - HostFlags |= D3DCLEAR_STENCIL; - else - EmuLog(LOG_LEVEL::WARNING, "Unsupported : D3DCLEAR_STENCIL flag for D3DDevice_Clear without ZBuffer"); - } - - if(Flags & ~(X_D3DCLEAR_TARGET | X_D3DCLEAR_ZBUFFER | X_D3DCLEAR_STENCIL)) - EmuLog(LOG_LEVEL::WARNING, "Unsupported Flag(s) for D3DDevice_Clear : 0x%.08X", Flags & ~(X_D3DCLEAR_TARGET | X_D3DCLEAR_ZBUFFER | X_D3DCLEAR_STENCIL)); - } - - HRESULT hRet; - - if (pRects != nullptr) { - // Scale the fill based on our scale factor - D3DRECT rect = *pRects; - rect.x1 *= g_RenderScaleFactor; - rect.x2 *= g_RenderScaleFactor; - rect.y1 *= g_RenderScaleFactor; - rect.y2 *= g_RenderScaleFactor; - hRet = g_pD3DDevice->Clear(Count, &rect, HostFlags, Color, Z, Stencil); - } else { - hRet = g_pD3DDevice->Clear(Count, pRects, HostFlags, Color, Z, Stencil); - } - - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Clear"); -} - - -// ****************************************************************** -// * patch: D3DDevice_CopyRects -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_CopyRects) -( - X_D3DSurface* pSourceSurface, - CONST RECT* pSourceRectsArray, - UINT cRects, - X_D3DSurface* pDestinationSurface, - CONST POINT* pDestPointsArray -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pSourceSurface); - LOG_FUNC_ARG(pSourceRectsArray); - LOG_FUNC_ARG(cRects); - LOG_FUNC_ARG(pDestinationSurface); - LOG_FUNC_ARG(pDestPointsArray); - LOG_FUNC_END; - - // We skip the trampoline to prevent unnecessary work - // As our surfaces remain on the GPU, calling the trampoline would just - // result in a memcpy from an empty Xbox surface to another empty Xbox Surface - D3DSURFACE_DESC SourceDesc, DestinationDesc; - auto pHostSourceSurface = GetHostSurface(pSourceSurface); - auto pHostDestSurface = GetHostSurface(pDestinationSurface); - - if (pHostSourceSurface == nullptr || pHostDestSurface == nullptr) { - // Test Case: DOA2 attempts to copy from an index buffer resource type - // TODO: What should we do here? - LOG_TEST_CASE("D3DDevice-CopyRects: Failed to fetch host surfaces"); - return; - } - - pHostSourceSurface->GetDesc(&SourceDesc); - pHostDestSurface->GetDesc(&DestinationDesc); - - // If the source is a render-target and the destination is not, we need force it to be re-created as one - // This is because StrechRects cannot copy from a Render-Target to a Non-Render Target - // Test Case: Crash Bandicoot: Wrath of Cortex attemps to copy the render-target to a texture - // This fixes an issue on the pause screen where the screenshot of the current scene was not displayed correctly - if ((SourceDesc.Usage & D3DUSAGE_RENDERTARGET) != 0 && (DestinationDesc.Usage & D3DUSAGE_RENDERTARGET) == 0) { - pHostDestSurface = GetHostSurface(pDestinationSurface, D3DUSAGE_RENDERTARGET); - pHostDestSurface->GetDesc(&DestinationDesc); - } - - // If no rectangles were given, default to 1 (entire surface) - if (cRects == 0) { - cRects = 1; - } - - for (UINT i = 0; i < cRects; i++) { - RECT SourceRect, DestRect; - - if (pSourceRectsArray != nullptr) { - SourceRect = pSourceRectsArray[i]; - } else { - SourceRect.left = 0; - SourceRect.right = SourceDesc.Width; - SourceRect.top = 0; - SourceRect.bottom = SourceDesc.Height; - } - - if (pDestPointsArray != nullptr) { - DestRect.left = pDestPointsArray[i].x; - DestRect.right = DestRect.left + (SourceRect.right - SourceRect.left); - DestRect.top = pDestPointsArray[i].y; - DestRect.bottom = DestRect.top + (SourceRect.bottom - SourceRect.top); - } else if (pSourceRectsArray) { - DestRect = SourceRect; - } else { - DestRect.left = 0; - DestRect.right = DestinationDesc.Width; - DestRect.top = 0; - DestRect.bottom = DestinationDesc.Height; - } - - HRESULT hRet = g_pD3DDevice->StretchRect(pHostSourceSurface, &SourceRect, pHostDestSurface, &DestRect, D3DTEXF_NONE); - if (FAILED(hRet)) { - LOG_TEST_CASE("D3DDevice_CopyRects: Failed to copy surface"); - } - } -} - -#define CXBX_SWAP_PRESENT_FORWARD (256 + 4 + 1) // = CxbxPresentForwardMarker + D3DSWAP_FINISH + D3DSWAP_COPY - -// ****************************************************************** -// * patch: D3DDevice_Present -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_Present) -( - CONST RECT* pSourceRect, - CONST RECT* pDestRect, - PVOID pDummy1, - PVOID pDummy2 -) -{ - // LOG_FORWARD("D3DDevice_Swap"); - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pSourceRect) - LOG_FUNC_ARG(pDestRect) - LOG_FUNC_ARG(pDummy1) - LOG_FUNC_ARG(pDummy2) - LOG_FUNC_END; - - EMUPATCH(D3DDevice_Swap)(CXBX_SWAP_PRESENT_FORWARD); // Xbox present ignores -} - -std::chrono::time_point frameStartTime; - -// LTCG specific swap function... -// Massive hack, but could coax some more LTCG titles into booting with HLE -// This uses a custom calling convention where parameter is passed in EAX -DWORD XTL::EMUPATCH(D3DDevice_Swap_0) -( -) -{ - uint32_t param; - __asm { - mov param, eax; - } - - return EMUPATCH(D3DDevice_Swap)(param); -} - -// ****************************************************************** -// * patch: D3DDevice_Swap -// ****************************************************************** -DWORD WINAPI XTL::EMUPATCH(D3DDevice_Swap) -( - DWORD Flags -) -{ - LOG_FUNC_ONE_ARG(Flags); - - // TODO: Ensure this flag is always the same across library versions - if (Flags != 0 && Flags != CXBX_SWAP_PRESENT_FORWARD) - EmuLog(LOG_LEVEL::WARNING, "XTL::EmuD3DDevice_Swap: Flags != 0"); - - // Fetch the host backbuffer - IDirect3DSurface *pCurrentHostBackBuffer = nullptr; - HRESULT hRet = g_pD3DDevice->GetBackBuffer( - 0, // iSwapChain - 0, D3DBACKBUFFER_TYPE_MONO, &pCurrentHostBackBuffer); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer - Unable to get backbuffer surface!"); - if (hRet == D3D_OK) { - assert(pCurrentHostBackBuffer != nullptr); - - // Get backbuffer dimensions; TODO : remember this once, at creation/resize time - D3DSURFACE_DESC BackBufferDesc; - pCurrentHostBackBuffer->GetDesc(&BackBufferDesc); - - // TODO: Implement a hot-key to change the filter? - // Note: LoadSurfaceFilter Must be D3DTEXF_NONE, D3DTEXF_POINT or D3DTEXF_LINEAR - // Before StretchRects we used D3DX_FILTER_POINT here, but that gave jagged edges in Dashboard. - // Dxbx note : D3DX_FILTER_LINEAR gives a smoother image, but 'bleeds' across borders - // LoadOverlayFilter must be a D3DX filter DWORD value - const D3DTEXTUREFILTERTYPE LoadSurfaceFilter = D3DTEXF_LINEAR; - const DWORD LoadOverlayFilter = D3DX_DEFAULT; - - auto pXboxBackBufferHostSurface = GetHostSurface(g_pXbox_BackBufferSurface, D3DUSAGE_RENDERTARGET); - if (pXboxBackBufferHostSurface) { - // Blit Xbox BackBuffer to host BackBuffer - // TODO: Respect aspect ratio - hRet = g_pD3DDevice->StretchRect( - /* pSourceSurface = */ pXboxBackBufferHostSurface, - /* pSourceRect = */ nullptr, - /* pDestSurface = */ pCurrentHostBackBuffer, - /* pDestRect = */ nullptr, - /* Filter = */ LoadSurfaceFilter - ); - - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Couldn't blit Xbox BackBuffer to host BackBuffer : %X", hRet); - } - } - - // Is there an overlay to be presented too? - if (g_OverlayProxy.Surface.Common) { - X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(&g_OverlayProxy.Surface); - if (X_Format != X_D3DFMT_YUY2) { - LOG_TEST_CASE("Xbox overlay surface isn't using X_D3DFMT_YUY2"); - } - - // Interpret the Xbox overlay data (depending the color space conversion render state) - // as either YUV or RGB format (note that either one must be a 3 bytes per pixel format) - D3DFORMAT PCFormat; - // TODO : Before reading from pgraph, flush all pending push-buffer commands - switch (GET_MASK(HLE_read_NV2A_pgraph_register(NV_PGRAPH_CONTROL_0), NV_PGRAPH_CONTROL_0_CSCONVERT)) { - case 0: // = pass-through - PCFormat = D3DFMT_YUY2; - break; - case 1: // = CRYCB_TO_RGB - PCFormat = D3DFMT_YUY2; // Test-case : Turok (intro movie) - break; - case 2: // = SCRYSCB_TO_RGB - LOG_TEST_CASE("SCRYSCB_TO_RGB"); - PCFormat = D3DFMT_YUY2; - break; - default: - LOG_TEST_CASE("Unrecognized NV_PGRAPH_CONTROL_0_CSCONVERT"); - PCFormat = D3DFMT_YUY2; - break; - } - - // Blit Xbox overlay to host backbuffer - uint8_t *pOverlayData = (uint8_t*)GetDataFromXboxResource(&g_OverlayProxy.Surface); - UINT OverlayWidth, OverlayHeight, OverlayDepth, OverlayRowPitch, OverlaySlicePitch; - CxbxGetPixelContainerMeasures( - &g_OverlayProxy.Surface, - 0, // dwMipMapLevel - &OverlayWidth, &OverlayHeight, &OverlayDepth, &OverlayRowPitch, &OverlaySlicePitch); - - RECT EmuSourRect = { 0 }; - RECT EmuDestRect = { 0 }; - - if (g_OverlayProxy.SrcRect.right > 0) { - EmuSourRect = g_OverlayProxy.SrcRect; - } - else { - SetRect(&EmuSourRect, 0, 0, OverlayWidth, OverlayHeight); - } - - if (g_OverlayProxy.DstRect.right > 0) { - // If there's a destination rectangle given, copy that into our local variable : - EmuDestRect = g_OverlayProxy.DstRect; - - // Make sure to scale the values based on the difference between the Xbox and Host backbuffer - // We can't use the scale factor here because we are blitting directly to the host backbuffer - // NOT an Xbox surface! - DWORD XboxBackBufferWidth = GetPixelContainerWidth(g_pXbox_BackBufferSurface); - DWORD XboxBackBufferHeight = GetPixelContainerHeight(g_pXbox_BackBufferSurface); - - // We also need to account for any MSAA which may have enlarged the Xbox Backbuffer - float xScale, yScale; - GetMultiSampleScale(xScale, yScale); - - xScale = (float)BackBufferDesc.Width / ((float)XboxBackBufferWidth / xScale); - yScale = (float)BackBufferDesc.Height / ((float)XboxBackBufferHeight / yScale); - - EmuDestRect.top = (LONG)(EmuDestRect.top * yScale); - EmuDestRect.left = (LONG)(EmuDestRect.left * xScale); - EmuDestRect.bottom = (LONG)(EmuDestRect.bottom * yScale); - EmuDestRect.right = (LONG)(EmuDestRect.right * xScale); - } else { - // Use backbuffer width/height since that may differ from the Window size - EmuDestRect.right = BackBufferDesc.Width; - EmuDestRect.bottom = BackBufferDesc.Height; - } - - // load the YUY2 into the backbuffer - - // Limit the width and height of the output to the backbuffer dimensions. - // This will (hopefully) prevent exceptions in Blinx - The Time Sweeper - // (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/285) - { - // Use our (bounded) copy when bounds exceed : - if (EmuDestRect.right > (LONG)BackBufferDesc.Width) { - EmuDestRect.right = (LONG)BackBufferDesc.Width; - } - - if (EmuDestRect.bottom > (LONG)BackBufferDesc.Height) { - EmuDestRect.bottom = (LONG)BackBufferDesc.Height; - } - } - - // Create a temporary surface to hold the overlay - // This is faster than loading directly into the backbuffer because it offloads scaling to the GPU - // Without this, upscaling tanks the frame-rate! - IDirect3DSurface* pTemporaryOverlaySurface; - HRESULT hRet = g_pD3DDevice->CreateOffscreenPlainSurface( - OverlayWidth, - OverlayHeight, - D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, - &pTemporaryOverlaySurface, - nullptr - ); - - if (FAILED(hRet)) { - EmuLog(LOG_LEVEL::WARNING, "Couldn't create temporary overlay surface : %X", hRet); - } else { - RECT doNotScaleRect = { 0, 0, (LONG)OverlayWidth, (LONG)OverlayHeight }; - - // Use D3DXLoadSurfaceFromMemory() to do conversion, we don't stretch at this moment in time - // avoiding the need for YUY2toARGB() (might become relevant when porting to D3D9 or OpenGL) - // see https://msdn.microsoft.com/en-us/library/windows/desktop/bb172902(v=vs.85).aspx - hRet = D3DXLoadSurfaceFromMemory( - /* pDestSurface = */ pTemporaryOverlaySurface, - /* pDestPalette = */ nullptr, - /* pDestRect = */ &doNotScaleRect, - /* pSrcMemory = */ pOverlayData, // Source buffer - /* SrcFormat = */ PCFormat, - /* SrcPitch = */ OverlayRowPitch, - /* pSrcPalette = */ nullptr, - /* pSrcRect = */ &doNotScaleRect, // This parameter cannot be NULL - /* Filter = */ LoadOverlayFilter, - /* ColorKey = */ g_OverlayProxy.EnableColorKey ? g_OverlayProxy.ColorKey : 0); - - DEBUG_D3DRESULT(hRet, "D3DXLoadSurfaceFromMemory - UpdateOverlay could not convert buffer!\n"); - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Couldn't load Xbox overlay to host surface : %X", hRet); - } else { - // TODO: Respect aspect ratio - hRet = g_pD3DDevice->StretchRect( - /* pSourceSurface = */ pTemporaryOverlaySurface, - /* pSourceRect = */ &EmuSourRect, - /* pDestSurface = */ pCurrentHostBackBuffer, - /* pDestRect = */ &EmuDestRect, - /* Filter = */ LoadSurfaceFilter - ); - - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Couldn't load Xbox overlay to host back buffer : %X", hRet); - } - } - - pTemporaryOverlaySurface->Release(); - } - } - - pCurrentHostBackBuffer->Release(); - } - - g_pD3DDevice->EndScene(); - - hRet = g_pD3DDevice->Present(0, 0, 0, 0); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Present"); - - hRet = g_pD3DDevice->BeginScene(); - - // RenderStates need reapplying each frame, but can be re-used between draw calls - // This forces them to be reset - XboxRenderStates.SetDirty(); - - // Check if we need to enable our frame-limiter - DWORD presentationInverval = g_Xbox_PresentationInterval_Override > 0 ? g_Xbox_PresentationInterval_Override : g_Xbox_PresentationInterval_Default; - if ((presentationInverval != D3DPRESENT_INTERVAL_IMMEDIATE) && !g_bHack_UnlockFramerate) { - // If the last frame completed faster than the Xbox target swap rate, wait for it - - auto targetRefreshRate = 60.0f; // TODO: Read from Xbox Display Mode - - // Determine how many 'frames' worth of time we need to wait for - // This allows games that require a locked framerate (eg JSRF) to function correctly - // While allowing titles with an unlocked frame-rate to not be limited - auto multiplier = 1.0f; - switch (presentationInverval) { - case D3DPRESENT_INTERVAL_ONE: - case 0x80000001: // D3DPRESENT_INTERVAL_ONE_OR_IMMEDIATE: - multiplier = 1.0f; - break; - case D3DPRESENT_INTERVAL_TWO: - case 0x80000002: // D3DPRESENT_INTERVAL_TWO_OR_IMMEDIATE: - multiplier = 2.0f; - break; - case D3DPRESENT_INTERVAL_THREE: - multiplier = 3.0f; - break; - case D3DPRESENT_INTERVAL_FOUR: - multiplier = 4.0f; - break; - } - - auto targetDuration = std::chrono::duration(((1000.0f / targetRefreshRate) * multiplier)); - auto targetTimestamp = frameStartTime + targetDuration; - auto actualDuration = std::chrono::duration(std::chrono::steady_clock::now() - frameStartTime); - auto startTimeAjustment = actualDuration - targetDuration; - - // Only enter the wait loop if the frame took too long - if (actualDuration < targetDuration) { - // If we need to wait for a larger amount of time (>= 1 frame at 60FPS), we can just sleep - if ((targetTimestamp - std::chrono::steady_clock::now()) > std::chrono::duration(16.0)) { - std::this_thread::sleep_until(targetTimestamp); - } else { - // Otherwise, we fall-through and just keep polling - // This prevents large waits from hogging CPU power, but allows small waits/ to remain precice. - while (std::chrono::steady_clock::now() < targetTimestamp) { - ; - } - } - } - } - - frameStartTime = std::chrono::steady_clock::now(); - - UpdateFPSCounter(); - - if (Flags == CXBX_SWAP_PRESENT_FORWARD) // Only do this when forwarded from Present - { - // Put primitives per frame in the title - /*{ - char szString[64]; - - sprintf( szString, "Cxbx: PPF(%d)", g_dwPrimPerFrame ); - - SetWindowText( CxbxKrnl_hEmuParent, szString ); - - g_dwPrimPerFrame = 0; - }*/ - - // TODO : Check if this should be done at Swap-not-Present-time too : - // not really accurate because you definately dont always present on every vblank - g_Xbox_VBlankData.Swap = g_Xbox_VBlankData.VBlank; - - if (g_Xbox_VBlankData.VBlank == g_VBLastSwap + 1) - g_Xbox_VBlankData.Flags = 1; // D3DVBLANK_SWAPDONE - else - { - g_Xbox_VBlankData.Flags = 2; // D3DVBLANK_SWAPMISSED - g_Xbox_SwapData.MissedVBlanks++; - } - } - - // Handle Swap Callback function - { - g_Xbox_SwapData.Swap++; - - if(g_pXbox_SwapCallback != xbnullptr) - { - - g_pXbox_SwapCallback(&g_Xbox_SwapData); - - } - } - - DWORD result; - if (Flags == CXBX_SWAP_PRESENT_FORWARD) // Only do this when forwarded from Present - result = D3D_OK; // Present always returns success - else - result = g_Xbox_SwapData.Swap; // Swap returns number of swaps - - return result; -} - -bool IsSupportedFormat(XTL::X_D3DFORMAT X_Format, XTL::X_D3DRESOURCETYPE XboxResourceType, DWORD D3DUsage) { - // TODO : Nuance the following, because the Direct3D 8 docs states - // CheckDeviceFormat is needed when D3DUSAGE_RENDERTARGET or - // D3DUSAGE_DYNAMNIC is specified. - // Otherwise, lookup resource type and accompanying 'SupportedFormat' array - bool *pbSupportedFormats = g_bSupportsFormatTexture; - - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: { - if (D3DUsage & D3DUSAGE_RENDERTARGET) { - pbSupportedFormats = g_bSupportsFormatSurfaceRenderTarget; - } else if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - pbSupportedFormats = g_bSupportsFormatSurfaceDepthStencil; - } else { - pbSupportedFormats = g_bSupportsFormatSurface; - } - break; - } - case XTL::X_D3DRTYPE_VOLUME: { - pbSupportedFormats = g_bSupportsFormatTexture; // TODO : Complete - break; - } - case XTL::X_D3DRTYPE_TEXTURE: { - if (D3DUsage & D3DUSAGE_RENDERTARGET) { - pbSupportedFormats = g_bSupportsFormatTextureRenderTarget; - } else if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - pbSupportedFormats = g_bSupportsFormatTextureDepthStencil; - } else { - pbSupportedFormats = g_bSupportsFormatTexture; - } - break; - } - case XTL::X_D3DRTYPE_VOLUMETEXTURE: { - pbSupportedFormats = g_bSupportsFormatVolumeTexture; // TODO : Complete - break; - } - case XTL::X_D3DRTYPE_CUBETEXTURE: { - pbSupportedFormats = g_bSupportsFormatCubeTexture; // TODO : Complete - break; - } - } // switch XboxResourceType - - return pbSupportedFormats[X_Format]; -} - -// Was patch: IDirect3DResource8_Register -void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize) -{ - if (pResource == xbnullptr) - return; - - // Determine the resource type name - const char *ResourceTypeName; - XTL::X_D3DRESOURCETYPE XboxResourceType = GetXboxD3DResourceType(pResource); - - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_NONE: ResourceTypeName = "None"; break; - case XTL::X_D3DRTYPE_SURFACE: ResourceTypeName = "Surface"; break; - case XTL::X_D3DRTYPE_VOLUME: ResourceTypeName = "Volume"; break; - case XTL::X_D3DRTYPE_TEXTURE: ResourceTypeName = "Texture"; break; - case XTL::X_D3DRTYPE_VOLUMETEXTURE: ResourceTypeName = "VolumeTexture"; break; - case XTL::X_D3DRTYPE_CUBETEXTURE: ResourceTypeName = "CubeTexture"; break; - case XTL::X_D3DRTYPE_VERTEXBUFFER: ResourceTypeName = "VertexBuffer"; break; - case XTL::X_D3DRTYPE_INDEXBUFFER: ResourceTypeName = "IndexBuffer"; break; - case XTL::X_D3DRTYPE_PUSHBUFFER: ResourceTypeName = "PushBuffer"; break; - case XTL::X_D3DRTYPE_PALETTE: ResourceTypeName = "Palette"; break; - case XTL::X_D3DRTYPE_FIXUP: ResourceTypeName = "Fixup"; break; - default: - EmuLog(LOG_LEVEL::WARNING, "CreateHostResource :-> Unrecognized Xbox Resource Type 0x%.08X", XboxResourceType); - return; - } - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pResource) - LOG_FUNC_ARG(iTextureStage) - LOG_FUNC_ARG(dwSize) - LOG_FUNC_ARG(ResourceTypeName) - LOG_FUNC_END; - - // Retrieve and test the xbox resource buffer address - VAddr VirtualAddr = (VAddr)GetDataFromXboxResource(pResource); - if ((VirtualAddr & ~PHYSICAL_MAP_BASE) == 0) { - // TODO: Fix or handle this situation..? - LOG_TEST_CASE("CreateHostResource : VirtualAddr == 0"); - // This is probably an unallocated resource, mapped into contiguous memory (0x80000000 OR 0xF0000000) - EmuLog(LOG_LEVEL::WARNING, "CreateHostResource :-> %s carries no data - skipping conversion", ResourceTypeName); - return; - } - - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_NONE: { - break; - } - - case XTL::X_D3DRTYPE_SURFACE: { - XTL::X_D3DSurface *pXboxSurface = (XTL::X_D3DSurface *)pResource; - XTL::X_D3DBaseTexture *pParentXboxTexture = (pXboxSurface) ? (XTL::X_D3DBaseTexture*)pXboxSurface->Parent : xbnullptr; - - // Don't init the Parent if the Surface and Surface Parent formats differ - // Happens in some Outrun 2006 SetRenderTarget calls - if (pParentXboxTexture && (pXboxSurface->Format == pParentXboxTexture->Format)) { - // For surfaces with a parent texture, map these to a host texture first - // TODO : Investigate how it's possible (and how we could fix) the case when - // the following call to GetHostBaseTexture would reject non-texture resources, - // which would seem to trigger a "CreateCubeTexture Failed!" regression. - IDirect3DBaseTexture *pParentHostBaseTexture = GetHostBaseTexture(pParentXboxTexture, D3DUsage, iTextureStage); - IDirect3DSurface* pNewHostSurface; - switch (pParentHostBaseTexture->GetType()) { - case D3DRTYPE_VOLUMETEXTURE: { - LOG_TEST_CASE("Using child surface of VolumeTexture"); - // TODO - break; - } - case D3DRTYPE_CUBETEXTURE: { - // test-case : Burnout - auto pParentHostTexture = (IDirect3DCubeTexture*)pParentHostBaseTexture; - - D3DCUBEMAP_FACES CubeMapFace = D3DCUBEMAP_FACE_POSITIVE_X; - UINT SurfaceLevel = 0; - GetSurfaceFaceAndLevelWithinTexture(pXboxSurface, pParentXboxTexture, SurfaceLevel, CubeMapFace); - - HRESULT hRet = pParentHostTexture->GetCubeMapSurface(CubeMapFace, SurfaceLevel, &pNewHostSurface); - - DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetCubeMapSurface"); - if (hRet == D3D_OK) { - SetHostSurface(pXboxSurface, pNewHostSurface, iTextureStage); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created CubeTexture surface level (Face: %u, Level: %u, pResource: 0x%.08X, pNewHostSurface: 0x%.08X)", - CubeMapFace, SurfaceLevel, pResource, pNewHostSurface); - return; - } - - break; - } - case D3DRTYPE_TEXTURE: { - IDirect3DTexture* pParentHostTexture = (IDirect3DTexture*)pParentHostBaseTexture; - - UINT SurfaceLevel = 0; - GetSurfaceFaceAndLevelWithinTexture(pXboxSurface, pParentXboxTexture, SurfaceLevel); - HRESULT hRet = pParentHostTexture->GetSurfaceLevel(SurfaceLevel, &pNewHostSurface); - - DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetSurfaceLevel"); - if (hRet == D3D_OK) { - SetHostSurface(pXboxSurface, pNewHostSurface, iTextureStage); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created Texture surface level (Level: %u, pResource: 0x%.08X, pNewHostSurface: 0x%.08X)", - SurfaceLevel, pResource, pNewHostSurface); - return; - } - break; - } - } - - EmuLog(LOG_LEVEL::WARNING, "Failed getting host surface level - falling through to regular surface creation"); - } - // fall through - } - case XTL::X_D3DRTYPE_VOLUME: { - // Note : Use and check for null, since X_D3DRTYPE_SURFACE might fall through here (by design) - XTL::X_D3DVolume *pXboxVolume = (XboxResourceType == XTL::X_D3DRTYPE_VOLUME) ? (XTL::X_D3DVolume *)pResource : xbnullptr; - XTL::X_D3DVolumeTexture *pParentXboxVolumeTexture = (pXboxVolume) ? (XTL::X_D3DVolumeTexture *)pXboxVolume->Parent : xbnullptr; - if (pParentXboxVolumeTexture) { - // For volumes with a parent volume texture, map these to a host volume texture first - IDirect3DVolumeTexture *pParentHostVolumeTexture = GetHostVolumeTexture(pParentXboxVolumeTexture, iTextureStage); - UINT VolumeLevel = 0; // TODO : Derive actual level based on pXboxVolume->Data delta to pParentXboxVolumeTexture->Data - IDirect3DVolume *pNewHostVolume; - HRESULT hRet = pParentHostVolumeTexture->GetVolumeLevel(VolumeLevel, &pNewHostVolume); - DEBUG_D3DRESULT(hRet, "pParentHostVolumeTexture->GetVolumeLevel"); - if (hRet == D3D_OK) { - SetHostVolume(pXboxVolume, pNewHostVolume, iTextureStage); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created volume level (%u, 0x%.08X, 0x%.08X)", - VolumeLevel, pResource, pNewHostVolume); - return; - } - - EmuLog(LOG_LEVEL::WARNING, "Failed getting host volume level - falling through to regular volume creation"); - } - // fall through - } - case XTL::X_D3DRTYPE_TEXTURE: - case XTL::X_D3DRTYPE_VOLUMETEXTURE: - case XTL::X_D3DRTYPE_CUBETEXTURE: { - XTL::X_D3DPixelContainer *pPixelContainer = (XTL::X_D3DPixelContainer*)pResource; - XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer); - D3DPOOL D3DPool = D3DPOOL_DEFAULT; // Was: D3DPOOL_MANAGED TODO : Nuance D3DPOOL where/when needed - - if (EmuXBFormatIsDepthBuffer(X_Format)) { - D3DUsage |= D3DUSAGE_DEPTHSTENCIL; - } - else if (pPixelContainer == g_pXbox_RenderTarget) { - if (EmuXBFormatIsRenderTarget(X_Format)) - D3DUsage |= D3DUSAGE_RENDERTARGET; - else - EmuLog(LOG_LEVEL::WARNING, "Updating RenderTarget %s with an incompatible format!", ResourceTypeName); - } - - // Determine the format we'll be using on host D3D - D3DFORMAT PCFormat; - bool bConvertToARGB = false; - - if (EmuXBFormatRequiresConversionToARGB(X_Format)) { - bConvertToARGB = true; - PCFormat = D3DFMT_A8R8G8B8; - - // Unset D3DUSAGE_DEPTHSTENCIL: It's not possible for ARGB textures to be depth stencils - // Fixes CreateTexture error in Virtua Cop 3 (Chihiro) - D3DUsage &= ~D3DUSAGE_DEPTHSTENCIL; - } - else { - // Does host CheckDeviceFormat() succeed on this format? - if (IsSupportedFormat(X_Format, XboxResourceType, D3DUsage)) { - // Then use matching host format - PCFormat = EmuXB2PC_D3DFormat(X_Format); - - // If, and ONLY if this is the default backbuffer, make sure the format matches the host backbuffer - if (pResource == g_pXbox_BackBufferSurface) { - PCFormat = g_EmuCDPD.HostPresentationParameters.BackBufferFormat; - } - } - else { - if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - // If it was a depth stencil, fall back to a known supported depth format - EmuLog(LOG_LEVEL::WARNING, "Xbox %s Format %x will be converted to D3DFMT_D24S8", ResourceTypeName, X_Format); - PCFormat = D3DFMT_D24S8; - } else if (EmuXBFormatCanBeConvertedToARGB(X_Format)) { - EmuLog(LOG_LEVEL::WARNING, "Xbox %s Format %x will be converted to ARGB", ResourceTypeName, X_Format); - bConvertToARGB = true; - PCFormat = D3DFMT_A8R8G8B8; - } else { - // Otherwise, use a best matching format - /*CxbxKrnlCleanup*/EmuLog(LOG_LEVEL::WARNING, "Encountered a completely incompatible %s format!", ResourceTypeName); - PCFormat = EmuXB2PC_D3DFormat(X_Format); - } - } - } - - // Update D3DPool accordingly to the active D3DUsage flags - if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - D3DPool = D3DPOOL_DEFAULT; - } - if (D3DUsage & D3DUSAGE_RENDERTARGET) { - D3DPool = D3DPOOL_DEFAULT; - } - if (D3DUsage & D3DUSAGE_DYNAMIC) { - D3DPool = D3DPOOL_DEFAULT; - } - - // Interpret Width/Height/BPP - bool bCubemap = pPixelContainer->Format & X_D3DFORMAT_CUBEMAP; - bool bSwizzled = EmuXBFormatIsSwizzled(X_Format); - bool bCompressed = EmuXBFormatIsCompressed(X_Format); - DWORD dwMinSize = (bCompressed) ? 4 : 1; - UINT dwBPP = EmuXBFormatBytesPerPixel(X_Format); - UINT dwMipMapLevels = CxbxGetPixelContainerMipMapLevels(pPixelContainer); - UINT dwWidth, dwHeight, dwDepth, dwRowPitch, dwSlicePitch; - - // Interpret Width/Height/BPP - CxbxGetPixelContainerMeasures(pPixelContainer, 0, &dwWidth, &dwHeight, &dwDepth, &dwRowPitch, &dwSlicePitch); - - // Each mip-map level is 1/2 the size of the previous level - // D3D9 forbids creation of a texture with more mip-map levels than it is divisible - // EG: A 256x256 texture cannot have more than 8 levels, since that would create a texture smaller than 1x1 - // Because of this, we need to cap dwMipMapLevels when required - if (dwMipMapLevels > 0) { - // Calculate how many mip-map levels it takes to get to a texture of 1 pixels in either dimension - UINT highestMipMapLevel = 1; - UINT width = dwWidth; UINT height = dwHeight; - while (width > 1 || height > 1) { - width /= 2; - height /= 2; - highestMipMapLevel++; - } - - // If the desired mip-map level was higher than the maximum possible, cap it - // Test case: Shin Megami Tensei: Nine - if (dwMipMapLevels > highestMipMapLevel) { - LOG_TEST_CASE("Too many mip-map levels"); - dwMipMapLevels = highestMipMapLevel; - } - } - - if (dwDepth != 1) { - LOG_TEST_CASE("CreateHostResource : Depth != 1"); - } - - // The following is necessary for DXT* textures (4x4 blocks minimum) - // TODO: Figure out if this is necessary under other circumstances? - if (bCompressed) { - if (dwWidth < dwMinSize) { - LOG_TEST_CASE("CreateHostResource : dwWidth < dwMinSize"); - EmuLog(LOG_LEVEL::WARNING, "Expanding %s width (%d->%d)", ResourceTypeName, dwWidth, dwMinSize); - dwWidth = dwMinSize; - } - - if (dwHeight < dwMinSize) { - LOG_TEST_CASE("CreateHostResource : dwHeight < dwMinSize"); - EmuLog(LOG_LEVEL::WARNING, "Expanding %s height (%d->%d)", ResourceTypeName, dwHeight, dwMinSize); - dwHeight = dwMinSize; - } - } - - // One of these will be created : each also has an intermediate copy to allow UpdateTexture to work - // This means we don't need to lock the GPU resource anymore, so we can use D3DPOOL_DEFAULT to allow Stretch/CopyRects to work! - IDirect3DSurface *pNewHostSurface = nullptr; // for X_D3DRTYPE_SURFACE - IDirect3DVolume *pNewHostVolume = nullptr; // for X_D3DRTYPE_VOLUME - IDirect3DTexture *pNewHostTexture = nullptr; // for X_D3DRTYPE_TEXTURE - IDirect3DTexture *pIntermediateHostTexture = nullptr; - IDirect3DVolumeTexture *pNewHostVolumeTexture = nullptr; // for X_D3DRTYPE_VOLUMETEXTURE - IDirect3DVolumeTexture *pIntermediateHostVolumeTexture = nullptr; - IDirect3DCubeTexture *pNewHostCubeTexture = nullptr; // for X_D3DRTYPE_CUBETEXTURE - IDirect3DCubeTexture *pIntermediateHostCubeTexture = nullptr; - - HRESULT hRet; - - // Create the surface/volume/(volume/cube/)texture - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: { - if (D3DUsage & D3DUSAGE_RENDERTARGET) { - hRet = g_pD3DDevice->CreateRenderTarget(dwWidth * g_RenderScaleFactor, dwHeight * g_RenderScaleFactor, PCFormat, - g_EmuCDPD.HostPresentationParameters.MultiSampleType, - 0, // MultisampleQuality - true, // Lockable - &pNewHostSurface, - nullptr // pSharedHandle - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateRenderTarget"); - } else - if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - hRet = g_pD3DDevice->CreateDepthStencilSurface(dwWidth * g_RenderScaleFactor, dwHeight * g_RenderScaleFactor, PCFormat, - g_EmuCDPD.HostPresentationParameters.MultiSampleType, - 0, // MultisampleQuality - false, // Discard - &pNewHostSurface, - nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateDepthStencilSurface"); - } - else { - D3DPool = D3DPOOL_SYSTEMMEM; - hRet = g_pD3DDevice->CreateOffscreenPlainSurface(dwWidth, dwHeight, PCFormat, D3DPool, &pNewHostSurface, nullptr); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateOffscreenPlainSurface"); - } - - // First fail, retry with a fallback format - // If this succeeds, the surface may not render correctly, but it won't crash - if (hRet != D3D_OK) { - if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - EmuLog(LOG_LEVEL::WARNING, "CreateDepthStencilSurface Failed\n\nError: %s\nDesc: %s", - DXGetErrorString(hRet), DXGetErrorDescription(hRet)); - } - else { - EmuLog(LOG_LEVEL::WARNING, "CreateImageSurface Failed\n\nError: %s\nDesc: %s", - DXGetErrorString(hRet), DXGetErrorDescription(hRet)); - } - - EmuLog(LOG_LEVEL::WARNING, "Trying Fallback"); - hRet = g_pD3DDevice->CreateOffscreenPlainSurface(dwWidth, dwHeight, PCFormat, D3DPool, &pNewHostSurface, nullptr); - } - - // If the fallback failed, show an error and exit execution. - if (hRet != D3D_OK) { - // We cannot safely continue in this state. - CxbxKrnlCleanup("CreateImageSurface Failed!\n\nError: %s\nDesc: %s", - DXGetErrorString(hRet), DXGetErrorDescription(hRet)); - } - - SetHostSurface(pResource, pNewHostSurface, iTextureStage); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", - ResourceTypeName, pResource, pNewHostSurface); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Width : %d, Height : %d, Format : %d", - dwWidth, dwHeight, PCFormat); - break; - } - - case XTL::X_D3DRTYPE_VOLUME: { - LOG_UNIMPLEMENTED(); - // Note : Host D3D can only(?) retrieve a volume like this : - // hRet = pNewHostVolumeTexture->GetVolumeLevel(level, &pNewHostVolume); - // So, we need to do this differently - we need to step up to the containing VolumeTexture, - // and retrieve and convert all of it's GetVolumeLevel() slices. - pNewHostVolume = nullptr; - // SetHostVolume(pResource, pNewHostVolume, iTextureStage); - // EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", - // ResourceTypeName, pResource, pNewHostVolume); - break; - } - - case XTL::X_D3DRTYPE_TEXTURE: { - hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, - D3DUsage, PCFormat, D3DPool, &pNewHostTexture, - nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture"); - - // If the above failed, we might be able to use an ARGB texture instead - if ((hRet != D3D_OK) && (PCFormat != D3DFMT_A8R8G8B8) && EmuXBFormatCanBeConvertedToARGB(X_Format)) { - hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, - D3DUsage, D3DFMT_A8R8G8B8, D3DPool, &pNewHostTexture, - nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture(D3DFMT_A8R8G8B8)"); - - if (hRet == D3D_OK) { - // Okay, now this works, make sure the texture gets converted - bConvertToARGB = true; - PCFormat = D3DFMT_A8R8G8B8; - } - } - - // Now create our intermediate texture for UpdateTexture, but not for render targets or depth stencils - if (hRet == D3D_OK && (D3DUsage & D3DUSAGE_RENDERTARGET) == 0 && (D3DUsage & D3DUSAGE_DEPTHSTENCIL) == 0) { - hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, - 0, PCFormat, D3DPOOL_SYSTEMMEM, &pIntermediateHostTexture, - nullptr - ); - } - - /*if(FAILED(hRet)) - { - hRet = g_pD3DDevice->CreateTexture - ( - dwWidth, dwHeight, dwMipMapLevels, D3DUsage, PCFormat, - D3DPOOL_SYSTEMMEM, &pNewHostTexture, - nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture(D3DPOOL_SYSTEMMEM)"); - }*/ - - - if (hRet != D3D_OK) { - CxbxKrnlCleanup("CreateTexture Failed!\n\n" - "Error: 0x%X\nFormat: %d\nDimensions: %dx%d", hRet, PCFormat, dwWidth, dwHeight); - } - - SetHostTexture(pResource, pNewHostTexture, iTextureStage); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", - ResourceTypeName, pResource, pNewHostTexture); - break; - } - - case XTL::X_D3DRTYPE_VOLUMETEXTURE: { - hRet = g_pD3DDevice->CreateVolumeTexture(dwWidth, dwHeight, dwDepth, - dwMipMapLevels, D3DUsage, PCFormat, D3DPool, &pNewHostVolumeTexture, - nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateVolumeTexture"); - - // Now create our intermediate texture for UpdateTexture, but not for render targets or depth stencils - if (hRet == D3D_OK && (D3DUsage & D3DUSAGE_RENDERTARGET) == 0 && (D3DUsage & D3DUSAGE_DEPTHSTENCIL) == 0) { - hRet = g_pD3DDevice->CreateVolumeTexture(dwWidth, dwHeight, dwDepth, - dwMipMapLevels, 0, PCFormat, D3DPOOL_SYSTEMMEM, &pIntermediateHostVolumeTexture, - nullptr - ); - } - - if (hRet != D3D_OK) { - CxbxKrnlCleanup("CreateVolumeTexture Failed!\n\nError: %s\nDesc: %s", - DXGetErrorString(hRet), DXGetErrorDescription(hRet)); - } - - SetHostVolumeTexture(pResource, pNewHostVolumeTexture, iTextureStage); - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", - ResourceTypeName, pResource, pNewHostVolumeTexture); - break; - } - - case XTL::X_D3DRTYPE_CUBETEXTURE: { - EmuLog(LOG_LEVEL::DEBUG, "CreateCubeTexture(%d, %d, 0, %d, D3DPOOL_MANAGED)", dwWidth, - dwMipMapLevels, PCFormat); - - hRet = g_pD3DDevice->CreateCubeTexture(dwWidth, dwMipMapLevels, D3DUsage, - PCFormat, D3DPool, &pNewHostCubeTexture, - nullptr - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateCubeTexture"); - - // Now create our intermediate texture for UpdateTexture, but not for render targets or depth stencils - if (hRet == D3D_OK && (D3DUsage & D3DUSAGE_RENDERTARGET) == 0 && (D3DUsage & D3DUSAGE_DEPTHSTENCIL) == 0) { - hRet = g_pD3DDevice->CreateCubeTexture(dwWidth, dwMipMapLevels, 0, - PCFormat, D3DPOOL_SYSTEMMEM, &pIntermediateHostCubeTexture, - nullptr - ); - } - - if (hRet != D3D_OK) { - CxbxKrnlCleanup("CreateCubeTexture Failed!\n\nError: \nDesc: "/*, - DXGetErrorString(hRet), DXGetErrorDescription(hRet)*/); - } - - SetHostCubeTexture(pResource, pNewHostCubeTexture, iTextureStage); - // TODO : Cube face surfaces can be used as a render-target, - // so we need to associate host surfaces to each surface of this cube texture - // However, we can't do it here: On Xbox, a new Surface is created on every call to - // GetCubeMapSurface, so it needs to be done at surface conversion time by looking up - // the parent CubeTexture - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", - ResourceTypeName, pResource, pNewHostCubeTexture); - break; - } - } // switch XboxResourceType - - // If this resource is a render target or depth stencil, don't attempt to lock/copy it as it won't work anyway - // In this case, we simply return - if (D3DUsage & D3DUSAGE_RENDERTARGET || D3DUsage & D3DUSAGE_DEPTHSTENCIL) { - return; - } - - DWORD D3DLockFlags = D3DLOCK_NOSYSLOCK; - - DWORD dwCubeFaceOffset = 0; - DWORD dwCubeFaceSize = 0; - D3DCUBEMAP_FACES last_face = (bCubemap) ? D3DCUBEMAP_FACE_NEGATIVE_Z : D3DCUBEMAP_FACE_POSITIVE_X; - for (int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= last_face; face++) { - // As we iterate through mipmap levels, we'll adjust the source resource offset - DWORD dwMipOffset = 0; - DWORD dwMipWidth = dwWidth; - DWORD dwMipHeight = dwHeight; - DWORD dwMipDepth = dwDepth; - DWORD dwMipRowPitch = dwRowPitch; - DWORD dwSrcSlicePitch = dwMipRowPitch * dwMipHeight; // TODO - - for (unsigned int mipmap_level = 0; mipmap_level < dwMipMapLevels; mipmap_level++) { - - // Calculate size of this mipmap level - DWORD dwMipSize = dwMipRowPitch * dwMipHeight; - if (bCompressed) { - dwMipSize /= 4; - } - - // Lock the host resource - D3DLOCKED_RECT LockedRect = {}; - D3DLOCKED_BOX LockedBox = {}; - - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: - hRet = pNewHostSurface->LockRect(&LockedRect, nullptr, D3DLockFlags); - break; - case XTL::X_D3DRTYPE_VOLUME: - hRet = pNewHostVolume->LockBox(&LockedBox, nullptr, D3DLockFlags); - break; - case XTL::X_D3DRTYPE_TEXTURE: - hRet = pIntermediateHostTexture->LockRect(mipmap_level, &LockedRect, nullptr, D3DLockFlags); - break; - case XTL::X_D3DRTYPE_VOLUMETEXTURE: - hRet = pIntermediateHostVolumeTexture->LockBox(mipmap_level, &LockedBox, nullptr, D3DLockFlags); - break; - case XTL::X_D3DRTYPE_CUBETEXTURE: - hRet = pIntermediateHostCubeTexture->LockRect((D3DCUBEMAP_FACES)face, mipmap_level, &LockedRect, nullptr, D3DLockFlags); - break; - default: - assert(false); - } // switch XboxResourceType - - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Locking host %s failed!", ResourceTypeName); - continue; // This often happens on depth-stencil surfaces - let's ignore their copies for now - } - - // Determine destination buffer attributes - uint8_t *pDst; - DWORD dwDstRowPitch; - DWORD dwDstSlicePitch; - - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_VOLUME: - case XTL::X_D3DRTYPE_VOLUMETEXTURE: - pDst = (uint8_t *)LockedBox.pBits; - dwDstRowPitch = LockedBox.RowPitch; - dwDstSlicePitch = LockedBox.SlicePitch; - break; - default: - pDst = (uint8_t *)LockedRect.pBits; - dwDstRowPitch = LockedRect.Pitch; - dwDstSlicePitch = 0; - } - - uint8_t *pSrc = (uint8_t *)VirtualAddr + dwMipOffset; - - // Do we need to convert to ARGB? - if (bConvertToARGB) { - EmuLog(LOG_LEVEL::DEBUG, "Unsupported texture format, expanding to D3DFMT_A8R8G8B8"); - - // Convert a row at a time, using a libyuv-like callback approach : - if (!ConvertD3DTextureToARGBBuffer( - X_Format, - pSrc, dwMipWidth, dwMipHeight, dwMipRowPitch, dwSrcSlicePitch, - pDst, dwDstRowPitch, dwDstSlicePitch, - dwDepth, - iTextureStage)) { - CxbxKrnlCleanup("Unhandled conversion!"); - } - } - else if (bSwizzled) { - // First we need to unswizzle the texture data - EmuUnswizzleBox( - pSrc, dwMipWidth, dwMipHeight, dwMipDepth, - dwBPP, - pDst, dwDstRowPitch, dwDstSlicePitch - ); - } - else if (bCompressed) { - memcpy(pDst, pSrc, dwMipSize); - } - else { - /* TODO : // Let DirectX convert the surface (including palette formats) : - if(!EmuXBFormatRequiresConversionToARGB) { - D3DXLoadSurfaceFromMemory( - GetHostSurface(pResource), - nullptr, // no destination palette - &destRect, - pSrc, // Source buffer - dwMipPitch, // Source pitch - g_pXbox_Palette_Data, - &SrcRect, - D3DX_DEFAULT, // D3DX_FILTER_NONE, - 0 // No ColorKey? - ); - } else { - */ - if ((dwDstRowPitch == dwMipRowPitch) && (dwMipRowPitch == dwMipWidth * dwBPP)) { - memcpy(pDst, pSrc, dwMipSize); - } - else { - for (DWORD v = 0; v < dwMipHeight; v++) { - memcpy(pDst, pSrc, dwMipWidth * dwBPP); - pDst += dwDstRowPitch; - pSrc += dwMipRowPitch; - } - } - } - - // Unlock the host resource - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: - hRet = pNewHostSurface->UnlockRect(); - break; - case XTL::X_D3DRTYPE_VOLUME: - hRet = pNewHostVolume->UnlockBox(); - break; - case XTL::X_D3DRTYPE_TEXTURE: - hRet = pIntermediateHostTexture->UnlockRect(mipmap_level); - break; - case XTL::X_D3DRTYPE_VOLUMETEXTURE: - hRet = pIntermediateHostVolumeTexture->UnlockBox(mipmap_level); - break; - case XTL::X_D3DRTYPE_CUBETEXTURE: - hRet = pIntermediateHostCubeTexture->UnlockRect((D3DCUBEMAP_FACES)face, mipmap_level); - break; - default: - assert(false); - } - - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Unlocking host %s failed!", ResourceTypeName); - } - - if (face == D3DCUBEMAP_FACE_POSITIVE_X) { - dwCubeFaceSize += dwDepth * dwMipSize; - } - - // Calculate the next mipmap level dimensions - dwMipOffset += dwMipSize; - if (dwMipWidth > dwMinSize) { - dwMipWidth /= 2; - dwMipRowPitch /= 2; - } - - if (dwMipHeight > dwMinSize) { - dwMipHeight /= 2; - } - - if (dwMipDepth > 1) { - dwMipDepth /= 2; - } - } // for mipmap levels - - if (face == D3DCUBEMAP_FACE_POSITIVE_X) { - dwCubeFaceSize = ROUND_UP(dwCubeFaceSize, X_D3DTEXTURE_CUBEFACE_ALIGNMENT); - } - - dwCubeFaceOffset += dwCubeFaceSize; - } // for cube faces - - - // Copy from the intermediate resource to the final host resource - // This is necessary because CopyRects/StretchRects only works on resources in the DEFAULT pool - // But resources in this pool are not lockable: We must use UpdateSurface/UpdateTexture instead! - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: - case XTL::X_D3DRTYPE_VOLUME: - // We didn't use a copy for Surfaces or Volumes - break; - case XTL::X_D3DRTYPE_TEXTURE: - g_pD3DDevice->UpdateTexture(pIntermediateHostTexture, pNewHostTexture); - pIntermediateHostTexture->Release(); - break; - case XTL::X_D3DRTYPE_VOLUMETEXTURE: - g_pD3DDevice->UpdateTexture(pIntermediateHostVolumeTexture, pNewHostVolumeTexture); - pIntermediateHostVolumeTexture->Release(); - break; - case XTL::X_D3DRTYPE_CUBETEXTURE: - g_pD3DDevice->UpdateTexture(pIntermediateHostCubeTexture, pNewHostCubeTexture); - pIntermediateHostCubeTexture->Release(); - break; - default: - assert(false); - } - - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Updating host %s failed!", ResourceTypeName); - } - - // Debug resource dumping -//#define _DEBUG_DUMP_TEXTURE_REGISTER "D:\\" -#ifdef _DEBUG_DUMP_TEXTURE_REGISTER - bool bDumpConvertedTextures = true; // TODO : Make this a runtime changeable setting - if (bDumpConvertedTextures) { - char szFilePath[MAX_PATH]; - - switch (XboxResourceType) { - case XTL::X_D3DRTYPE_SURFACE: { - static int dwDumpSurface = 0; - sprintf(szFilePath, _DEBUG_DUMP_TEXTURE_REGISTER "%.03d-Surface%.03d.dds", X_Format, dwDumpSurface++); - D3DXSaveSurfaceToFileA(szFilePath, D3DXIFF_DDS, pNewHostSurface, nullptr, nullptr); - break; - } - case XTL::X_D3DRTYPE_VOLUME: { - // TODO - break; - } - case XTL::X_D3DRTYPE_TEXTURE: { - static int dwDumpTexure = 0; - sprintf(szFilePath, _DEBUG_DUMP_TEXTURE_REGISTER "%.03d-Texture%.03d.dds", X_Format, dwDumpTexure++); - D3DXSaveTextureToFileA(szFilePath, D3DXIFF_DDS, pNewHostTexture, nullptr); - break; - } - case XTL::X_D3DRTYPE_VOLUMETEXTURE: { - // TODO - break; - } - case XTL::X_D3DRTYPE_CUBETEXTURE: { - static int dwDumpCubeTexture = 0; - for (unsigned int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= D3DCUBEMAP_FACE_NEGATIVE_Z; face++) { - IDirect3DSurface *pSurface; - if (D3D_OK == pNewHostCubeTexture->GetCubeMapSurface((D3DCUBEMAP_FACES)face, 0, &pSurface)) { - sprintf(szFilePath, _DEBUG_DUMP_TEXTURE_REGISTER "%.03d-CubeTexure%.03d-%d.dds", X_Format, dwDumpCubeTexture, face); - D3DXSaveSurfaceToFileA(szFilePath, D3DXIFF_DDS, pSurface, nullptr, nullptr); - pSurface->Release(); - } - } - dwDumpCubeTexture++; - break; - } - } // switch XboxResourceType - } -#endif - - break; - } - - // case X_D3DRTYPE_VERTEXBUFFER: { break; } // TODO - - // case X_D3DRTYPE_INDEXBUFFER: { break; } // TODO - - case XTL::X_D3DRTYPE_PUSHBUFFER: { - XTL::X_D3DPushBuffer *pPushBuffer = (XTL::X_D3DPushBuffer*)pResource; - - // create push buffer - dwSize = g_VMManager.QuerySize(VirtualAddr); - if (dwSize == 0) { - // TODO: once this is known to be working, remove the warning - EmuLog(LOG_LEVEL::WARNING, "Push buffer allocation size unknown"); - pPushBuffer->Lock = X_D3DRESOURCE_LOCK_FLAG_NOSIZE; - break; - } - - EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %S (0x%.08X, 0x%.08X, 0x%.08X)", ResourceTypeName, pResource->Data, pPushBuffer->Size, pPushBuffer->AllocationSize); - break; - } - - //case X_D3DRTYPE_PALETTE: { break; } - - case XTL::X_D3DRTYPE_FIXUP: { - XTL::X_D3DFixup *pFixup = (XTL::X_D3DFixup*)pResource; - - EmuLog(LOG_LEVEL::WARNING, "X_D3DRTYPE_FIXUP is not yet supported\n" - "0x%.08X (pFixup->Common) \n" - "0x%.08X (pFixup->Data) \n" - "0x%.08X (pFixup->Lock) \n" - "0x%.08X (pFixup->Run) \n" - "0x%.08X (pFixup->Next) \n" - "0x%.08X (pFixup->Size) \n", pFixup->Common, pFixup->Data, pFixup->Lock, pFixup->Run, pFixup->Next, pFixup->Size); - break; - } - } // switch XboxResourceType -} - -// ****************************************************************** -// * patch: D3DDevice_EnableOverlay -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_EnableOverlay) -( - BOOL Enable -) -{ - LOG_FUNC_ONE_ARG(Enable); - - // The Xbox D3DDevice_EnableOverlay call merely resets the active - // NV2A overlay state, it doesn't actually enable or disable anything. - // Thus, we should just reset our overlay state here too. A title will - // show new overlay data via D3DDevice_UpdateOverlay (see below). - g_OverlayProxy = {}; -} - -// ****************************************************************** -// * patch: D3DDevice_UpdateOverlay -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_UpdateOverlay) -( - X_D3DSurface *pSurface, - CONST RECT *SrcRect, - CONST RECT *DstRect, - BOOL EnableColorKey, - D3DCOLOR ColorKey -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pSurface) - LOG_FUNC_ARG(SrcRect) - LOG_FUNC_ARG(DstRect) - LOG_FUNC_ARG(EnableColorKey) - LOG_FUNC_ARG(ColorKey) - LOG_FUNC_END; - - // Reset and remember the overlay arguments, so our D3DDevice_Swap patch - // can correctly show this overlay surface data. - g_OverlayProxy = {}; - if (pSurface) { - g_OverlayProxy.Surface = *pSurface; - if (SrcRect) - g_OverlayProxy.SrcRect = *SrcRect; - - if (DstRect) - g_OverlayProxy.DstRect = *DstRect; - - g_OverlayProxy.EnableColorKey = EnableColorKey; - g_OverlayProxy.ColorKey = ColorKey; - // Update overlay if present was not called since the last call to - // EmuD3DDevice_UpdateOverlay. - if (g_OverlaySwap != g_Xbox_SwapData.Swap - 1) { - EMUPATCH(D3DDevice_Swap)(CXBX_SWAP_PRESENT_FORWARD); - } - - g_OverlaySwap = g_Xbox_SwapData.Swap; - } -} - -// ****************************************************************** -// * patch: D3DDevice_GetOverlayUpdateStatus -// ****************************************************************** -BOOL WINAPI XTL::EMUPATCH(D3DDevice_GetOverlayUpdateStatus)() -{ - LOG_FUNC(); - - LOG_UNIMPLEMENTED(); - - // TODO: Actually check for update status - return TRUE; -} - -// ****************************************************************** -// * patch: D3DDevice_BlockUntilVerticalBlank -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)() -{ - LOG_FUNC(); - - std::unique_lock lk(g_VBConditionMutex); - g_VBConditionVariable.wait(lk); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVerticalBlankCallback -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVerticalBlankCallback) -( - X_D3DVBLANKCALLBACK pCallback -) -{ - LOG_FUNC_ONE_ARG(pCallback); - - g_pXbox_VerticalBlankCallback = pCallback; -} - - -// ****************************************************************** -// * patch: D3DDevice_SetRenderState_Simple -// ****************************************************************** -VOID __fastcall XTL::EMUPATCH(D3DDevice_SetRenderState_Simple) -( - DWORD Method, - DWORD Value -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Method) - LOG_FUNC_ARG(Value) - LOG_FUNC_END; - - XB_TRMP(D3DDevice_SetRenderState_Simple)(Method, Value); - - // Fetch the RenderState conversion info for the given input - int XboxRenderStateIndex = -1; - for (int i = X_D3DRS_FIRST; i <= X_D3DRS_LAST; i++) { - if (GetDxbxRenderStateInfo(i).M == PUSH_METHOD(Method)) { - XboxRenderStateIndex = i; - break; - } - } - - // If we could not map it, log and return - if (XboxRenderStateIndex == -1) { - EmuLog(LOG_LEVEL::WARNING, "RenderState_Simple(0x%.08X (%s), 0x%.08X) could not be found in RenderState table", Method, GetDxbxRenderStateInfo(XboxRenderStateIndex).S, Value); - return; - } - - EmuLog(LOG_LEVEL::DEBUG, "RenderState_Simple: %s = 0x%08X", GetDxbxRenderStateInfo(XboxRenderStateIndex).S, Value); - - XboxRenderStates.SetXboxRenderState(XboxRenderStateIndex, Value); -} - -// LTCG specific D3DDevice_SetTransform function... -// This uses a custom calling convention where parameter is passed in EAX, EDX -VOID __stdcall XTL::EMUPATCH(D3DDevice_SetTransform_0) -( -) -{ - D3DTRANSFORMSTATETYPE param1; - CONST D3DMATRIX *param2; - - __asm { - mov param1, eax - mov param2, edx - } - - return EMUPATCH(D3DDevice_SetTransform)(param1, param2); -} - -// ****************************************************************** -// * patch: D3DDevice_SetTransform -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetTransform) -( - D3DTRANSFORMSTATETYPE State, - CONST D3DMATRIX *pMatrix -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(State) - LOG_FUNC_ARG(pMatrix) - LOG_FUNC_END; - - /* - printf("pMatrix (%d)\n", State); - printf("{\n"); - printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_11, pMatrix->_12, pMatrix->_13, pMatrix->_14); - printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_21, pMatrix->_22, pMatrix->_23, pMatrix->_24); - printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_31, pMatrix->_32, pMatrix->_33, pMatrix->_34); - printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_41, pMatrix->_42, pMatrix->_43, pMatrix->_44); - printf("}\n"); - - if(State == 6 && (pMatrix->_11 == 1.0f) && (pMatrix->_22 == 1.0f) && (pMatrix->_33 == 1.0f) && (pMatrix->_44 == 1.0f)) - { - g_bSkipPush = TRUE; - printf("SkipPush ON\n"); - } - else - { - g_bSkipPush = FALSE; - printf("SkipPush OFF\n"); - } - */ - - State = EmuXB2PC_D3DTS(State); - - HRESULT hRet = g_pD3DDevice->SetTransform(State, pMatrix); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTransform"); -} - -// ****************************************************************** -// * patch: D3DDevice_MultiplyTransform -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_MultiplyTransform) -( - D3DTRANSFORMSTATETYPE State, - CONST D3DMATRIX *pMatrix -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(State) - LOG_FUNC_ARG(pMatrix) - LOG_FUNC_END; - - State = EmuXB2PC_D3DTS(State); - - HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform"); -} - - -// ****************************************************************** -// * patch: D3DDevice_GetTransform -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetTransform) -( - D3DTRANSFORMSTATETYPE State, - D3DMATRIX *pMatrix -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(State) - LOG_FUNC_ARG(pMatrix) - LOG_FUNC_END; - - State = EmuXB2PC_D3DTS(State); - - HRESULT hRet = g_pD3DDevice->GetTransform(State, pMatrix); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetTransform"); -} - -// ****************************************************************** -// * patch: Lock2DSurface -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(Lock2DSurface) -( - X_D3DPixelContainer *pPixelContainer, - D3DCUBEMAP_FACES FaceType, - UINT Level, - D3DLOCKED_RECT *pLockedRect, - RECT *pRect, - DWORD Flags - ) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pPixelContainer) - LOG_FUNC_ARG(FaceType) - LOG_FUNC_ARG(Level) - LOG_FUNC_ARG(pLockedRect) - LOG_FUNC_ARG(pRect) - LOG_FUNC_ARG(Flags) - LOG_FUNC_END; - - - // Pass through to the Xbox implementation of this function - XB_TRMP(Lock2DSurface)(pPixelContainer, FaceType, Level, pLockedRect, pRect, Flags); - - // Mark the resource as modified - ForceResourceRehash(pPixelContainer); -} - - -// ****************************************************************** -// * patch: Lock3DSurface -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(Lock3DSurface) -( - X_D3DPixelContainer *pPixelContainer, - UINT Level, - D3DLOCKED_BOX *pLockedVolume, - D3DBOX *pBox, - DWORD Flags - ) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pPixelContainer) - LOG_FUNC_ARG(Level) - LOG_FUNC_ARG(pLockedVolume) - LOG_FUNC_ARG(pBox) - LOG_FUNC_ARG(Flags) - LOG_FUNC_END; - - // Pass through to the Xbox implementation of this function - XB_TRMP(Lock3DSurface)(pPixelContainer, Level, pLockedVolume, pBox, Flags); - - // Mark the resource as modified - ForceResourceRehash(pPixelContainer); -} - - -// LTCG specific D3DDevice_SetStreamSource function... -// This uses a custom calling convention where parameter is passed in EBX, EAX -// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX. -// Test-case: Ninja Gaiden -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource_4) -( - UINT Stride -) -{ - UINT StreamNumber; - X_D3DVertexBuffer *pStreamData; - - __asm { - mov pStreamData, ebx - mov StreamNumber, eax - } - - //LOG_FUNC_BEGIN - // LOG_FUNC_ARG(StreamNumber) - // LOG_FUNC_ARG(pStreamData) - // LOG_FUNC_ARG(Stride) - // LOG_FUNC_END; - EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetStreamSource_4(StreamNumber : %08X pStreamData : %08X Stride : %08X);", StreamNumber, pStreamData, Stride); - - CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); - - // TODO : Forward to Xbox implementation - // This should stop us having to patch GetStreamSource! - //XB_TRMP(D3DDevice_SetStreamSource_4)(StreamNumber, pStreamData, Stride); -} - -// This uses a custom calling convention where parameter is passed in EAX -// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX. -// Test-case: Superman - The Man Of Steel -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource_8) -( - X_D3DVertexBuffer *pStreamData, - UINT Stride -) -{ - UINT StreamNumber; - - __asm { - mov StreamNumber, eax - } - - //LOG_FUNC_BEGIN - // LOG_FUNC_ARG(StreamNumber) - // LOG_FUNC_ARG(pStreamData) - // LOG_FUNC_ARG(Stride) - // LOG_FUNC_END; - EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetStreamSource_8(StreamNumber : %08X pStreamData : %08X Stride : %08X);", StreamNumber, pStreamData, Stride); - - CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); - - // TODO : Forward to Xbox implementation - // This should stop us having to patch GetStreamSource! - //XB_TRMP(D3DDevice_SetStreamSource_8)(pStreamData, Stride); -} - -// ****************************************************************** -// * patch: D3DDevice_SetStreamSource -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource) -( - UINT StreamNumber, - X_D3DVertexBuffer *pStreamData, - UINT Stride -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(StreamNumber) - LOG_FUNC_ARG(pStreamData) - LOG_FUNC_ARG(Stride) - LOG_FUNC_END; - - CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); - - // Forward to Xbox implementation - // This should stop us having to patch GetStreamSource! - XB_TRMP(D3DDevice_SetStreamSource)(StreamNumber, pStreamData, Stride); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShader) -( - DWORD Handle -) -{ - LOG_FUNC_ONE_ARG(Handle); - - CxbxImpl_SetVertexShader(Handle); - - UpdateViewPortOffsetAndScaleConstants(); -} - -// TODO : Move to own file -constexpr unsigned int IndicesPerPage = PAGE_SIZE / sizeof(INDEX16); -constexpr unsigned int InputQuadsPerPage = ((IndicesPerPage * VERTICES_PER_QUAD) / VERTICES_PER_TRIANGLE) / TRIANGLES_PER_QUAD; - -// TODO : Move to own file -// Called by CxbxDrawPrimitiveUP (indirectly by D3DDevice_DrawVerticesUP, -// EmuExecutePushBufferRaw and EmuFlushIVB) when PrimitiveType == X_D3DPT_QUADLIST. -// Emulated by calling g_pD3DDevice->DrawIndexedPrimitiveUP with index data that maps -// quads to triangles. This function creates the index buffer that is needed for this; -// For every quad that must be drawn, we generate indices for two triangles. -// Note, that the resulting index data can be re-used for later comparable draw calls -// and only needs to grow when current length doesn't suffices for a larger draw. -INDEX16 *CxbxAssureQuadListIndexData(UINT NrOfQuadIndices) -{ - if (g_QuadToTriangleIndexData_Size < NrOfQuadIndices) - { - g_QuadToTriangleIndexData_Size = RoundUp(NrOfQuadIndices, InputQuadsPerPage); - UINT NrOfTriangleIndices = QuadToTriangleVertexCount(g_QuadToTriangleIndexData_Size); - if (g_pQuadToTriangleIndexData != nullptr) { - free(g_pQuadToTriangleIndexData); - } - - g_pQuadToTriangleIndexData = (INDEX16 *)malloc(NrOfTriangleIndices * sizeof(INDEX16)); - CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleIndices, g_pQuadToTriangleIndexData); - } - - return g_pQuadToTriangleIndexData; -} - -// TODO : Move to own file -// Makes a D3D IndexBuffer active that contains quadlist-to-trianglelist indices. -// Uses CxbxAssureQuadListIndexData to populate the index buffer with. -// Note, that the resulting index buffer can be re-used for later comparable draw calls -// and only needs to grow when current length doesn't sufficesw for a larger draw. -void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadIndices) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - HRESULT hRet; - - if (g_QuadToTriangleHostIndexBuffer_Size < NrOfQuadIndices) - { - // Round the number of indices up so we'll allocate whole pages - g_QuadToTriangleHostIndexBuffer_Size = RoundUp(NrOfQuadIndices, InputQuadsPerPage); - UINT NrOfTriangleIndices = QuadToTriangleVertexCount(g_QuadToTriangleHostIndexBuffer_Size); // 4 > 6 - - // Create a new native index buffer of the above determined size : - if (g_pQuadToTriangleHostIndexBuffer != nullptr) { - g_pQuadToTriangleHostIndexBuffer->Release(); // test-case : XDK PointSprites - g_pQuadToTriangleHostIndexBuffer = nullptr; - } - - // Create a new native index buffer of the above determined size : - g_pQuadToTriangleHostIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleIndices); - if (g_pQuadToTriangleHostIndexBuffer == nullptr) - CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : IndexBuffer Create Failed!"); - - // Put quadlist-to-triangle-list index mappings into this buffer : - INDEX16* pHostIndexBufferData = nullptr; - hRet = g_pQuadToTriangleHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); - DEBUG_D3DRESULT(hRet, "g_pQuadToTriangleHostIndexBuffer->Lock"); - if (pHostIndexBufferData == nullptr) - CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : Could not lock index buffer!"); - - memcpy(pHostIndexBufferData, CxbxAssureQuadListIndexData(NrOfQuadIndices), NrOfTriangleIndices * sizeof(INDEX16)); - - g_pQuadToTriangleHostIndexBuffer->Unlock(); - } - - // Activate the new native index buffer : - hRet = g_pD3DDevice->SetIndices(g_pQuadToTriangleHostIndexBuffer); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); - - if (FAILED(hRet)) - CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : SetIndices Failed!"); // +DxbxD3DErrorString(hRet)); -} - -// TODO : Move to own file -// Calls SetIndices with a separate index-buffer, that's populated with the supplied indices. -void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - HRESULT hRet; - - if (g_pClosingLineLoopHostIndexBuffer == nullptr) { - g_pClosingLineLoopHostIndexBuffer = CxbxCreateIndexBuffer(VERTICES_PER_LINE); - if (g_pClosingLineLoopHostIndexBuffer == nullptr) - CxbxKrnlCleanup("Unable to create g_pClosingLineLoopHostIndexBuffer for D3DPT_LINELOOP emulation"); - } - - INDEX16 *pCxbxClosingLineLoopIndexBufferData = nullptr; - hRet = g_pClosingLineLoopHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pCxbxClosingLineLoopIndexBufferData, D3DLOCK_DISCARD); - DEBUG_D3DRESULT(hRet, "g_pClosingLineLoopHostIndexBuffer->Lock"); - - // Set the indices for the two VERTICES_PER_LINE : - pCxbxClosingLineLoopIndexBufferData[0] = LowIndex; - pCxbxClosingLineLoopIndexBufferData[1] = HighIndex; - - hRet = g_pClosingLineLoopHostIndexBuffer->Unlock(); - DEBUG_D3DRESULT(hRet, "g_pClosingLineLoopHostIndexBuffer->Unlock"); - - hRet = g_pD3DDevice->SetIndices(g_pClosingLineLoopHostIndexBuffer); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); - - hRet = g_pD3DDevice->DrawIndexedPrimitive( - /*PrimitiveType=*/D3DPT_LINELIST, - /*BaseVertexIndex=*/0, // Note : Callers must apply BaseVertexIndex to the LowIndex and HighIndex argument values - /*MinVertexIndex=*/LowIndex, - /*NumVertices=*/VERTICES_PER_LINE, - /*startIndex=*/0, - /*primCount=*/1 - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(CxbxDrawIndexedClosingLine)"); - - g_dwPrimPerFrame++; -} - -// TODO : Move to own file -void CxbxDrawIndexedClosingLineUP(INDEX16 LowIndex, INDEX16 HighIndex, void *pHostVertexStreamZeroData, UINT uiHostVertexStreamZeroStride) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - -#if 0 - // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than - // via CxbxDrawIndexedClosingLine, by drawing two indices via DrawIndexedPrimitiveUP. - // (This is simpler because we use just indices and don't need to copy the vertices.) - INDEX16 CxbxClosingLineIndices[2] = { LowIndex, HighIndex }; - - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - /*PrimitiveType=*/D3DPT_LINELIST, - /*MinVertexIndex=*/LowIndex, - /*NumVertices=*/(HighIndex - LowIndex) + 1, - /*PrimitiveCount=*/1, - /*pIndexData=*/CxbxClosingLineIndices, - /*IndexDataFormat=*/D3DFMT_INDEX16, - pHostVertexStreamZeroData, - uiHostVertexStreamZeroStride - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP(CxbxDrawIndexedClosingLineUP)"); -#else // TODO : If NumVertices is high, performance might suffer - drawing a copy of the two vertices could be faster - // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than - // via CxbxDrawIndexedClosingLine, by drawing two vertices via DrawPrimitiveUP. - // (This is simpler because we just copy the vertices, and don't need a separate index buffer.) - uint8_t VertexData[512]; assert(512 >= 2 * uiHostVertexStreamZeroStride); - uint8_t *FirstVertex = (uint8_t *)pHostVertexStreamZeroData + (LowIndex * uiHostVertexStreamZeroStride); - uint8_t *SecondVertex = (uint8_t *)pHostVertexStreamZeroData + (HighIndex * uiHostVertexStreamZeroStride); - - memcpy(VertexData, FirstVertex, uiHostVertexStreamZeroStride); - memcpy(VertexData + uiHostVertexStreamZeroStride, SecondVertex, uiHostVertexStreamZeroStride); - - HRESULT hRet = g_pD3DDevice->DrawPrimitiveUP( - /*PrimitiveType=*/D3DPT_LINELIST, - /*PrimitiveCount=*/1, - /*pVertexStreamZeroData=*/VertexData, - uiHostVertexStreamZeroStride - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitiveUP(CxbxDrawIndexedClosingLineUP)"); -#endif - - g_dwPrimPerFrame++; -} - -// Requires assigned pXboxIndexData -// Called by D3DDevice_DrawIndexedVertices and EmuExecutePushBufferRaw (twice) -void CxbxDrawIndexed(CxbxDrawContext &DrawContext) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - assert(DrawContext.dwStartVertex == 0); - assert(DrawContext.pXboxIndexData != nullptr); - assert(DrawContext.dwVertexCount > 0); // TODO : If this fails, make responsible callers do an early-exit - assert(IsValidCurrentShader()); - - bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); - ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pXboxIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); - // Note : CxbxUpdateActiveIndexBuffer calls SetIndices - - // Set LowIndex and HighIndex *before* VerticesInBuffer gets derived - DrawContext.LowIndex = CacheEntry.LowIndex; - DrawContext.HighIndex = CacheEntry.HighIndex; - - VertexBufferConverter.Apply(&DrawContext); // Sets dwHostPrimitiveCount - - INT BaseVertexIndex = DrawContext.dwBaseVertexIndex; - UINT primCount = DrawContext.dwHostPrimitiveCount; - if (bConvertQuadListToTriangleList) { - if (DrawContext.dwVertexCount == 4) - LOG_TEST_CASE("X_D3DPT_QUADLIST (single quad)"); // breakpoint location - else - LOG_TEST_CASE("X_D3DPT_QUADLIST"); - - if (BaseVertexIndex > 0) - LOG_TEST_CASE("X_D3DPT_QUADLIST (BaseVertexIndex > 0)"); - - // Convert draw arguments from quads to triangles : - // Note : BaseVertexIndex must NOT be mapped through QuadToTriangleVertexCount(BaseVertexIndex)! - primCount *= TRIANGLES_PER_QUAD; - } - - // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers - // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, NumVertices and StartIndex arguments. - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - /* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - BaseVertexIndex, - /* MinVertexIndex = */CacheEntry.LowIndex, - /* NumVertices = */(CacheEntry.HighIndex - CacheEntry.LowIndex) + 1, - /* startIndex = DrawContext.dwStartVertex = */0, - primCount); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive"); - - g_dwPrimPerFrame += primCount; - if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { - // Close line-loops using a final single line, drawn from the end to the start vertex - if (BaseVertexIndex == 0) { - LOG_TEST_CASE("X_D3DPT_LINELOOP"); - } else { - LOG_TEST_CASE("X_D3DPT_LINELOOP (BaseVertexIndex > 0)"); - // Note : This is to find test-cases for the BaseVertexIndex addition below: - } - // Read the end and start index from the supplied index data - INDEX16 LowIndex = BaseVertexIndex + DrawContext.pXboxIndexData[0]; - INDEX16 HighIndex = BaseVertexIndex + DrawContext.pXboxIndexData[DrawContext.dwHostPrimitiveCount]; - // If needed, swap so highest index is higher than lowest (duh) - if (HighIndex < LowIndex) { - std::swap(HighIndex, LowIndex); - } - - // Draw the closing line using a helper function (which will SetIndices) - CxbxDrawIndexedClosingLine(LowIndex, HighIndex); - // NOTE : We don't restore the previously active index buffer - } -} - -// TODO : Move to own file -// Drawing function specifically for rendering Xbox draw calls supplying a 'User Pointer'. -// Called by D3DDevice_DrawVerticesUP, EmuExecutePushBufferRaw and EmuFlushIVB -void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - assert(DrawContext.dwStartVertex == 0); - assert(DrawContext.pXboxVertexStreamZeroData != xbnullptr); - assert(DrawContext.uiXboxVertexStreamZeroStride > 0); - assert(DrawContext.dwBaseVertexIndex == 0); // No IndexBase under Draw*UP - - VertexBufferConverter.Apply(&DrawContext); - if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST) { - // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // test-case : X-Marbles and XDK Sample PlayField - // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : - INDEX16 *pIndexData = CxbxAssureQuadListIndexData(DrawContext.dwVertexCount); - // Convert quad vertex-count to triangle vertex count : - UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; - - // Instead of calling WalkIndexBuffer on pQuadToTriangleIndexBuffer, - // we can derive the LowIndex and HighIndexes ourselves here - INDEX16 LowIndex = 0; - INDEX16 HighIndex = (INDEX16)(DrawContext.dwVertexCount - 1); - - // Draw indexed triangles instead of quads - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - /*PrimitiveType=*/D3DPT_TRIANGLELIST, - /*MinVertexIndex=*/LowIndex, - /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, - PrimitiveCount, - pIndexData, - /*IndexDataFormat=*/D3DFMT_INDEX16, - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitieUP(X_D3DPT_QUADLIST)"); - - g_dwPrimPerFrame += PrimitiveCount; - } - else { - // Primitives other than X_D3DPT_QUADLIST can be drawn using one DrawPrimitiveUP call : - HRESULT hRet = g_pD3DDevice->DrawPrimitiveUP( - EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - DrawContext.dwHostPrimitiveCount, - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitiveUP"); - - g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; - if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { - // test-case : XDK samples reaching this case : DebugKeyboard, Gamepad, Tiling, ShadowBuffer - // Close line-loops using a final single line, drawn from the end to the start vertex : - CxbxDrawIndexedClosingLineUP( - (INDEX16)0, // LowIndex - (INDEX16)DrawContext.dwHostPrimitiveCount, // HighIndex, - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - } - } -} - -IDirect3DBaseTexture* CxbxConvertXboxSurfaceToHostTexture(XTL::X_D3DBaseTexture* pBaseTexture) -{ - LOG_INIT; - - IDirect3DTexture* pNewHostTexture = nullptr; -#if 0 // TODO : Complete, debug and activate (and then cleanup GetHostBaseTexture) - D3DFORMAT PCFormat = D3DFMT_A8B8G8R8; // TODO : Derive from pBaseTexture - - IDirect3DSurface* pHostSurface = GetHostSurface(pBaseTexture); // TODO : Extend this with a texture channel number too, if surfaces send to SetTexture can be paletized format? - - DWORD dwWidth = GetPixelContainerWidth(pBaseTexture); - DWORD dwHeight = GetPixelContainerHeight(pBaseTexture); - UINT dwMipMapLevels = CxbxGetPixelContainerMipMapLevels(pBaseTexture); - - HRESULT hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, - /*Usage=*/0, PCFormat, D3DPOOL_SYSTEMMEM, &pNewHostTexture, nullptr); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture (in CxbxConvertXboxSurfaceToHostTexture)"); - if (hRet != D3D_OK) { - CxbxKrnlCleanup("CreateTexture Failed!\n\nError: \nDesc: "/*, - DXGetErrorString(hRet), DXGetErrorDescription(hRet)*/); - } - - IDirect3DSurface* pHostTextureSurface = nullptr; - hRet = pNewHostTexture->GetSurfaceLevel(/*Level=*/0, &pHostTextureSurface); - DEBUG_D3DRESULT(hRet, "pHostBaseTexture->GetSurfaceLevel"); - - if (hRet == D3D_OK) { - hRet = D3DXLoadSurfaceFromSurface(pHostTextureSurface, nullptr, nullptr, pHostSurface, nullptr, nullptr, D3DX_FILTER_NONE, 0x00000000); - DEBUG_D3DRESULT(hRet, "D3DXLoadSurfaceFromSurface"); - pHostTextureSurface->Release(); - } -#endif - return (IDirect3DBaseTexture*)pNewHostTexture; // return it as a base texture -} - -void EmuUpdateActiveTextureStages() -{ - LOG_INIT; - - for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) - { - XTL::X_D3DBaseTexture *pBaseTexture = g_pXbox_SetTexture[i]; - IDirect3DBaseTexture *pHostBaseTexture = nullptr; - bool bNeedRelease = false; - - if (pBaseTexture != xbnullptr) { - DWORD Type = GetXboxCommonResourceType(pBaseTexture); - switch (Type) { - case X_D3DCOMMON_TYPE_TEXTURE: - pHostBaseTexture = GetHostBaseTexture(pBaseTexture, /*D3DUsage=*/0, i); - break; - case X_D3DCOMMON_TYPE_SURFACE: - // Surfaces can be set in the texture stages, instead of textures - LOG_TEST_CASE("ActiveTexture set to a surface (non-texture) resource"); // Test cases : Burnout, Outrun 2006 - // We must wrap the surface before using it as a texture - pHostBaseTexture = CxbxConvertXboxSurfaceToHostTexture(pBaseTexture); - // Release this texture (after SetTexture) when we succeeded in creating it : - bNeedRelease = pHostBaseTexture != nullptr; - break; - default: - LOG_TEST_CASE("ActiveTexture set to an unhandled resource type!"); - break; - } - } - - HRESULT hRet = g_pD3DDevice->SetTexture(i, pHostBaseTexture); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTexture"); - if (bNeedRelease) { - pHostBaseTexture->Release(); - } - } -} - -void CxbxUpdateNativeD3DResources() -{ - // Before we start, make sure our resource cache stays limited in size - PrunePaletizedTexturesCache(); // TODO : Could we move this to Swap instead? - - EmuUpdateActiveTextureStages(); - - // Some titles set Vertex Shader constants directly via pushbuffers rather than through D3D - // We handle that case by updating any constants that have the dirty flag set on the nv2a. - auto nv2a = g_NV2A->GetDeviceState(); - for(int i = 0; i < X_D3DVS_CONSTREG_COUNT; i++) { - // Skip vOffset and vScale constants, we don't want our values to be overwritten by accident - if (i == X_D3DSCM_RESERVED_CONSTANT_OFFSET_CORRECTED || i == X_D3DSCM_RESERVED_CONSTANT_SCALE_CORRECTED) { - continue; - } - - if (nv2a->pgraph.vsh_constants_dirty[i]) { - g_pD3DDevice->SetVertexShaderConstantF(i, (float*)&nv2a->pgraph.vsh_constants[i][0], 1); - nv2a->pgraph.vsh_constants_dirty[i] = false; - } - } - - // NOTE: Order is important here - // Some Texture States depend on RenderState values (Point Sprites) - // And some Pixel Shaders depend on Texture State values (BumpEnvMat, etc) - XboxRenderStates.Apply(); - XboxTextureStates.Apply(); - - // If Pixel Shaders are not disabled, process them - if (!g_DisablePixelShaders) { - DxbxUpdateActivePixelShader(); - } - -/* TODO : Port these : - DxbxUpdateActiveVertexShader(); - DxbxUpdateActiveTextures(); - DxbxUpdateDeferredStates(); // BeginPush sample shows us that this must come *after* texture update! - DxbxUpdateActiveVertexBufferStreams(); - DxbxUpdateActiveRenderTarget(); -*/ -} - -// This function should be called in thight idle-wait loops. -// It's purpose is to lower CPU cost in such a way that the -// caller will still repond quickly, without actually waiting -// or giving up it's time-slice. -// See https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-yieldprocessor -// and https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-pause-intrinsic -inline void CxbxCPUIdleWait() // TODO : Apply wherever applicable -{ - YieldProcessor(); -} - -// This function indicates whether Cxbx can flush host GPU commands. -bool CxbxCanFlushHostGPU() -{ - return (g_pHostQueryWaitForIdle != nullptr); -} - -// Wait until host GPU finished processing it's command queue -bool CxbxFlushHostGPU() -{ - // The following can only work when host GPU queries are available - if (!CxbxCanFlushHostGPU()) { - // If we can't query host GPU, return failure - return false; - } - - // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/queries - // Add an end marker to the command buffer queue. - // This, so that the next GetData will always have at least one - // final query event to flush out, after which GPU will be done. - g_pHostQueryWaitForIdle->Issue(D3DISSUE_END); - - // Empty the command buffer and wait until host GPU is idle. - while (S_FALSE == g_pHostQueryWaitForIdle->GetData(nullptr, 0, D3DGETDATA_FLUSH)) - CxbxCPUIdleWait(); - - // Signal caller that host GPU has been flushed - return true; -} - -// This function mimicks NV2A software callback events. -// Normally, these would be handled by actual push-buffer -// command handling at the point where they where inserted. -// Since our HLE mostly circumvents the NV2A pushbuffer, -// this function has to be called after 'pushing' functions. -void CxbxHandleXboxCallbacks() -{ - // The following can only work when host GPU queries are available - if (g_pHostQueryCallbackEvent != nullptr) { - // Query whether host GPU encountered a callback event already - if (S_FALSE == g_pHostQueryCallbackEvent->GetData(nullptr, 0, 0)) { - // If not, don't handle callbacks - return; - } - } - - // Process inserted callbacks - while (!g_Xbox_CallbackQueue.empty()) { - // Fetch a callback from the FIFO callback queue - s_Xbox_Callback XboxCallback = g_Xbox_CallbackQueue.front(); - g_Xbox_CallbackQueue.pop(); - - // Differentiate between write and read callbacks - if (XboxCallback.Type == XTL::X_D3DCALLBACK_WRITE) { - // Write callbacks should wait until GPU is idle - if (!CxbxFlushHostGPU()) { - // Host GPU can't be flushed. In the old behaviour, we made the callback anyway - // TODO : Should we keep doing that? - } - } else { - assert(XboxCallback.Type == XTL::X_D3DCALLBACK_READ); - // Should we mimick Read callback old behaviour? - if (g_bHack_DisableHostGPUQueries) { - // Note : Previously, we only processed Write, and ignored Read callbacks - continue; - } else { - // New behaviour does place Read callbacks too - } - } - - // Make the callback - XboxCallback.pCallback(XboxCallback.Context); - } -} - -// On Xbox, this function inserts push-buffer commands that -// will trigger the software handler to perform the callback -// when the GPU processes these commands. -// The type X_D3DCALLBACK_WRITE callbacks are prefixed with an -// wait-for-idle command, but otherwise they're identical. -// (Software handlers are triggered on NV2A via NV097_NO_OPERATION) -void CxbxImpl_InsertCallback -( - XTL::X_D3DCALLBACKTYPE Type, - XTL::X_D3DCALLBACK pCallback, - XTL::DWORD Context -) -{ - if (Type > XTL::X_D3DCALLBACK_WRITE) { - LOG_TEST_CASE("Illegal callback type!"); - return; - } - - if (pCallback == xbnullptr) { - LOG_TEST_CASE("pCallback == xbnullptr!"); - return; - } - - // Should we mimick old behaviour? - if (g_bHack_DisableHostGPUQueries) { - // Mimick old behaviour, in which only the final callback event - // was remembered, by emptying the callback queue entirely : - while (!g_Xbox_CallbackQueue.empty()) { - g_Xbox_CallbackQueue.pop(); - } - } - - // Push this callback's arguments into the callback queue : - s_Xbox_Callback XboxCallback = { pCallback, Type, Context }; - g_Xbox_CallbackQueue.push(XboxCallback); // g_Xbox_CallbackQueue.emplace(pCallback, Type, Context); doesn't compile? - - // Does host supports GPU queries? - if (g_pHostQueryCallbackEvent != nullptr) { - // Insert a callback event on host GPU, - // which will be handled by CxbxHandleXboxCallback - g_pHostQueryCallbackEvent->Issue(D3DISSUE_END); - } -} - -VOID __declspec(noinline) D3DDevice_SetPixelShaderCommon(DWORD Handle) -{ - // Cache the active shader handle - g_pXbox_PixelShader = (XTL::X_PixelShader*)Handle; - - // Copy the Pixel Shader data to our RenderState handler - // This mirrors the fact that unpatched SetPixelShader does the same thing! - // This shouldn't be necessary anymore, but shaders still break if we don't do this - if (g_pXbox_PixelShader != nullptr) { - memcpy(XboxRenderStates.GetPixelShaderRenderStatePointer(), g_pXbox_PixelShader->pPSDef, sizeof(XTL::X_D3DPIXELSHADERDEF) - 3 * sizeof(DWORD)); - XboxRenderStates.SetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES, g_pXbox_PixelShader->pPSDef->PSTextureModes); - } -} - -// LTCG specific D3DDevice_SetPixelShader function... -// This uses a custom calling convention where parameter is passed in EAX -// Test-case: Metal Wolf Chaos -// Test-case: Lord of the Rings: The Third Age -VOID WINAPI D3DDevice_SetPixelShader_0_IMPL -( - DWORD Handle -) -{ - LOG_FUNC_ONE_ARG(Handle); - - // Call the Xbox function to make sure D3D structures get set - __asm { - mov eax, Handle - call XB_TRMP(D3DDevice_SetPixelShader_0) - } - - D3DDevice_SetPixelShaderCommon(Handle); -} - -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPixelShader_0)() -{ - __asm { - push eax - call D3DDevice_SetPixelShader_0_IMPL - ret - } -} - -// ****************************************************************** -// * patch: D3DDevice_SetPixelShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPixelShader) -( - DWORD Handle -) -{ - LOG_FUNC_ONE_ARG(Handle); - - // Call the Xbox function to make sure D3D structures get set - XB_TRMP(D3DDevice_SetPixelShader)(Handle); - - D3DDevice_SetPixelShaderCommon(Handle); -} - -// ****************************************************************** -// * patch: D3DDevice_DrawVertices_4 -// LTCG specific D3DDevice_DrawVertices function... -// This uses a custom calling convention where parameter is passed in ECX, EAX and Stack -// Test Case: Conker -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices_4) -( - X_D3DPRIMITIVETYPE PrimitiveType -) -{ - UINT VertexCount; - UINT StartVertex; - - _asm { - mov VertexCount, eax - mov StartVertex, ecx - } - - EMUPATCH(D3DDevice_DrawVertices)(PrimitiveType, StartVertex, VertexCount); -} - -// ****************************************************************** -// * patch: D3DDevice_DrawVertices -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) -( - X_D3DPRIMITIVETYPE PrimitiveType, - UINT StartVertex, - UINT VertexCount -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(PrimitiveType) - LOG_FUNC_ARG(StartVertex) - LOG_FUNC_ARG(VertexCount) - LOG_FUNC_END; - - // Dxbx Note : In DrawVertices and DrawIndexedVertices, PrimitiveType may not be D3DPT_POLYGON - - if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { - LOG_TEST_CASE("Invalid VertexCount"); - return; - } - - // TODO : Call unpatched D3DDevice_SetStateVB(0); - - CxbxUpdateNativeD3DResources(); - if (IsValidCurrentShader()) { - CxbxDrawContext DrawContext = {}; - - DrawContext.XboxPrimitiveType = PrimitiveType; - DrawContext.dwVertexCount = VertexCount; - DrawContext.dwStartVertex = StartVertex; - - VertexBufferConverter.Apply(&DrawContext); - if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { - if (StartVertex == 0) { - //LOG_TEST_CASE("X_D3DPT_QUADLIST (StartVertex == 0)"); // disabled, hit too often - // test-case : ?X-Marbles - // test-case XDK Samples : AlphaFog, AntiAlias, BackBufferScale, BeginPush, Cartoon, TrueTypeFont (?maybe PlayField?) - } else { - LOG_TEST_CASE("X_D3DPT_QUADLIST (StartVertex > 0)"); - // https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/1156 - // test-case : All - Star Baseball '03 - // test-case : Army Men Major Malfunction - // test-case : Big Mutha Truckers - // test-case : BLiNX: the time sweeper - // test-case : Blood Wake - // test-case : Call of Duty: Finest Hour - // test-case : Flight academy - // test-case : FIFA World Cup 2002 - // test-case : GENMA ONIMUSHA - // test-case : Halo - Combat Evolved - // test-case : Harry Potter and the Sorcerer's Stone - // test-case : Heroes of the Pacific - // test-case : Hummer Badlands - // test-case : Knights Of The Temple 2 - // test-case : LakeMasters Bass fishing - // test-case : MetalDungeon - // test-case : NFL Fever 2003 Demo - main menu - // test-case : Night Caster 2 - // test-case : Pinball Hall of Fame - // test-case : Robotech : Battlecry - // test-case : SpiderMan 2 - // test-case : Splinter Cell Demo - // test-case : Stubbs the Zombie - // test-case : Tony Hawk's Pro Skater 2X (main menu entries) - // test-case : Worms 3D Special Edition - // test-case : XDK sample Lensflare (4, for 10 flare-out quads that use a linear texture; rendered incorrectly: https://youtu.be/idwlxHl9nAA?t=439) - DrawContext.dwStartVertex = StartVertex; // Breakpoint location for testing. - } - - // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : - // Assure & activate that special index buffer : - CxbxAssureQuadListD3DIndexBuffer(/*NrOfQuadIndices=*/DrawContext.dwVertexCount); - // This API's StartVertex argument is multiplied by vertex stride and added to the start of the vertex buffer; - // BaseVertexIndex offers the same functionality on host : - UINT BaseVertexIndex = DrawContext.dwStartVertex; - // Convert quad vertex count to triangle vertex count : - UINT NumVertices = QuadToTriangleVertexCount(DrawContext.dwVertexCount); - // Convert quad primitive count to triangle primitive count : - UINT primCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; - // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers - // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, NumVertices and StartIndex arguments. - // Emulate drawing quads by drawing each quad with two indexed triangles : - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( - /*PrimitiveType=*/D3DPT_TRIANGLELIST, - BaseVertexIndex, - /*MinVertexIndex=*/0, - NumVertices, - /*startIndex=*/0, - primCount - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(X_D3DPT_QUADLIST)"); - - g_dwPrimPerFrame += primCount; - } - else { - // if (StartVertex > 0) LOG_TEST_CASE("StartVertex > 0 (non-quad)"); // Verified test case : XDK Sample (PlayField) - HRESULT hRet = g_pD3DDevice->DrawPrimitive( - EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - DrawContext.dwStartVertex, - DrawContext.dwHostPrimitiveCount - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitive"); - - g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; - if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { - // Close line-loops using a final single line, drawn from the end to the start vertex - LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Text-cases needed - - assert(DrawContext.dwBaseVertexIndex == 0); // if this fails, it needs to be added to LowIndex and HighIndex : - INDEX16 LowIndex = (INDEX16)DrawContext.dwStartVertex; - INDEX16 HighIndex = (INDEX16)(DrawContext.dwStartVertex + DrawContext.dwHostPrimitiveCount); - // Draw the closing line using a helper function (which will SetIndices) - CxbxDrawIndexedClosingLine(LowIndex, HighIndex); - // NOTE : We don't restore the previously active index buffer - } - } - } - - CxbxHandleXboxCallbacks(); -} - -// ****************************************************************** -// * patch: D3DDevice_DrawVerticesUP -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVerticesUP) -( - X_D3DPRIMITIVETYPE PrimitiveType, - UINT VertexCount, - CONST PVOID pVertexStreamZeroData, - UINT VertexStreamZeroStride -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(PrimitiveType) - LOG_FUNC_ARG(VertexCount) - LOG_FUNC_ARG(pVertexStreamZeroData) - LOG_FUNC_ARG(VertexStreamZeroStride) - LOG_FUNC_END; - - if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { - LOG_TEST_CASE("Invalid VertexCount"); - return; - } - - // TODO : Call unpatched D3DDevice_SetStateUP(); - - CxbxUpdateNativeD3DResources(); - - if (IsValidCurrentShader()) { - CxbxDrawContext DrawContext = {}; - - DrawContext.XboxPrimitiveType = PrimitiveType; - DrawContext.dwVertexCount = VertexCount; - DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; - DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; - - CxbxDrawPrimitiveUP(DrawContext); - } - - CxbxHandleXboxCallbacks(); -} - -// ****************************************************************** -// * patch: D3DDevice_DrawIndexedVertices -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) -( - X_D3DPRIMITIVETYPE PrimitiveType, - UINT VertexCount, - CONST PWORD pIndexData -) -{ - // Test-cases : XDK samples (Cartoon, Gamepad) - // Note : In gamepad.xbe, the gamepad is drawn by D3DDevice_DrawIndexedVertices - // Dxbx Note : In DrawVertices and DrawIndexedVertices, PrimitiveType may not be D3DPT_POLYGON - - LOG_FUNC_BEGIN - LOG_FUNC_ARG(PrimitiveType) - LOG_FUNC_ARG(VertexCount) - LOG_FUNC_ARG(pIndexData) - LOG_FUNC_END; - - if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { - LOG_TEST_CASE("Invalid VertexCount"); - return; - } - - // TODO : Call unpatched D3DDevice_SetStateVB(g_Xbox_BaseVertexIndex); - - CxbxUpdateNativeD3DResources(); - - if (IsValidCurrentShader()) { - CxbxDrawContext DrawContext = {}; - - DrawContext.XboxPrimitiveType = PrimitiveType; - DrawContext.dwVertexCount = VertexCount; - DrawContext.dwBaseVertexIndex = g_Xbox_BaseVertexIndex; // Multiplied by vertex stride and added to the vertex buffer start - DrawContext.pXboxIndexData = pIndexData; // Used to derive VerticesInBuffer - - // Test case JSRF draws all geometry through this function (only sparks are drawn via another method) - // using X_D3DPT_TRIANGLELIST and X_D3DPT_TRIANGLESTRIP PrimitiveType - CxbxDrawIndexed(DrawContext); - } - - CxbxHandleXboxCallbacks(); -} - -// ****************************************************************** -// * patch: D3DDevice_DrawIndexedVerticesUP -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) -( - X_D3DPRIMITIVETYPE PrimitiveType, - UINT VertexCount, - CONST PVOID pIndexData, - CONST PVOID pVertexStreamZeroData, - UINT VertexStreamZeroStride -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(PrimitiveType) - LOG_FUNC_ARG(VertexCount) - LOG_FUNC_ARG(pIndexData) - LOG_FUNC_ARG(pVertexStreamZeroData) - LOG_FUNC_ARG(VertexStreamZeroStride) - LOG_FUNC_END; - - if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { - LOG_TEST_CASE("Invalid VertexCount"); - return; - } - - // TODO : Call unpatched D3DDevice_SetStateUP(); - - CxbxUpdateNativeD3DResources(); - - if (IsValidCurrentShader()) { - CxbxDrawContext DrawContext = {}; - INDEX16* pXboxIndexData = (INDEX16*)pIndexData; - - DrawContext.XboxPrimitiveType = PrimitiveType; - DrawContext.dwVertexCount = VertexCount; - DrawContext.pXboxIndexData = pXboxIndexData; // Used to derive VerticesInBuffer - // Note : D3DDevice_DrawIndexedVerticesUP does NOT use g_Xbox_BaseVertexIndex, so keep DrawContext.dwBaseVertexIndex at 0! - DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; - DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; - - // Determine LowIndex and HighIndex *before* VerticesInBuffer gets derived - WalkIndexBuffer(DrawContext.LowIndex, DrawContext.HighIndex, pXboxIndexData, VertexCount); - - VertexBufferConverter.Apply(&DrawContext); - - INDEX16* pHostIndexData; - UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount; - - bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST); - if (bConvertQuadListToTriangleList) { - LOG_TEST_CASE("X_D3DPT_QUADLIST"); - // Test-case : Buffy: The Vampire Slayer - // Test-case : XDK samples : FastLoad, BackBufferScale, DisplacementMap, Donuts3D, VolumeLight, PersistDisplay, PolynomialTextureMaps, SwapCallback, Tiling, VolumeFog, DebugKeyboard, Gamepad - // Convert draw arguments from quads to triangles : - pHostIndexData = CxbxCreateQuadListToTriangleListIndexData(pXboxIndexData, VertexCount); - PrimitiveCount *= TRIANGLES_PER_QUAD; - // Note, that LowIndex and HighIndex won't change due to this quad-to-triangle conversion, - // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. - } else { - // LOG_TEST_CASE("DrawIndexedPrimitiveUP"); // Test-case : Burnout, Namco Museum 50th Anniversary - pHostIndexData = pXboxIndexData; - } - - HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( - /*PrimitiveType=*/EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), - /*MinVertexIndex=*/DrawContext.LowIndex, - /*NumVertexIndices=*/(DrawContext.HighIndex - DrawContext.LowIndex) + 1, - PrimitiveCount, - pHostIndexData, - /*IndexDataFormat=*/D3DFMT_INDEX16, - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP"); - - if (bConvertQuadListToTriangleList) { - CxbxReleaseQuadListToTriangleListIndexData(pHostIndexData); - } - - g_dwPrimPerFrame += PrimitiveCount; - if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { - // Close line-loops using a final single line, drawn from the end to the start vertex - LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this test-case? - // Read the end and start index from the supplied index data - INDEX16 LowIndex = pXboxIndexData[0]; - INDEX16 HighIndex = pXboxIndexData[DrawContext.dwHostPrimitiveCount]; - // If needed, swap so highest index is higher than lowest (duh) - if (HighIndex < LowIndex) { - std::swap(HighIndex, LowIndex); - } - - // Close line-loops using a final single line, drawn from the end to the start vertex : - CxbxDrawIndexedClosingLineUP( - LowIndex, - HighIndex, - DrawContext.pHostVertexStreamZeroData, - DrawContext.uiHostVertexStreamZeroStride - ); - } - } - - CxbxHandleXboxCallbacks(); -} - -// ****************************************************************** -// * patch: D3DDevice_SetLight -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_SetLight) -( - DWORD Index, - CONST X_D3DLIGHT8 *pLight -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Index) - LOG_FUNC_ARG(pLight) - LOG_FUNC_END; - - XB_TRMP(D3DDevice_SetLight)(Index, pLight); - - HRESULT hRet = g_pD3DDevice->SetLight(Index, pLight); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetLight"); - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_SetMaterial -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetMaterial) -( - CONST X_D3DMATERIAL8 *pMaterial -) -{ - LOG_FUNC_ONE_ARG(pMaterial); - - HRESULT hRet = g_pD3DDevice->SetMaterial(pMaterial); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetMaterial"); -} - -// ****************************************************************** -// * patch: D3DDevice_LightEnable -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_LightEnable) -( - DWORD Index, - BOOL bEnable -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Index) - LOG_FUNC_ARG(bEnable) - LOG_FUNC_END; - - XB_TRMP(D3DDevice_LightEnable)(Index, bEnable); - - HRESULT hRet = g_pD3DDevice->LightEnable(Index, bEnable); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->LightEnable"); - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_SetRenderTarget -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetRenderTarget) -( - X_D3DSurface *pRenderTarget, - X_D3DSurface *pNewZStencil -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pRenderTarget) - LOG_FUNC_ARG(pNewZStencil) - LOG_FUNC_END; - - IDirect3DSurface *pHostRenderTarget = nullptr; - IDirect3DSurface *pHostDepthStencil = nullptr; - - XB_TRMP(D3DDevice_SetRenderTarget)(pRenderTarget, pNewZStencil); - - // In Xbox titles, CreateDevice calls SetRenderTarget for the back buffer - // We can use this to determine the Xbox backbuffer surface for later use! - if (g_pXbox_BackBufferSurface == xbnullptr) { - g_pXbox_BackBufferSurface = pRenderTarget; - // TODO : Some titles might render to another backbuffer later on, - // if that happens, we might need to skip the first one or two calls? - } - - if (g_pXbox_DefaultDepthStencilSurface == xbnullptr) { - g_pXbox_DefaultDepthStencilSurface = pNewZStencil; - } - - // The current render target is only replaced if it's passed in here non-null - if (pRenderTarget != xbnullptr) { - g_pXbox_RenderTarget = pRenderTarget; - } - else { - // If non is given, use the current Xbox render target - pRenderTarget = g_pXbox_RenderTarget; - // If there's no Xbox render target yet, fallback to the Xbox back buffer - if (pRenderTarget == xbnullptr) { - LOG_TEST_CASE("SetRenderTarget fallback to backbuffer"); - pRenderTarget = g_pXbox_BackBufferSurface; - } - } - - pHostRenderTarget = GetHostSurface(pRenderTarget, D3DUSAGE_RENDERTARGET); - - // The currenct depth stencil is always replaced by whats passed in here (even a null) - g_pXbox_DepthStencil = pNewZStencil; - pHostDepthStencil = GetHostSurface(g_pXbox_DepthStencil, D3DUSAGE_DEPTHSTENCIL); - - HRESULT hRet; - // Mimick Direct3D 8 SetRenderTarget by only setting render target if non-null - if (pHostRenderTarget) { - hRet = g_pD3DDevice->SetRenderTarget(/*RenderTargetIndex=*/0, pHostRenderTarget); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetRenderTarget"); - if (FAILED(hRet)) { - // If Direct3D 9 SetRenderTarget failed, skip setting depth stencil - return; - } - } - - hRet = g_pD3DDevice->SetDepthStencilSurface(pHostDepthStencil); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetDepthStencilSurface"); - - if (SUCCEEDED(hRet)) { - // Once we're sure the host depth-stencil is activated... - UpdateDepthStencilFlags(pHostDepthStencil); - } - - // Validate that our host render target is still the correct size - DWORD HostRenderTarget_Width, HostRenderTarget_Height; - if (GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) { - DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXbox_RenderTarget); - DWORD XboxRenderTarget_Height = GetPixelContainerHeight(g_pXbox_RenderTarget); - ValidateRenderTargetDimensions(HostRenderTarget_Width, HostRenderTarget_Height, XboxRenderTarget_Width, XboxRenderTarget_Height); - } - - UpdateViewPortOffsetAndScaleConstants(); -} - -// LTCG specific D3DDevice_SetPalette function... -// This uses a custom calling convention where parameter is passed in EAX -// Test-case: Ninja Gaiden -VOID __stdcall XTL::EMUPATCH(D3DDevice_SetPalette_4) -( -) -{ - static uint32_t returnAddr; - -#ifdef _DEBUG_TRACE - __asm add esp, 4 -#endif - - __asm { - pop returnAddr - push eax - call EmuPatch_D3DDevice_SetPalette - mov eax, 0 - push returnAddr - ret - } -} - -// ****************************************************************** -// * patch: D3DDevice_SetPalette -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPalette) -( - DWORD Stage, - X_D3DPalette *pPalette -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Stage) - LOG_FUNC_ARG(pPalette) - LOG_FUNC_END; - - // g_pD3DDevice9->SetPaletteEntries(Stage?, (PALETTEENTRY*)pPalette->Data); - // g_pD3DDevice9->SetCurrentTexturePalette(Stage, Stage); - - if (Stage >= XTL::X_D3DTS_STAGECOUNT) { - LOG_TEST_CASE("Stage out of bounds"); - } else { - // Note : Actual update of paletized textures (X_D3DFMT_P8) happens in EmuUpdateActiveTextureStages! - g_pXbox_Palette_Data[Stage] = GetDataFromXboxResource(pPalette); - g_Xbox_Palette_Size[Stage] = pPalette ? XboxD3DPaletteSizeToBytes(GetXboxPaletteSize(pPalette)) : 0; - } -} - -// LTCG specific D3DDevice_SetFlickerFilter function... -// This uses a custom calling convention where parameter is passed in ESI -// Test-case: Metal Wolf Chaos -VOID __stdcall XTL::EMUPATCH(D3DDevice_SetFlickerFilter_0) -( -) -{ - DWORD Filter; - - __asm { - mov Filter, esi - } - - return EMUPATCH(D3DDevice_SetFlickerFilter)(Filter); -} - -// ****************************************************************** -// * patch: D3DDevice_SetFlickerFilter -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3DDevice_SetFlickerFilter) -( - DWORD Filter -) -{ - LOG_FUNC_ONE_ARG(Filter); - - LOG_IGNORED(); -} - -// ****************************************************************** -// * patch: D3DDevice_SetSoftDisplayFilter -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3DDevice_SetSoftDisplayFilter) -( - BOOL Enable -) -{ - LOG_FUNC_ONE_ARG(Enable); - - LOG_IGNORED(); -} - -// LTCG specific D3DDevice_DeleteVertexShader function... -// This uses a custom calling convention where parameter is passed in EAX -// UNTESTED - Need test-case! -VOID __stdcall XTL::EMUPATCH(D3DDevice_DeleteVertexShader_0) -( -) -{ - DWORD Handle; - - __asm { - mov Handle, eax - } - - LOG_TEST_CASE("Validate this function!"); - return EMUPATCH(D3DDevice_DeleteVertexShader)(Handle); -} - -// ****************************************************************** -// * patch: D3DDevice_DeleteVertexShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_DeleteVertexShader) -( - DWORD Handle -) -{ - LOG_FUNC_ONE_ARG(Handle); - - XB_TRMP(D3DDevice_DeleteVertexShader)(Handle); - - CxbxImpl_DeleteVertexShader(Handle); -} - -// ****************************************************************** -// * patch: D3DDevice_SelectVertexShaderDirect -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SelectVertexShaderDirect) -( - X_VERTEXATTRIBUTEFORMAT *pVAF, - DWORD Address -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pVAF) - LOG_FUNC_ARG(Address) - LOG_FUNC_END; - - CxbxImpl_SelectVertexShaderDirect(pVAF, Address); -} - -// ****************************************************************** -// * patch: D3DDevice_GetShaderConstantMode -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetShaderConstantMode) -( - DWORD *pMode -) -{ - LOG_FUNC_ONE_ARG(pMode); - - if(pMode) - { - *pMode = g_Xbox_VertexShaderConstantMode; - } -} - -// ****************************************************************** -// * patch: D3DDevice_GetVertexShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShader) -( - DWORD *pHandle -) -{ - LOG_FUNC_ONE_ARG(pHandle); - - if(pHandle) - { - (*pHandle) = g_Xbox_VertexShader_Handle; - } -} - -// ****************************************************************** -// * patch: D3DDevice_GetVertexShaderConstant -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderConstant) -( - INT Register, - void *pConstantData, - DWORD ConstantCount -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Register) - LOG_FUNC_ARG(pConstantData) - LOG_FUNC_ARG(ConstantCount) - LOG_FUNC_END; - - // Xbox vertex shader constants range from -96 to 95 - // The host does not support negative, so we adjust to 0..191 - Register += X_D3DSCM_CORRECTION; - - HRESULT hRet = g_pD3DDevice->GetVertexShaderConstantF - ( - Register, - (float*)pConstantData, // TODO : Validate this work correctly under D3D9 - ConstantCount - ); - - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetVertexShaderConstant"); -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderInputDirect -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderInputDirect) -( - X_VERTEXATTRIBUTEFORMAT *pVAF, - UINT StreamCount, - X_STREAMINPUT *pStreamInputs -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pVAF) - LOG_FUNC_ARG(StreamCount) - LOG_FUNC_ARG(pStreamInputs) - LOG_FUNC_END; - - // If pVAF is given, it's copied into a global Xbox VertexBuffer struct and - // D3DDevice_SetVertexShaderInput is called with Handle set to that address, or-ed with 1 (X_D3DFVF_RESERVED0) - // Otherwise, D3DDevice_SetVertexShaderInput is called with Handle 0. - - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_GetVertexShaderInput -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput) -( - DWORD *pHandle, - UINT *pStreamCount, - X_STREAMINPUT *pStreamInputs -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pHandle) - LOG_FUNC_ARG(pStreamCount) - LOG_FUNC_ARG(pStreamInputs) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - return 0; -} - -// ****************************************************************** -// * patch: D3DDevice_SetVertexShaderInput -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderInput) -( - DWORD Handle, - UINT StreamCount, - X_STREAMINPUT *pStreamInputs -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(StreamCount) - LOG_FUNC_ARG(pStreamInputs) - LOG_FUNC_END; - - // When this API is in effect, VertexBuffers as set by Xbox SetStreamSource are disregarded, - // instead, the pStreamInputs[].VertexBuffer streams are used. - - // If Handle is NULL, all VertexShader input state is cleared (after which the VertexBuffers as set by SetStreamSource are used once again). - - // Otherwise, Handle is the address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) - // The given pStreamInputs are stored in a global array, and the NV2A is programmed to read - // each vertex attribute (as defined in the given VertexShader.VertexAttribute.Slots[]) to read - // the attribute data from the pStreamInputs[slot].VertexBuffer + pStreamInputs[slot].Offset + VertexShader.VertexAttribute.Slots[slot].Offset - - CxbxImpl_SetVertexShaderInput(Handle, StreamCount, pStreamInputs); -} - -// ****************************************************************** -// * patch: D3DDevice_RunVertexStateShader -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_RunVertexStateShader) -( - DWORD Address, - CONST FLOAT *pData -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Address) - LOG_FUNC_ARG(pData) - LOG_FUNC_END; - - // If pData is assigned, pData[0..3] is pushed towards nv2a transform data registers - // then sends the nv2a a command to launch the vertex shader function located at Address - - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_LoadVertexShaderProgram -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShaderProgram) -( - CONST DWORD *pFunction, - DWORD Address - ) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pFunction) - LOG_FUNC_ARG(Address) - LOG_FUNC_END; - - CxbxImpl_LoadVertexShaderProgram(pFunction, Address); -} - -// ****************************************************************** -// * patch: D3DDevice_SetDepthClipPlanes -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_SetDepthClipPlanes) -( - FLOAT Near, - FLOAT Far, - DWORD Flags -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Near) - LOG_FUNC_ARG(Far) - LOG_FUNC_ARG(Flags) - LOG_FUNC_END; - - HRESULT hRet = D3D_OK; - - switch(Flags) // Member of X_D3DSET_DEPTH_CLIP_PLANES_FLAGS enum - { - case X_D3DSDCP_SET_VERTEXPROGRAM_PLANES: - { - // Sets the depth-clipping planes used whenever vertex shader programs are active - // TODO - - // pDevice->fNear = Near - // pDevice->fFar = Far - } - break; - - case X_D3DSDCP_SET_FIXEDFUNCTION_PLANES: - { - // Sets the depth-clipping planes used whenever the fixed-function pipeline is in use. - // TODO - - // pDevice->fNear = Near - // pDevice->fFar = Far - } - break; - - case X_D3DSDCP_USE_DEFAULT_VERTEXPROGRAM_PLANES: - { - // Causes Direct3D to disregard the depth-clipping planes set when using X_D3DSDCP_SET_VERTEXPROGRAM_PLANE. - // Direct3D will resume using its own internally calculated clip planes when vertex shader programs are active. - // TODO - } - break; - - case X_D3DSDCP_USE_DEFAULT_FIXEDFUNCTION_PLANES: - { - // Causes Direct3D to disregard the depth-clipping planes set when using X_D3DSDCP_SET_FIXEDFUNCTION_PLANES. - // Direct3D will resume using its own internally calculated clip planes when the fixed-function pipeline is active. - // TODO - } - break; - - default: - EmuLog(LOG_LEVEL::WARNING, "Unknown SetDepthClipPlanes Flags provided"); - } - - // TODO - - - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_InsertFence -// ****************************************************************** -DWORD WINAPI XTL::EMUPATCH(D3DDevice_InsertFence)() -{ - LOG_FUNC(); - - // TODO: Actually implement this - DWORD dwRet = 0x8000BEEF; - - LOG_UNIMPLEMENTED(); - - return dwRet; -} - -// ****************************************************************** -// * patch: D3DDevice_IsFencePending -// ****************************************************************** -BOOL WINAPI XTL::EMUPATCH(D3DDevice_IsFencePending) -( - DWORD Fence -) -{ - LOG_FUNC_ONE_ARG(Fence); - - // TODO: Implement - LOG_UNIMPLEMENTED(); - - return FALSE; -} - -// ****************************************************************** -// * patch: D3DDevice_BlockOnFence -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_BlockOnFence) -( - DWORD Fence -) -{ - LOG_FUNC_ONE_ARG(Fence); - - // TODO: Implement - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: IDirect3DResource8_BlockUntilNotBusy -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DResource_BlockUntilNotBusy) -( - X_D3DResource *pThis -) -{ - LOG_FUNC_ONE_ARG(pThis); - - // TODO: Implement - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_SetScreenSpaceOffset -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetScreenSpaceOffset) -( - FLOAT x, - FLOAT y -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(x) - LOG_FUNC_ARG(y) - LOG_FUNC_END; - - // No need to log this, it's safe to ignore. - //EmuLog(LOG_LEVEL::WARNING, "EmuD3DDevice_SetScreenSpaceOffset ignored"); -} - -// ****************************************************************** -// * patch: D3DDevice_InsertCallback -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_InsertCallback) -( - X_D3DCALLBACKTYPE Type, - X_D3DCALLBACK pCallback, - DWORD Context -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Type) - LOG_FUNC_ARG(pCallback) - LOG_FUNC_ARG(Context) - LOG_FUNC_END; - - CxbxImpl_InsertCallback(Type, pCallback, Context); - - LOG_INCOMPLETE(); -} - -// ****************************************************************** -// * patch: D3DDevice_DrawRectPatch -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_DrawRectPatch) -( - UINT Handle, - CONST FLOAT *pNumSegs, - CONST D3DRECTPATCH_INFO *pRectPatchInfo -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(pNumSegs) - LOG_FUNC_ARG(pRectPatchInfo) - LOG_FUNC_END; - - CxbxUpdateNativeD3DResources(); - - HRESULT hRet = g_pD3DDevice->DrawRectPatch( Handle, pNumSegs, pRectPatchInfo ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawRectPatch"); - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_DrawTriPatch -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_DrawTriPatch) -( - UINT Handle, - CONST FLOAT *pNumSegs, - CONST D3DTRIPATCH_INFO* pTriPatchInfo -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(pNumSegs) - LOG_FUNC_ARG(pTriPatchInfo) - LOG_FUNC_END; - - CxbxUpdateNativeD3DResources(); - - HRESULT hRet = g_pD3DDevice->DrawTriPatch(Handle, pNumSegs, pTriPatchInfo); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawTriPatch"); - - return hRet; -} - -#pragma warning(disable:4244) -// ****************************************************************** -// * patch: D3DDevice_GetProjectionViewportMatrix -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetProjectionViewportMatrix) -( - D3DXMATRIX *pProjectionViewport -) -{ - LOG_FUNC_ONE_ARG(pProjectionViewport); - - // blueshogun96 1/25/10 - // It's been almost 3 years, but I think this is a better - // implementation. Still probably not right, but better - // then before. - - HRESULT hRet; - D3DXMATRIX Out, mtxProjection, mtxViewport; - D3DVIEWPORT Viewport; - - // Get current viewport - hRet = g_pD3DDevice->GetViewport(&Viewport); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetViewport - Unable to get viewport!"); - - // Get current projection matrix - hRet = g_pD3DDevice->GetTransform(D3DTS_PROJECTION, &mtxProjection); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetTransform - Unable to get projection matrix!"); - - // Clear the destination matrix - ::ZeroMemory(&Out, sizeof(D3DMATRIX)); - - // Create the Viewport matrix manually - // Direct3D8 doesn't give me everything I need in a viewport structure - // (one thing I REALLY HATE!) so some constants will have to be used - // instead. - - float ClipWidth = 2.0f; - float ClipHeight = 2.0f; - float ClipX = -1.0f; - float ClipY = 1.0f; - float Width = DWtoF(Viewport.Width); - float Height = DWtoF(Viewport.Height); - - D3DXMatrixIdentity(&mtxViewport); - mtxViewport._11 = Width / ClipWidth; - mtxViewport._22 = -(Height / ClipHeight); - mtxViewport._41 = -(ClipX * mtxViewport._11); - mtxViewport._42 = -(ClipY * mtxViewport._22); - - // Multiply projection and viewport matrix together - Out = mtxProjection * mtxViewport; - - *pProjectionViewport = Out; - -// __asm int 3; -} -#pragma warning(default:4244) - -// ****************************************************************** -// * patch: D3DDevice_SetStateVB (D3D::CDevice::SetStateVB) -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStateVB)( ULONG Unknown1 ) -{ - LOG_FUNC_ONE_ARG(Unknown1); - - // TODO: Anything? -// __asm int 3; - - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_SetStateUP (D3D::CDevice::SetStateUP) -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStateUP)() -{ - LOG_FUNC(); - - LOG_UNIMPLEMENTED(); - - // TODO: Anything? -// __asm int 3; - -} - -// ****************************************************************** -// * patch: D3DDevice_SetStipple -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3DDevice_SetStipple)( DWORD* pPattern ) -{ - LOG_FUNC_ONE_ARG(pPattern); - - // We need an OpenGL port... badly - - LOG_IGNORED(); -} - -// ****************************************************************** -// * patch: D3DDevice_SetSwapCallback -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3DDevice_SetSwapCallback) -( - X_D3DSWAPCALLBACK pCallback -) -{ - LOG_FUNC_ONE_ARG(pCallback); - - g_pXbox_SwapCallback = pCallback; -} - -// ****************************************************************** -// * patch: D3DDevice_PersistDisplay -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_PersistDisplay)() -{ - LOG_FUNC(); - - LOG_INCOMPLETE(); - - // TODO: This function simply saves a copy of the display to a surface and persists it in contiguous memory - // This function, if ever required, can be implemented as the following - // 1. Check for an existing persisted surface via AvGetSavedDataAddress, free it if necessary - // 2. Create an Xbox format surface with the same size and format as active display - // 3. Copy the host framebuffer to the xbox surface, converting format if necessary - // 4. Set the display mode via AvSetDisplayMode to the same format as the persisted surface, - // passing the ->Data pointer of the xbox surface as the framebuffer pointer. - // 5. Use MmPersistContigousMemory to persist the surface data across reboot - // 6. Call AvSetSavedDataAddress, passing the xbox surface data pointer - - // Call the native Xbox function so that AvSetSavedDataAddress is called and the VMManager can know its correct address - if (XB_TRMP(D3DDevice_PersistDisplay)) { - return XB_TRMP(D3DDevice_PersistDisplay)(); - } - return 0; -} - -// ****************************************************************** -// * patch: D3DDevice_PrimeVertexCache -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_PrimeVertexCache) -( - UINT VertexCount, - WORD *pIndexData -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(VertexCount) - LOG_FUNC_ARG(pIndexData) - LOG_FUNC_END; - - // TODO: Implement - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_SetModelView -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetModelView) -( - CONST D3DMATRIX *pModelView, - CONST D3DMATRIX *pInverseModelView, - CONST D3DMATRIX *pComposite -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pModelView) - LOG_FUNC_ARG(pInverseModelView) - LOG_FUNC_ARG(pComposite) - LOG_FUNC_END; - - // TODO: Implement - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_FlushVertexCache -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3DDevice_FlushVertexCache)() -{ - LOG_FUNC(); - - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_GetModelView -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetModelView)(D3DXMATRIX* pModelView) -{ - LOG_FUNC_ONE_ARG(pModelView); - - D3DXMATRIX mtxWorld, mtxView; - - // I hope this is right - g_pD3DDevice->GetTransform( D3DTS_WORLD, &mtxWorld ); - g_pD3DDevice->GetTransform( D3DTS_VIEW, &mtxView ); - - *pModelView = mtxWorld * mtxView; - - return D3D_OK; -} - - -DWORD PushBuffer[64 * 1024 / sizeof(DWORD)]; - -// ****************************************************************** -// * patch: D3D_SetCommonDebugRegisters -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3D_SetCommonDebugRegisters)() -{ - LOG_FUNC(); - - // NOTE: I added this because I was too lazy to deal with emulating certain render - // states that use it. - - LOG_UNIMPLEMENTED(); - -} - -// ****************************************************************** -// * patch: D3DDevice_IsBusy -// ****************************************************************** -BOOL WINAPI XTL::EMUPATCH(D3DDevice_IsBusy)() -{ - LOG_FUNC(); - - // NOTE: This function returns FALSE when the NV2A FIFO is empty/complete, or NV_PGRAPH_STATUS = 0 - // Otherwise, it returns true. - - return FALSE; -} - -// ****************************************************************** -// * patch: D3D_BlockOnTime -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3D_BlockOnTime)( DWORD Unknown1, int Unknown2 ) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Unknown1) - LOG_FUNC_ARG(Unknown2) - LOG_FUNC_END; - - // NOTE: This function is not meant to be emulated. Just use it to find out - // the function that is calling it, and emulate that instead!!! If necessary, - // create an XRef... - - //__asm int 3; - - LOG_UNIMPLEMENTED(); -} - -bool DestroyResource_Common(XTL::X_D3DResource* pResource) -{ - if (pResource == g_pXbox_RenderTarget) { - LOG_TEST_CASE("Skipping Release of active Xbox Render Target"); - return false; - } - - if (pResource == g_pXbox_DepthStencil) { - LOG_TEST_CASE("Skipping Release of active Xbox Depth Stencil"); - return false; - } - - if (pResource == g_pXbox_BackBufferSurface) { - LOG_TEST_CASE("Skipping Release of active Xbox BackBuffer"); - return false; - } - - if (pResource == g_pXbox_DefaultDepthStencilSurface) { - LOG_TEST_CASE("Skipping Release of default Xbox Depth Stencil"); - return false; - } - - for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) { - if (pResource == g_pXbox_SetTexture[i]) { - // This shouldn't happen, since texture resources that get destroyed, - // shouldn't be set to any stage anymore. - LOG_TEST_CASE("Skipping Release of active Xbox Texture"); - return false; - } - } - - // Release the host copy (if it exists!) - FreeHostResource(GetHostResourceKey(pResource)); - - return true; -} - -// ****************************************************************** -// * patch: D3D_DestroyResource -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3D_DestroyResource)(X_D3DResource* pResource) -{ - LOG_FUNC_ONE_ARG(pResource); - - if (DestroyResource_Common(pResource)) { - // Call the Xbox version of DestroyResource - XB_TRMP(D3D_DestroyResource)(pResource); - } -} - -// ****************************************************************** -// * patch: D3D_DestroyResource_LTCG -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3D_DestroyResource__LTCG)() -{ - X_D3DResource* pResource; - __asm { - mov pResource, edi - } - - if (DestroyResource_Common(pResource)) { - // Call the Xbox version of DestroyResource - __asm { - mov edi, pResource - call XB_TRMP(D3D_DestroyResource__LTCG) - } - } -} - - -// ****************************************************************** -// * patch: D3DDevice_SetRenderTargetFast -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetRenderTargetFast) -( - X_D3DSurface *pRenderTarget, - X_D3DSurface *pNewZStencil, - DWORD Flags -) -{ - LOG_FORWARD("D3DDevice_SetRenderTarget"); - - // Redirect to the standard version. - - EMUPATCH(D3DDevice_SetRenderTarget)(pRenderTarget, pNewZStencil); -} - -// ****************************************************************** -// * patch: D3D::LazySetPointParams -// ****************************************************************** -void WINAPI XTL::EMUPATCH(D3D_LazySetPointParams) -( - void* Device -) -{ - LOG_FUNC_ONE_ARG(Device); - - LOG_UNIMPLEMENTED(); -} - -// ****************************************************************** -// * patch: D3DDevice_GetMaterial -// ****************************************************************** -VOID WINAPI XTL::EMUPATCH(D3DDevice_GetMaterial) -( - X_D3DMATERIAL8* pMaterial -) -{ - LOG_FUNC_ONE_ARG(pMaterial); - - HRESULT hRet = D3D_OK; - - if (pMaterial) - { - hRet = g_pD3DDevice->GetMaterial(pMaterial); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetMaterial"); - } - - if(FAILED(hRet)) - { - EmuLog(LOG_LEVEL::WARNING, "We're lying about getting a material!"); - hRet = D3D_OK; - } -} - -// LTCG specific D3DDevice_SetPixelShaderConstant function... -// This uses a custom calling convention where parameter is passed in ECX, EAX -// TODO: Log function is not working due lost parameter in EAX. -// Test-case: Otogi 2, Ninja Gaiden: Black -VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPixelShaderConstant_4) -( - CONST PVOID pConstantData -) -{ - DWORD Register; - DWORD ConstantCount; - - __asm { - mov Register, ecx - mov ConstantCount, eax - } - - //LOG_FUNC_BEGIN - // LOG_FUNC_ARG(Register) - // LOG_FUNC_ARG(pConstantData) - // LOG_FUNC_ARG(ConstantCount) - // LOG_FUNC_END; - EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetPixelShaderConstant_4(Register : %d pConstantData : %08X ConstantCount : %d);", Register, pConstantData, ConstantCount); - - HRESULT hRet = g_pD3DDevice->SetPixelShaderConstantF - ( - Register, - (PixelShaderConstantType*)pConstantData, - ConstantCount - ); - //DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetPixelShaderConstant"); - - if(FAILED(hRet)) - { - EmuLog(LOG_LEVEL::WARNING, "We're lying about setting a pixel shader constant!"); - - hRet = D3D_OK; - } -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** +#define LOG_PREFIX CXBXR_MODULE::D3D8 + +#ifdef CXBXR_EMU_EXPORTS // DbgConsole only in Cxbx/cxbxr, not in cxbxr-emu + #undef INCLUDE_DBG_CONSOLE +#else + #define INCLUDE_DBG_CONSOLE +#endif +#include "common\util\hasher.h" +#include +#include + + +#include +#include "common\util\CxbxUtil.h" +#include "CxbxVersion.h" +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\Emu.h" +#include "EmuShared.h" +#ifdef INCLUDE_DBG_CONSOLE +#include "gui\DbgConsole.h" +#endif +#include "core\hle\D3D8\ResourceTracker.h" +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For LPDIRECTDRAWSURFACE7 +#include "core\hle\D3D8\XbVertexBuffer.h" +#include "core\hle\D3D8\XbVertexShader.h" +#include "core\hle\D3D8\XbPixelShader.h" // For DxbxUpdateActivePixelShader +#include "core\hle\D3D8\XbPushBuffer.h" +#include "core\kernel\memory-manager\VMManager.h" // for g_VMManager +#include "core\hle\XAPI\Xapi.h" // For EMUPATCH +#include "core\hle\D3D8\XbConvert.h" +#include "Logging.h" +#include "..\XbD3D8Logging.h" +#include "core\hle\Intercept.hpp" // for bLLE_GPU +#include "devices\video\nv2a.h" // For GET_MASK, NV_PGRAPH_CONTROL_0, PUSH_METHOD +#include "gui/resource/ResCxbx.h" +#include "RenderStates.h" +#include "TextureStates.h" +#include "WalkIndexBuffer.h" +#include "core\kernel\common\strings.hpp" // For uem_str +#include "common\input\SdlJoystick.h" +#include "common/util/strConverter.hpp" // for utf8_to_utf16 +#include "VertexShaderSource.h" + +#include +#include +#include +#include +#include + +XboxRenderStateConverter XboxRenderStates; +XboxTextureStateConverter XboxTextureStates; + +// Allow use of time duration literals (making 16ms, etc possible) +using namespace std::literals::chrono_literals; + +// Global(s) +HWND g_hEmuWindow = NULL; // rendering window +IDirect3DDevice *g_pD3DDevice = nullptr; // Direct3D Device + +// Static Variable(s) +static IDirectDrawSurface7 *g_pDDSPrimary = nullptr; // DirectDraw7 Primary Surface +static IDirectDrawClipper *g_pDDClipper = nullptr; // DirectDraw7 Clipper +static IDirectDraw7 *g_pDD7 = nullptr; // DirectDraw7 +static HMONITOR g_hMonitor = NULL; // Handle to DirectDraw monitor +static GUID g_ddguid = { 0 }; // DirectDraw driver GUID +static DDCAPS g_DriverCaps = { 0 }; + +static bool g_bSupportsFormatSurface[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support surface format? +static bool g_bSupportsFormatSurfaceRenderTarget[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support surface format? +static bool g_bSupportsFormatSurfaceDepthStencil[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support surface format? +static bool g_bSupportsFormatTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support texture format? +static bool g_bSupportsFormatTextureRenderTarget[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support texture format? +static bool g_bSupportsFormatTextureDepthStencil[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false };// Does device support texture format? +static bool g_bSupportsFormatVolumeTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support surface format? +static bool g_bSupportsFormatCubeTexture[XTL::X_D3DFMT_LIN_R8G8B8A8 + 1] = { false }; // Does device support surface format? +static HBRUSH g_hBgBrush = NULL; // Background Brush +static volatile bool g_bRenderWindowActive = false; +static BOOL g_bIsFauxFullscreen = FALSE; +static DWORD g_OverlaySwap = 0; // Set in D3DDevice_UpdateOverlay +static int g_iWireframe = 0; // wireframe toggle +static bool g_bHack_UnlockFramerate = false; // ignore the xbox presentation interval +static bool g_bHasDepth = false; // Does device have a Depth Buffer? +static bool g_bHasStencil = false; // Does device have a Stencil Buffer? +static DWORD g_dwPrimPerFrame = 0; // Number of primitives within one frame + +static Settings::s_video g_XBVideo; + +// D3D based variables +static IDirect3D *g_pDirect3D = nullptr; + D3DCAPS g_D3DCaps = {}; // Direct3D Caps +static IDirect3DVertexBuffer *g_pDummyBuffer = nullptr; // Dummy buffer, used to set unused stream sources with +static IDirect3DIndexBuffer *g_pClosingLineLoopHostIndexBuffer = nullptr; +static IDirect3DIndexBuffer *g_pQuadToTriangleHostIndexBuffer = nullptr; + +static bool g_bEnableHostQueryVisibilityTest = true; +static std::stack g_HostQueryVisibilityTests; +static std::map g_HostVisibilityTestMap; + +// cached Direct3D state variable(s) +static size_t g_QuadToTriangleHostIndexBuffer_Size = 0; // = NrOfQuadIndices +static INDEX16 *g_pQuadToTriangleIndexData = nullptr; +static size_t g_QuadToTriangleIndexData_Size = 0; // = NrOfQuadIndices + +static CxbxVertexBufferConverter VertexBufferConverter = {}; + +struct { + XTL::X_D3DSurface Surface; + RECT SrcRect; + RECT DstRect; + BOOL EnableColorKey; + D3DCOLOR ColorKey; +} g_OverlayProxy; + +typedef struct { + // Arguments to D3DDevice_InsertCallback : + XTL::X_D3DCALLBACK pCallback; + XTL::X_D3DCALLBACKTYPE Type; + XTL::DWORD Context; +} s_Xbox_Callback; + +static std::queue g_Xbox_CallbackQueue; +static bool g_bHack_DisableHostGPUQueries = false; // TODO : Make configurable +static IDirect3DQuery *g_pHostQueryWaitForIdle = nullptr; +static IDirect3DQuery *g_pHostQueryCallbackEvent = nullptr; + +// Vertex buffer symbols, declared in XbVertexBuffer.cpp +extern void CxbxImpl_SetStreamSource(UINT StreamNumber, XTL::X_D3DVertexBuffer* pStreamData, UINT Stride); + +static std::condition_variable g_VBConditionVariable; // Used in BlockUntilVerticalBlank +static std::mutex g_VBConditionMutex; // Used in BlockUntilVerticalBlank +static DWORD g_VBLastSwap = 0; + +static XTL::DWORD g_Xbox_PresentationInterval_Default = D3DPRESENT_INTERVAL_IMMEDIATE; + XTL::DWORD g_Xbox_PresentationInterval_Override = 0; +static XTL::X_D3DSWAPDATA g_Xbox_SwapData = {0}; // current swap information +static XTL::X_D3DSWAPCALLBACK g_pXbox_SwapCallback = xbnullptr; // Swap/Present callback routine +static XTL::X_D3DVBLANKDATA g_Xbox_VBlankData = {0}; // current vertical blank information +static XTL::X_D3DVBLANKCALLBACK g_pXbox_VerticalBlankCallback = xbnullptr; // Vertical-Blank callback routine + + XTL::X_D3DSurface *g_pXbox_BackBufferSurface = xbnullptr; +static XTL::X_D3DSurface *g_pXbox_DefaultDepthStencilSurface = xbnullptr; + XTL::X_D3DSurface *g_pXbox_RenderTarget = xbnullptr; +static XTL::X_D3DSurface *g_pXbox_DepthStencil = xbnullptr; + XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType = XTL::X_D3DMULTISAMPLE_NONE; + + XTL::X_VERTEXSHADERCONSTANTMODE g_Xbox_VertexShaderConstantMode = X_D3DSCM_192CONSTANTS; // Set by D3DDevice_SetShaderConstantMode, TODO : Move to XbVertexShader.cpp +static XTL::DWORD g_Xbox_BaseVertexIndex = 0; // Set by D3DDevice_SetIndices, read by D3DDevice_DrawIndexedVertices : a value that's effectively added to every vertex index (as stored in an index buffer) by multiplying this by vertex stride and added to the vertex buffer start (see BaseVertexIndex in CxbxDrawIndexed) +static XTL::DWORD *g_pXbox_BeginPush_Buffer = xbnullptr; // primary push buffer + + XTL::X_PixelShader* g_pXbox_PixelShader = xbnullptr; +static XTL::PVOID g_pXbox_Palette_Data[XTL::X_D3DTS_STAGECOUNT] = { xbnullptr, xbnullptr, xbnullptr, xbnullptr }; // cached palette pointer +static unsigned g_Xbox_Palette_Size[XTL::X_D3DTS_STAGECOUNT] = { 0 }; // cached palette size + + + XTL::X_D3DBaseTexture *g_pXbox_SetTexture[XTL::X_D3DTS_STAGECOUNT] = {0,0,0,0}; // Set by our D3DDevice_SetTexture and D3DDevice_SwitchTexture patches +static XTL::X_D3DBaseTexture CxbxActiveTextureCopies[XTL::X_D3DTS_STAGECOUNT] = {}; // Set by D3DDevice_SwitchTexture. Cached active texture + +/* Unused : +static XTL::DWORD *g_Xbox_D3DDevice; // TODO: This should be a D3DDevice structure + +static DWORD g_dwVertexShaderUsage = 0; // Unused. If needed, move to XbVertexShader.cpp +*/ + + XTL::DWORD g_Xbox_VertexShader_Handle = 0; + +// Static Function(s) +static BOOL WINAPI EmuEnumDisplayDevices(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm); +static DWORD WINAPI EmuRenderWindow(LPVOID); +static DWORD WINAPI EmuCreateDeviceProxy(LPVOID); +static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +static DWORD WINAPI EmuUpdateTickCount(LPVOID); +static inline void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize); +static void UpdateCurrentMSpFAndFPS(); // Used for benchmarking/fps count + +extern void UpdateFPSCounter(); + +#define CXBX_D3DCOMMON_IDENTIFYING_MASK (X_D3DCOMMON_TYPE_MASK | X_D3DCOMMON_D3DCREATED) + +typedef struct resource_key_hash { + // All Xbox X_D3DResource structs have these fields : + DWORD Common; // We set this to the CXBX_D3DCOMMON_IDENTIFYING_MASK bits of the source Common field + DWORD Data; // We set this as-is to a copy of the source Data field + // DWORD Lock; // We leave away the source Lock field, since it's entirely volatile (not deterministic) + union { + struct { + // For non-pixel-containers, we set the Xbox resource address for now (TODO : Come up with something better) : + xbaddr ResourceAddr; // We set this as-is + }; + struct { + // For XTL::X_D3DPixelContainer's we also set these fields : + DWORD Format; // We set this as-is + DWORD Size; // We set this as-is + // For X_D3DFMT_P8 paletized pixel-containers, we also set this field : + uint64_t PaletteHash; + }; + }; + + // These operator overloads are required to use resource_key_t in resource_cache_t : + bool operator==(const struct resource_key_hash& other) const + { + return (Common == other.Common) + && (Data == other.Data) + && (Format == other.Format) + && (Size == other.Size) + && (PaletteHash == other.PaletteHash); + // Note : ResourceAddr doesn't need comparison, since it's union'ed with Format,Size,PaletteHash already + } + + // See https://marknelson.us/posts/2011/09/03/hash-functions-for-c-unordered-containers.html + size_t operator()(const struct resource_key_hash& value) const + { + return (size_t)ComputeHash((void*)&value, sizeof(value)); + } +} resource_key_t; + +// information passed to the create device proxy thread +struct EmuD3D8CreateDeviceProxyData +{ + // Set by EmuD3DInit() + XTL::UINT Adapter; + D3DDEVTYPE DeviceType; + HWND hFocusWindow; + // Set byt EMUPATCH(Direct3D_CreateDevice) + XTL::X_D3DPRESENT_PARAMETERS XboxPresentationParameters; + volatile bool bReady; + volatile bool bCreate; // false : release + // Set by EmuCreateDeviceProxy() + XTL::DWORD BehaviorFlags; + D3DPRESENT_PARAMETERS HostPresentationParameters; + volatile HRESULT hRet; +} +g_EmuCDPD = {0}; + +// Declare trampolines +#define XB_TRAMPOLINES(XB_MACRO) \ + XB_MACRO(HRESULT, WINAPI, D3DDevice_CreateVertexShader, (CONST DWORD*, CONST DWORD*, DWORD*, DWORD) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_DeleteVertexShader, (DWORD) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_GetBackBuffer, (INT, D3DBACKBUFFER_TYPE, XTL::X_D3DSurface**) ); \ + XB_MACRO(XTL::X_D3DSurface*, WINAPI, D3DDevice_GetBackBuffer2, (INT) ); \ + XB_MACRO(HRESULT, WINAPI, D3DDevice_GetDepthStencilSurface, (XTL::X_D3DSurface**) ); \ + XB_MACRO(XTL::X_D3DSurface*, WINAPI, D3DDevice_GetDepthStencilSurface2, (VOID) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_GetDisplayMode, (XTL::X_D3DDISPLAYMODE*) ); \ + XB_MACRO(HRESULT, WINAPI, D3DDevice_GetRenderTarget, (XTL::X_D3DSurface**) ); \ + XB_MACRO(XTL::X_D3DSurface*, WINAPI, D3DDevice_GetRenderTarget2, (VOID) ); \ + XB_MACRO(HRESULT, WINAPI, D3DDevice_LightEnable, (DWORD, BOOL) ); \ + /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShader, (DWORD, DWORD) );*/\ + /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShaderProgram, (CONST DWORD*, DWORD) );*/\ + /*XB_MACRO(VOID, __stdcall, D3DDevice_LoadVertexShader_0, () );*/\ + /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShader_4, (DWORD) );*/\ + XB_MACRO(HRESULT, WINAPI, D3DDevice_PersistDisplay, (VOID) ); \ + XB_MACRO(HRESULT, WINAPI, D3DDevice_Reset, (XTL::X_D3DPRESENT_PARAMETERS*) ); \ + /*XB_MACRO(VOID, WINAPI, D3DDevice_SelectVertexShader, (DWORD, DWORD) );*/\ + /*XB_MACRO(VOID, __stdcall, D3DDevice_SelectVertexShader_0, () );*/\ + /*XB_MACRO(VOID, __stdcall, D3DDevice_SelectVertexShader_4, (DWORD) );*/\ + /*XB_MACRO(VOID, WINAPI, D3DDevice_SetGammaRamp, (DWORD, CONST X_D3DGAMMARAMP*) );*/\ + XB_MACRO(VOID, WINAPI, D3DDevice_SetIndices, (XTL::X_D3DIndexBuffer*, UINT) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetIndices_4, (UINT) ); \ + XB_MACRO(HRESULT, WINAPI, D3DDevice_SetLight, (DWORD, CONST XTL::X_D3DLIGHT8*) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetPixelShader, (DWORD) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetPixelShader_0, () ); \ + XB_MACRO(VOID, __fastcall, D3DDevice_SetRenderState_Simple, (DWORD, DWORD) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetRenderTarget, (XTL::X_D3DSurface*, XTL::X_D3DSurface*) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetStreamSource, (UINT, XTL::X_D3DVertexBuffer*, UINT) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetStreamSource_4, (UINT, XTL::X_D3DVertexBuffer*, UINT) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetStreamSource_8, (XTL::X_D3DVertexBuffer*, UINT) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetTexture, (DWORD, XTL::X_D3DBaseTexture*) ); \ + XB_MACRO(VOID, WINAPI, D3DDevice_SetTexture_4, (XTL::X_D3DBaseTexture*) ); \ + /*XB_MACRO(VOID, WINAPI, D3DDevice_SetVertexShader, (DWORD) );*/\ + /*XB_MACRO(VOID, WINAPI, D3DDevice_SetVertexShaderInput, (DWORD, UINT, XTL::X_STREAMINPUT*) );*/\ + XB_MACRO(VOID, WINAPI, D3DDevice_SetViewport, (CONST XTL::X_D3DVIEWPORT8*) ); \ + XB_MACRO(VOID, WINAPI, D3D_DestroyResource, (XTL::X_D3DResource*) ); \ + XB_MACRO(VOID, WINAPI, D3D_DestroyResource__LTCG, (VOID) ); \ + XB_MACRO(HRESULT, WINAPI, Direct3D_CreateDevice, (UINT, D3DDEVTYPE, HWND, DWORD, XTL::X_D3DPRESENT_PARAMETERS*, IDirect3DDevice**) ); \ + XB_MACRO(HRESULT, WINAPI, Direct3D_CreateDevice_16, (UINT, D3DDEVTYPE, HWND, XTL::X_D3DPRESENT_PARAMETERS*) ); \ + XB_MACRO(HRESULT, WINAPI, Direct3D_CreateDevice_4, (XTL::X_D3DPRESENT_PARAMETERS*) ); \ + XB_MACRO(VOID, WINAPI, Lock2DSurface, (XTL::X_D3DPixelContainer*, D3DCUBEMAP_FACES, UINT, D3DLOCKED_RECT*, RECT*, DWORD) ); \ + XB_MACRO(VOID, WINAPI, Lock3DSurface, (XTL::X_D3DPixelContainer*, UINT, D3DLOCKED_BOX*, D3DBOX*, DWORD) ); \ + +XB_TRAMPOLINES(XB_trampoline_declare); + +void LookupTrampolines() +{ + XB_TRAMPOLINES(XB_trampoline_lookup); +} + +#undef XB_TRAMPOLINES + +const char *CxbxGetErrorDescription(HRESULT hResult) +{ + // TODO : For D3D9, Use DXGetErrorDescription9(hResult) (requires another DLL though) + // See : https://www.fairyengine.com/articles/dxmultiviews.htm + // and : https://web.archive.org/web/20101231193248/https://www.gamedev.net/community/forums/showfaq.asp?forum_id=10 + // and : https://www.gamedev.net/community/forums/topic.asp?topic_id=16157 + // But https://blogs.msdn.microsoft.com/chuckw/2012/04/24/wheres-dxerr-lib/ + // suggests to use FormatMessage with FORMAT_MESSAGE_FROM_SYSTEM for DirectX errors + switch (hResult) + { + case D3DERR_INVALIDCALL: return "Invalid Call"; + case D3DERR_NOTAVAILABLE: return "Not Available"; + // case D3DERR_OUTOFVIDEOMEMORY: return "Out of Video Memory"; // duplicate of DDERR_OUTOFVIDEOMEMORY + + case D3D_OK: return "No error occurred."; +#if 0 + case D3DERR_BADMAJORVERSION: return "The service that you requested is unavailable in this major version of DirectX. (A major version denotes a primary release, such as DirectX 6.0.) "; + case D3DERR_BADMINORVERSION: return "The service that you requested is available in this major version of DirectX, but not in this minor version. Get the latest version of the component run time from Microsoft. (A minor version denotes a secondary release, such as DirectX 6.1.) "; + case D3DERR_COLORKEYATTACHED: return "The application attempted to create a texture with a surface that uses a color key for transparency. "; +#endif + case D3DERR_CONFLICTINGTEXTUREFILTER: return "The current texture filters cannot be used together. "; + case D3DERR_CONFLICTINGTEXTUREPALETTE: return "The current textures cannot be used simultaneously. This generally occurs when a multitexture device requires that all palettized textures simultaneously enabled also share the same palette. "; + case D3DERR_CONFLICTINGRENDERSTATE: return "The currently set render states cannot be used together. "; +#if 0 + case D3DERR_DEVICEAGGREGATED: return "The IDirect3DDevice7::SetRenderTarget method was called on a device that was retrieved from the render target surface. "; + case D3DERR_EXECUTE_CLIPPED_FAILED: return "The execute buffer could not be clipped during execution. "; + case D3DERR_EXECUTE_CREATE_FAILED: return "The execute buffer could not be created. This typically occurs when no memory is available to allocate the execute buffer. "; + case D3DERR_EXECUTE_DESTROY_FAILED: return "The memory for the execute buffer could not be deallocated. "; + case D3DERR_EXECUTE_FAILED: return "The contents of the execute buffer are invalid and cannot be executed. "; + case D3DERR_EXECUTE_LOCK_FAILED: return "The execute buffer could not be locked. "; + case D3DERR_EXECUTE_LOCKED: return "The operation requested by the application could not be completed because the execute buffer is locked. "; + case D3DERR_EXECUTE_NOT_LOCKED: return "The execute buffer could not be unlocked because it is not currently locked. "; + case D3DERR_EXECUTE_UNLOCK_FAILED: return "The execute buffer could not be unlocked. "; + case D3DERR_INBEGIN: return "The requested operation cannot be completed while scene rendering is taking place. Try again after the scene is completed and the IDirect3DDevice7::EndScene method is called. "; + case D3DERR_INBEGINSTATEBLOCK: return "The operation cannot be completed while recording states for a state block. Complete recording by calling the IDirect3DDevice7::EndStateBlock method, and try again. "; + case D3DERR_INITFAILED: return "A rendering device could not be created because the new device could not be initialized. "; + case D3DERR_INVALID_DEVICE: return "The requested device type is not valid. "; + case D3DERR_INVALIDCURRENTVIEWPORT: return "The currently selected viewport is not valid. "; + case D3DERR_INVALIDMATRIX: return "The requested operation could not be completed because the combination of the currently set world, view, and projection matrices is invalid (the determinant of the combined matrix is 0). "; + case D3DERR_INVALIDPALETTE: return "The palette associated with a surface is invalid. "; + case D3DERR_INVALIDPRIMITIVETYPE: return "The primitive type specified by the application is invalid. "; + case D3DERR_INVALIDRAMPTEXTURE: return "Ramp mode is being used, and the texture handle in the current material does not match the current texture handle that is set as a render state. "; + case D3DERR_INVALIDSTATEBLOCK: return "The state block handle is invalid. "; + case D3DERR_INVALIDVERTEXFORMAT: return "The combination of flexible vertex format flags specified by the application is not valid. "; + case D3DERR_INVALIDVERTEXTYPE: return "The vertex type specified by the application is invalid. "; + case D3DERR_LIGHT_SET_FAILED: return "The attempt to set lighting parameters for a light object failed. "; + case D3DERR_LIGHTHASVIEWPORT: return "The requested operation failed because the light object is associated with another viewport. "; + case D3DERR_LIGHTNOTINTHISVIEWPORT: return "The requested operation failed because the light object has not been associated with this viewport. "; + case D3DERR_MATERIAL_CREATE_FAILED: return "The material could not be created. This typically occurs when no memory is available to allocate for the material. "; + case D3DERR_MATERIAL_DESTROY_FAILED: return "The memory for the material could not be deallocated. "; + case D3DERR_MATERIAL_GETDATA_FAILED: return "The material parameters could not be retrieved. "; + case D3DERR_MATERIAL_SETDATA_FAILED: return "The material parameters could not be set. "; + case D3DERR_MATRIX_CREATE_FAILED: return "The matrix could not be created. This can occur when no memory is available to allocate for the matrix. "; + case D3DERR_MATRIX_DESTROY_FAILED: return "The memory for the matrix could not be deallocated. "; + case D3DERR_MATRIX_GETDATA_FAILED: return "The matrix data could not be retrieved. This can occur when the matrix was not created by the current device. "; + case D3DERR_MATRIX_SETDATA_FAILED: return "The matrix data could not be set. This can occur when the matrix was not created by the current device. "; + case D3DERR_NOCURRENTVIEWPORT: return "The viewport parameters could not be retrieved because none have been set. "; + case D3DERR_NOTINBEGIN: return "The requested rendering operation could not be completed because scene rendering has not begun. Call IDirect3DDevice7::BeginScene to begin rendering, and try again. "; + case D3DERR_NOTINBEGINSTATEBLOCK: return "The requested operation could not be completed because it is only valid while recording a state block. Call the IDirect3DDevice7::BeginStateBlock method, and try again. "; + case D3DERR_NOVIEWPORTS: return "The requested operation failed because the device currently has no viewports associated with it. "; + case D3DERR_SCENE_BEGIN_FAILED: return "Scene rendering could not begin. "; + case D3DERR_SCENE_END_FAILED: return "Scene rendering could not be completed. "; + case D3DERR_SCENE_IN_SCENE: return "Scene rendering could not begin because a previous scene was not completed by a call to the IDirect3DDevice7::EndScene method. "; + case D3DERR_SCENE_NOT_IN_SCENE: return "Scene rendering could not be completed because a scene was not started by a previous call to the IDirect3DDevice7::BeginScene method. "; + case D3DERR_SETVIEWPORTDATA_FAILED: return "The viewport parameters could not be set. "; + case D3DERR_STENCILBUFFER_NOTPRESENT: return "The requested stencil buffer operation could not be completed because there is no stencil buffer attached to the render target surface. "; + case D3DERR_SURFACENOTINVIDMEM: return "The device could not be created because the render target surface is not located in video memory. (Hardware-accelerated devices require video-memory render target surfaces.) "; + case D3DERR_TEXTURE_BADSIZE: return "The dimensions of a current texture are invalid. This can occur when an application attempts to use a texture that has dimensions that are not a power of 2 with a device that requires them. "; + case D3DERR_TEXTURE_CREATE_FAILED: return "The texture handle for the texture could not be retrieved from the driver. "; + case D3DERR_TEXTURE_DESTROY_FAILED: return "The device was unable to deallocate the texture memory. "; + case D3DERR_TEXTURE_GETSURF_FAILED: return "The DirectDraw surface used to create the texture could not be retrieved. "; + case D3DERR_TEXTURE_LOAD_FAILED: return "The texture could not be loaded. "; + case D3DERR_TEXTURE_LOCK_FAILED: return "The texture could not be locked. "; + case D3DERR_TEXTURE_LOCKED: return "The requested operation could not be completed because the texture surface is currently locked. "; + case D3DERR_TEXTURE_NO_SUPPORT: return "The device does not support texture mapping. "; + case D3DERR_TEXTURE_NOT_LOCKED: return "The requested operation could not be completed because the texture surface is not locked. "; + case D3DERR_TEXTURE_SWAP_FAILED: return "The texture handles could not be swapped. "; + case D3DERR_TEXTURE_UNLOCK_FAILED: return "The texture surface could not be unlocked. "; +#endif + case D3DERR_TOOMANYOPERATIONS: return "The application is requesting more texture-filtering operations than the device supports. "; +#if 0 + case D3DERR_TOOMANYPRIMITIVES: return "The device is unable to render the provided number of primitives in a single pass. "; +#endif + case D3DERR_UNSUPPORTEDALPHAARG: return "The device does not support one of the specified texture-blending arguments for the alpha channel. "; + case D3DERR_UNSUPPORTEDALPHAOPERATION: return "The device does not support one of the specified texture-blending operations for the alpha channel. "; + case D3DERR_UNSUPPORTEDCOLORARG: return "The device does not support one of the specified texture-blending arguments for color values. "; + case D3DERR_UNSUPPORTEDCOLOROPERATION: return "The device does not support one of the specified texture-blending operations for color values. "; + case D3DERR_UNSUPPORTEDFACTORVALUE: return "The specified texture factor value is not supported by the device. "; + case D3DERR_UNSUPPORTEDTEXTUREFILTER: return "The specified texture filter is not supported by the device. "; +#if 0 + case D3DERR_VBUF_CREATE_FAILED: return "The vertex buffer could not be created. This can happen when there is insufficient memory to allocate a vertex buffer. "; + case D3DERR_VERTEXBUFFERLOCKED: return "The requested operation could not be completed because the vertex buffer is locked. "; + case D3DERR_VERTEXBUFFEROPTIMIZED: return "The requested operation could not be completed because the vertex buffer is optimized. (The contents of optimized vertex buffers are driver-specific and considered private.) "; + case D3DERR_VERTEXBUFFERUNLOCKFAILED: return "The vertex buffer could not be unlocked because the vertex buffer memory was overrun. Be sure that your application does not write beyond the size of the vertex buffer. "; + case D3DERR_VIEWPORTDATANOTSET: return "The requested operation could not be completed because viewport parameters have not yet been set. Set the viewport parameters by calling the IDirect3DDevice7::SetViewport method, and try again. "; + case D3DERR_VIEWPORTHASNODEVICE: return "The requested operation could not be completed because the viewport has not yet been associated with a device. Associate the viewport with a rendering device by calling the IDirect3DDevice3::AddViewport method, and try again. "; +#endif + case D3DERR_WRONGTEXTUREFORMAT: return "The pixel format of the texture surface is not valid. "; +#if 0 + case D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY: return "The requested operation could not be completed because the specified device requires system-memory depth-buffer surfaces. (Software rendering devices require system-memory depth buffers.) "; + case D3DERR_ZBUFF_NEEDS_VIDEOMEMORY: return "The requested operation could not be completed because the specified device requires video-memory depth-buffer surfaces. (Hardware-accelerated devices require video-memory depth buffers.) "; + case D3DERR_ZBUFFER_NOTPRESENT: return "The requested operation could not be completed because the render target surface does not have an attached depth buffer. "; + case DD_OK: return "The request completed successfully."; +#endif + case DDERR_ALREADYINITIALIZED: return "The object has already been initialized."; + case DDERR_BLTFASTCANTCLIP: return "A DirectDrawClipper object is attached to a source surface that has passed into a call to the IDirectDrawSurface7::BltFast method."; + case DDERR_CANNOTATTACHSURFACE: return "A surface cannot be attached to another requested surface."; + case DDERR_CANNOTDETACHSURFACE: return "A surface cannot be detached from another requested surface."; + case DDERR_CANTCREATEDC: return "Windows cannot create any more device contexts (DCs), or a DC has requested a palette-indexed surface when the surface had no palette and the display mode was not palette-indexed (in this case DirectDraw cannot select a proper palette into the DC)."; + case DDERR_CANTDUPLICATE: return "Primary and 3-D surfaces, or surfaces that are implicitly created, cannot be duplicated."; + case DDERR_CANTLOCKSURFACE: return "Access to this surface is refused because an attempt was made to lock the primary surface without DCI support."; + case DDERR_CANTPAGELOCK: return "An attempt to page-lock a surface failed. Page lock does not work on a display-memory surface or an emulated primary surface."; + case DDERR_CANTPAGEUNLOCK: return "An attempt to page-unlock a surface failed. Page unlock does not work on a display-memory surface or an emulated primary surface."; + case DDERR_CLIPPERISUSINGHWND: return "An attempt was made to set a clip list for a DirectDrawClipper object that is already monitoring a window handle."; + case DDERR_COLORKEYNOTSET: return "No source color key is specified for this operation."; + case DDERR_CURRENTLYNOTAVAIL: return "No support is currently available."; + case DDERR_DDSCAPSCOMPLEXREQUIRED: return "New for DirectX 7.0. The surface requires the DDSCAPS_COMPLEX flag."; + case DDERR_DCALREADYCREATED: return "A device context (DC) has already been returned for this surface. Only one DC can be retrieved for each surface."; + case DDERR_DEVICEDOESNTOWNSURFACE: return "Surfaces created by one DirectDraw device cannot be used directly by another DirectDraw device."; + case DDERR_DIRECTDRAWALREADYCREATED: return "A DirectDraw object representing this driver has already been created for this process."; + case DDERR_EXCEPTION: return "An exception was encountered while performing the requested operation."; + case DDERR_EXCLUSIVEMODEALREADYSET: return "An attempt was made to set the cooperative level when it was already set to exclusive."; + case DDERR_EXPIRED: return "The data has expired and is therefore no longer valid."; + case DDERR_GENERIC: return "There is an undefined error condition."; + case DDERR_HEIGHTALIGN: return "The height of the provided rectangle is not a multiple of the required alignment."; + case DDERR_HWNDALREADYSET: return "The DirectDraw cooperative-level window handle has already been set. It cannot be reset while the process has surfaces or palettes created."; + case DDERR_HWNDSUBCLASSED: return "DirectDraw is prevented from restoring state because the DirectDraw cooperative-level window handle has been subclassed."; + case DDERR_IMPLICITLYCREATED: return "The surface cannot be restored because it is an implicitly created surface."; + case DDERR_INCOMPATIBLEPRIMARY: return "The primary surface creation request does not match the existing primary surface."; + case DDERR_INVALIDCAPS: return "One or more of the capability bits passed to the callback function are incorrect."; + case DDERR_INVALIDCLIPLIST: return "DirectDraw does not support the provided clip list."; + case DDERR_INVALIDDIRECTDRAWGUID: return "The globally unique identifier (GUID) passed to the DirectDrawCreate function is not a valid DirectDraw driver identifier."; + case DDERR_INVALIDMODE: return "DirectDraw does not support the requested mode."; + case DDERR_INVALIDOBJECT: return "DirectDraw received a pointer that was an invalid DirectDraw object."; + case DDERR_INVALIDPARAMS: return "One or more of the parameters passed to the method are incorrect."; + case DDERR_INVALIDPIXELFORMAT: return "The pixel format was invalid as specified."; + case DDERR_INVALIDPOSITION: return "The position of the overlay on the destination is no longer legal."; + case DDERR_INVALIDRECT: return "The provided rectangle was invalid."; + case DDERR_INVALIDSTREAM: return "The specified stream contains invalid data."; + case DDERR_INVALIDSURFACETYPE: return "The surface was of the wrong type."; + case DDERR_LOCKEDSURFACES: return "One or more surfaces are locked, causing the failure of the requested operation."; + case DDERR_MOREDATA: return "There is more data available than the specified buffer size can hold."; + case DDERR_NEWMODE: return "New for DirectX 7.0. When IDirectDraw7::StartModeTest is called with the DDSMT_ISTESTREQUIRED flag, it may return this value to denote that some or all of the resolutions can and should be tested. IDirectDraw7::EvaluateMode returns this value to indicate that the test has switched to a new display mode."; + case DDERR_NO3D: return "No 3-D hardware or emulation is present."; + case DDERR_NOALPHAHW: return "No alpha-acceleration hardware is present or available, causing the failure of the requested operation."; + case DDERR_NOBLTHW: return "No blitter hardware is present."; + case DDERR_NOCLIPLIST: return "No clip list is available."; + case DDERR_NOCLIPPERATTACHED: return "No DirectDrawClipper object is attached to the surface object."; + case DDERR_NOCOLORCONVHW: return "No color-conversion hardware is present or available."; + case DDERR_NOCOLORKEY: return "The surface does not currently have a color key."; + case DDERR_NOCOLORKEYHW: return "There is no hardware support for the destination color key."; + case DDERR_NOCOOPERATIVELEVELSET: return "A create function was called without the IDirectDraw7::SetCooperativeLevel method."; + case DDERR_NODC: return "No device context (DC) has ever been created for this surface."; + case DDERR_NODDROPSHW: return "No DirectDraw raster-operation (ROP) hardware is available."; + case DDERR_NODIRECTDRAWHW: return "Hardware-only DirectDraw object creation is not possible; the driver does not support any hardware."; + case DDERR_NODIRECTDRAWSUPPORT: return "DirectDraw support is not possible with the current display driver."; + case DDERR_NODRIVERSUPPORT: return "New for DirectX 7.0. Testing cannot proceed because the display adapter driver does not enumerate refresh rates."; + case DDERR_NOEMULATION: return "Software emulation is not available."; + case DDERR_NOEXCLUSIVEMODE: return "The operation requires the application to have exclusive mode, but the application does not have exclusive mode."; + case DDERR_NOFLIPHW: return "Flipping visible surfaces is not supported."; + case DDERR_NOFOCUSWINDOW: return "An attempt was made to create or set a device window without first setting the focus window."; + case DDERR_NOGDI: return "No GDI is present."; + case DDERR_NOHWND: return "Clipper notification requires a window handle, or no window handle has been previously set as the cooperative level window handle."; + case DDERR_NOMIPMAPHW: return "No mipmap-capable texture mapping hardware is present or available."; + case DDERR_NOMIRRORHW: return "No mirroring hardware is present or available."; + case DDERR_NOMONITORINFORMATION: return "New for DirectX 7.0. Testing cannot proceed because the monitor has no associated EDID data."; + case DDERR_NONONLOCALVIDMEM: return "An attempt was made to allocate nonlocal video memory from a device that does not support nonlocal video memory."; + case DDERR_NOOPTIMIZEHW: return "The device does not support optimized surfaces."; + case DDERR_NOOVERLAYDEST: return "The IDirectDrawSurface7::GetOverlayPosition method is called on an overlay that the IDirectDrawSurface7::UpdateOverlay method has not been called on to establish as a destination."; + case DDERR_NOOVERLAYHW: return "No overlay hardware is present or available."; + case DDERR_NOPALETTEATTACHED: return "No palette object is attached to this surface."; + case DDERR_NOPALETTEHW: return "There is no hardware support for 16- or 256-color palettes."; + case DDERR_NORASTEROPHW: return "No appropriate raster-operation hardware is present or available."; + case DDERR_NOROTATIONHW: return "No rotation hardware is present or available."; + case DDERR_NOSTEREOHARDWARE: return "There is no stereo hardware present or available."; + case DDERR_NOSTRETCHHW: return "There is no hardware support for stretching."; + case DDERR_NOSURFACELEFT: return "There is no hardware present that supports stereo surfaces."; + case DDERR_NOT4BITCOLOR: return "The DirectDrawSurface object is not using a 4-bit color palette, and the requested operation requires a 4-bit color palette."; + case DDERR_NOT4BITCOLORINDEX: return "The DirectDrawSurface object is not using a 4-bit color index palette, and the requested operation requires a 4-bit color index palette."; + case DDERR_NOT8BITCOLOR: return "The DirectDrawSurface object is not using an 8-bit color palette, and the requested operation requires an 8-bit color palette."; + case DDERR_NOTAOVERLAYSURFACE: return "An overlay component is called for a nonoverlay surface."; + case DDERR_NOTEXTUREHW: return "The operation cannot be carried out because no texture-mapping hardware is present or available."; + case DDERR_NOTFLIPPABLE: return "An attempt was made to flip a surface that cannot be flipped."; + case DDERR_NOTFOUND: return "The requested item was not found."; + case DDERR_NOTINITIALIZED: return "An attempt was made to call an interface method of a DirectDraw object created by CoCreateInstance before the object was initialized."; + case DDERR_NOTLOADED: return "The surface is an optimized surface, but it has not yet been allocated any memory."; + case DDERR_NOTLOCKED: return "An attempt was made to unlock a surface that was not locked."; + case DDERR_NOTPAGELOCKED: return "An attempt was made to page-unlock a surface with no outstanding page locks."; + case DDERR_NOTPALETTIZED: return "The surface being used is not a palette-based surface."; + case DDERR_NOVSYNCHW: return "There is no hardware support for vertical blanksynchronized operations."; + case DDERR_NOZBUFFERHW: return "The operation to create a z-buffer in display memory or to perform a blit, using a z-buffer cannot be carried out because there is no hardware support for z-buffers."; + case DDERR_NOZOVERLAYHW: return "The overlay surfaces cannot be z-layered, based on the z-order because the hardware does not support z-ordering of overlays."; + case DDERR_OUTOFCAPS: return "The hardware needed for the requested operation has already been allocated."; + case DDERR_OUTOFMEMORY: return "DirectDraw does not have enough memory to perform the operation."; + case DDERR_OUTOFVIDEOMEMORY: return "DirectDraw does not have enough display memory to perform the operation."; + case DDERR_OVERLAPPINGRECTS: return "The source and destination rectangles are on the same surface and overlap each other."; + case DDERR_OVERLAYCANTCLIP: return "The hardware does not support clipped overlays."; + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: return "An attempt was made to have more than one color key active on an overlay."; + case DDERR_OVERLAYNOTVISIBLE: return "The IDirectDrawSurface7::GetOverlayPosition method was called on a hidden overlay."; + case DDERR_PALETTEBUSY: return "Access to this palette is refused because the palette is locked by another thread."; + case DDERR_PRIMARYSURFACEALREADYEXISTS: return "This process has already created a primary surface."; + case DDERR_REGIONTOOSMALL: return "The region passed to the IDirectDrawClipper::GetClipList method is too small."; + case DDERR_SURFACEALREADYATTACHED: return "An attempt was made to attach a surface to another surface to which it is already attached."; + case DDERR_SURFACEALREADYDEPENDENT: return "An attempt was made to make a surface a dependency of another surface on which it is already dependent."; + case DDERR_SURFACEBUSY: return "Access to the surface is refused because the surface is locked by another thread."; + case DDERR_SURFACEISOBSCURED: return "Access to the surface is refused because the surface is obscured."; + case DDERR_SURFACELOST: return "Access to the surface is refused because the surface memory is gone. Call the IDirectDrawSurface7::Restore method on this surface to restore the memory associated with it."; + case DDERR_SURFACENOTATTACHED: return "The requested surface is not attached."; + case DDERR_TESTFINISHED: return "New for DirectX 7.0. When returned by the IDirectDraw7::StartModeTest method, this value means that no test could be initiated because all the resolutions chosen for testing already have refresh rate information in the registry. When returned by IDirectDraw7::EvaluateMode, the value means that DirectDraw has completed a refresh rate test."; + case DDERR_TOOBIGHEIGHT: return "The height requested by DirectDraw is too large."; + case DDERR_TOOBIGSIZE: return "The size requested by DirectDraw is too large. However, the individual height and width are valid sizes."; + case DDERR_TOOBIGWIDTH: return "The width requested by DirectDraw is too large."; + case DDERR_UNSUPPORTED: return "The operation is not supported."; + case DDERR_UNSUPPORTEDFORMAT: return "The pixel format requested is not supported by DirectDraw."; + case DDERR_UNSUPPORTEDMASK: return "The bitmask in the pixel format requested is not supported by DirectDraw."; + case DDERR_UNSUPPORTEDMODE: return "The display is currently in an unsupported mode."; + case DDERR_VERTICALBLANKINPROGRESS: return "A vertical blank is in progress."; + case DDERR_VIDEONOTACTIVE: return "The video port is not active."; + case DDERR_WASSTILLDRAWING: return "The previous blit operation that is transferring information to or from this surface is incomplete."; + case DDERR_WRONGMODE: return "This surface cannot be restored because it was created in a different mode."; + case DDERR_XALIGN: return "The provided rectangle was not horizontally aligned on a required boundary."; + } + + return nullptr; +} + + +const char *D3DErrorString(HRESULT hResult) +{ + static char buffer[1024]; + buffer[0] = 0; // Reset static buffer! + + const char* errorCodeString = DXGetErrorString(hResult); + if (errorCodeString) + { + strcat(buffer, errorCodeString); + strcat(buffer, ": "); + } + + const char* errorDescription = CxbxGetErrorDescription(hResult); + if (errorDescription) + strcat(buffer, errorDescription); + else + strcat(buffer, "Unknown D3D error."); + + return buffer; +} + +VOID CxbxInitWindow(bool bFullInit) +{ + g_EmuShared->GetVideoSettings(&g_XBVideo); + + if(g_XBVideo.bFullScreen) + CxbxKrnl_hEmuParent = NULL; + + // create timing thread + if (bFullInit) + { + DWORD dwThreadId; + + HANDLE hThread = CreateThread(nullptr, 0, EmuUpdateTickCount, nullptr, 0, &dwThreadId); + // We set the priority of this thread a bit higher, to assure reliable timing : + SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); + + CxbxKrnlRegisterThread(hThread); + } + +/* TODO : Port this Dxbx code : + // create vblank handling thread + { + dwThreadId = 0; + {hThread :=} CreateThread(nullptr, 0, EmuThreadHandleVBlank, nullptr, 0, &dwThreadId); + } +*/ + // create window message processing thread + { + DWORD dwThreadId; + + g_bRenderWindowActive = false; + + HANDLE hRenderWindowThread = CreateThread(nullptr, 0, EmuRenderWindow, nullptr, 0, &dwThreadId); + + if (hRenderWindowThread == NULL) { + char szBuffer[1024] = { 0 }; + sprintf(szBuffer, "Creating EmuRenderWindowThread Failed: %08X", GetLastError()); + PopupFatal(nullptr, szBuffer); + EmuShared::Cleanup(); + ExitProcess(0); + } + + while(!g_bRenderWindowActive) + Sleep(0); + + Sleep(0); + } + + SetFocus(g_hEmuWindow); +} + +void DrawUEM(HWND hWnd) +{ + // Draw the universal error message (UEM) + // See https://xboxdevwiki.net/Fatal_Error + // Only call this from WM_PAINT message! + + PAINTSTRUCT ps; + + BeginPaint(hWnd, &ps); + + HDC hDC = GetDC(hWnd); + HDC hMemDC = CreateCompatibleDC(hDC); + HBITMAP hUEMBmp = CreateCompatibleBitmap(hDC, 640, 480); + HBITMAP hOriUEMBmp = (HBITMAP)SelectObject(hMemDC, hUEMBmp); + + + int nHeight = -MulDiv(8, GetDeviceCaps(hMemDC, LOGPIXELSY), 72); + + HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_ROMAN, "Verdana"); + + HGDIOBJ tmpObj = SelectObject(hMemDC, hFont); + + SetBkColor(hMemDC, RGB(0, 0, 0)); + + SetTextColor(hMemDC, RGB(0, 204, 0)); + + std::wstring utf16str = utf8_to_utf16(uem_str.c_str()); + + // Unfortunately, DrawTextW doesn't support vertical alignemnt, so we have to do the calculation + // ourselves. See here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/abd89aae-16a0-41c6-8db6-b119ea90b42a/win32-drawtext-how-center-in-vertical-with-new-lines-and-tabs?forum=vclanguage + + RECT rect = { 0, 0, 640, 480 }; + RECT textrect = { 0, 0, 640, 480 }; + DrawTextW(hMemDC, utf16str.c_str(), utf16str.length(), &textrect, DT_CALCRECT); + rect.top = (rect.bottom - textrect.bottom) / 2; + DrawTextW(hMemDC, utf16str.c_str(), utf16str.length(), &rect, DT_CENTER); + + + // Draw the Xbox error code + + SetTextColor(hMemDC, RGB(255, 255, 255)); + std::string err_str(std::to_string(g_CxbxFatalErrorCode)); + rect.left = 20; + DrawText(hMemDC, err_str.c_str(), err_str.length(), &rect, DT_LEFT); + + GetClientRect(hWnd, &rect); + SetStretchBltMode(hDC, COLORONCOLOR); + StretchBlt(hDC, rect.left, rect.top, rect.right, rect.bottom, hMemDC, 0, 0, 640, 480, SRCCOPY); + + SelectObject(hMemDC, hOriUEMBmp); + SelectObject(hDC, tmpObj); + + DeleteObject(hUEMBmp); + DeleteObject(hFont); + DeleteObject(hMemDC); + + if (hDC != NULL) + ReleaseDC(hWnd, hDC); + + EndPaint(hWnd, &ps); +} + +inline DWORD GetXboxCommonResourceType(const XTL::DWORD XboxResource_Common) +{ + DWORD dwCommonType = XboxResource_Common & X_D3DCOMMON_TYPE_MASK; + return dwCommonType; +} + +inline DWORD GetXboxCommonResourceType(const XTL::X_D3DResource* pXboxResource) +{ + // Don't pass in unassigned Xbox resources + assert(pXboxResource != xbnullptr); + + return GetXboxCommonResourceType(pXboxResource->Common); +} + +XTL::X_D3DFORMAT GetXboxPixelContainerFormat(const XTL::DWORD XboxPixelContainer_Format) +{ + XTL::X_D3DFORMAT d3d_format = (XTL::X_D3DFORMAT)((XboxPixelContainer_Format & X_D3DFORMAT_FORMAT_MASK) >> X_D3DFORMAT_FORMAT_SHIFT); + return d3d_format; +} + +XTL::X_D3DFORMAT GetXboxPixelContainerFormat(const XTL::X_D3DPixelContainer *pXboxPixelContainer) +{ + // Don't pass in unassigned Xbox pixel container + assert(pXboxPixelContainer != xbnullptr); + + return GetXboxPixelContainerFormat(pXboxPixelContainer->Format); +} + +inline int GetXboxPixelContainerDimensionCount(const XTL::X_D3DPixelContainer *pXboxPixelContainer) +{ + // Don't pass in unassigned Xbox pixel container + assert(pXboxPixelContainer != xbnullptr); + + return (XTL::X_D3DFORMAT)((pXboxPixelContainer->Format & X_D3DFORMAT_DIMENSION_MASK) >> X_D3DFORMAT_DIMENSION_SHIFT); +} + +XTL::X_D3DRESOURCETYPE GetXboxD3DResourceType(const XTL::X_D3DResource *pXboxResource) +{ + DWORD Type = GetXboxCommonResourceType(pXboxResource); + switch (Type) + { + case X_D3DCOMMON_TYPE_VERTEXBUFFER: + return XTL::X_D3DRTYPE_VERTEXBUFFER; + case X_D3DCOMMON_TYPE_INDEXBUFFER: + return XTL::X_D3DRTYPE_INDEXBUFFER; + case X_D3DCOMMON_TYPE_PUSHBUFFER: + return XTL::X_D3DRTYPE_PUSHBUFFER; + case X_D3DCOMMON_TYPE_PALETTE: + return XTL::X_D3DRTYPE_PALETTE; + case X_D3DCOMMON_TYPE_TEXTURE: + { + DWORD Format = ((XTL::X_D3DPixelContainer *)pXboxResource)->Format; + if (Format & X_D3DFORMAT_CUBEMAP) + return XTL::X_D3DRTYPE_CUBETEXTURE; + + if (GetXboxPixelContainerDimensionCount((XTL::X_D3DPixelContainer *)pXboxResource) > 2) + return XTL::X_D3DRTYPE_VOLUMETEXTURE; + + return XTL::X_D3DRTYPE_TEXTURE; + } + case X_D3DCOMMON_TYPE_SURFACE: + { + if (GetXboxPixelContainerDimensionCount((XTL::X_D3DPixelContainer *)pXboxResource) > 2) + return XTL::X_D3DRTYPE_VOLUME; + + return XTL::X_D3DRTYPE_SURFACE; + } + case X_D3DCOMMON_TYPE_FIXUP: + return XTL::X_D3DRTYPE_FIXUP; + } + + return XTL::X_D3DRTYPE_NONE; +} + +// This can be used to determine if resource Data adddresses +// need the PHYSICAL_MAP_BASE bit set or cleared +inline bool IsResourceTypeGPUReadable(const DWORD ResourceType) +{ + switch (ResourceType) { + case X_D3DCOMMON_TYPE_VERTEXBUFFER: + return true; + case X_D3DCOMMON_TYPE_INDEXBUFFER: + /// assert(false); // Index buffers are not allowed to be registered + break; + case X_D3DCOMMON_TYPE_PUSHBUFFER: + return false; + case X_D3DCOMMON_TYPE_PALETTE: + return true; + case X_D3DCOMMON_TYPE_TEXTURE: + return true; + case X_D3DCOMMON_TYPE_SURFACE: + return true; + case X_D3DCOMMON_TYPE_FIXUP: + // assert(false); // Fixup's are not allowed to be registered + break; + default: + CxbxKrnlCleanup("Unhandled resource type"); + } + + return false; +} + +inline bool IsPaletizedTexture(const XTL::DWORD XboxPixelContainer_Format) +{ + return GetXboxPixelContainerFormat(XboxPixelContainer_Format) == XTL::X_D3DFMT_P8; +} + +#if 0 // unused +inline bool IsYuvSurfaceOrTexture(const XTL::X_D3DResource* pXboxResource) +{ + if (GetXboxPixelContainerFormat((XTL::X_D3DPixelContainer *)pXboxResource) == XTL::X_D3DFMT_YUY2) + return true; + + return false; +} +#endif + +#if 0 // unused +inline bool IsXboxResourceLocked(const XTL::X_D3DResource *pXboxResource) +{ + bool result = !!(pXboxResource->Common & X_D3DCOMMON_ISLOCKED); + return result; +} +#endif + +#if 0 // unused +inline bool IsXboxResourceD3DCreated(const XTL::X_D3DResource *pXboxResource) +{ + bool result = !!(pXboxResource->Common & X_D3DCOMMON_D3DCREATED); + return result; +} +#endif + +void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource) +{ + // Don't pass in unassigned Xbox resources + if (pXboxResource == xbnullptr) + return nullptr; + + xbaddr pData = pXboxResource->Data; + if (pData == xbnull) + return nullptr; + + DWORD dwCommonType = GetXboxCommonResourceType(pXboxResource); + if (IsResourceTypeGPUReadable(dwCommonType)) + pData |= PHYSICAL_MAP_BASE; + + return (uint8_t*)pData; +} + +typedef struct { + IDirect3DResource* pHostResource = nullptr; + XTL::X_D3DResource* pXboxResource = xbnullptr; + DWORD dwXboxResourceType = 0; + void* pXboxData = xbnullptr; + size_t szXboxDataSize = 0; + uint64_t hash = 0; + bool forceRehash = false; + std::chrono::time_point nextHashTime; + std::chrono::milliseconds hashLifeTime = 1ms; + std::chrono::time_point lastUpdate; +} resource_info_t; + +typedef std::unordered_map resource_cache_t; +resource_cache_t g_Cxbx_Cached_Direct3DResources; +resource_cache_t g_Cxbx_Cached_PaletizedTextures; + +bool IsResourceAPixelContainer(XTL::DWORD XboxResource_Common) +{ + DWORD Type = GetXboxCommonResourceType(XboxResource_Common); + switch (Type) + { + case X_D3DCOMMON_TYPE_TEXTURE: + case X_D3DCOMMON_TYPE_SURFACE: + return true; + } + + return false; +} + +bool IsResourceAPixelContainer(XTL::X_D3DResource* pXboxResource) +{ + // Don't pass in unassigned Xbox resources + assert(pXboxResource != xbnullptr); + + return IsResourceAPixelContainer(pXboxResource->Common); +} + +resource_cache_t& GetResourceCache(resource_key_t& key) +{ + return IsResourceAPixelContainer(key.Common) && IsPaletizedTexture(key.Format) + ? g_Cxbx_Cached_PaletizedTextures : g_Cxbx_Cached_Direct3DResources; +} + +resource_key_t GetHostResourceKey(XTL::X_D3DResource* pXboxResource, int iTextureStage = -1) +{ + resource_key_t key = {}; + if (pXboxResource != xbnullptr) { + // Initially, don't base the key on the address of the resource, but on it's uniquely identifying values + key.Data = pXboxResource->Data; + key.Common = pXboxResource->Common & CXBX_D3DCOMMON_IDENTIFYING_MASK; + if (IsResourceAPixelContainer(pXboxResource)) { + // Pixel containers have more values they must be identified by: + auto pPixelContainer = (XTL::X_D3DPixelContainer*)pXboxResource; + key.Format = pPixelContainer->Format; + key.Size = pPixelContainer->Size; + // For paletized textures, include the current palette hash as well + if (IsPaletizedTexture(pPixelContainer->Format)) { + if (iTextureStage < 0) { + // ForceResourceRehash (called by Lock[23]DSurface) could hit this (not knowing the texture-stage) + LOG_TEST_CASE("Unknown texture stage!"); + } else { + assert(iTextureStage < XTL::X_D3DTS_STAGECOUNT); + // Protect for when this gets hit before an actual palette is set + if (g_Xbox_Palette_Size[iTextureStage] > 0) { + // This caters for palette changes (only the active one will be used, + // any intermediate changes have no effect). Obsolete palette texture + // conversions will be pruned from g_Cxbx_Cached_PaletizedTextures + key.PaletteHash = ComputeHash(g_pXbox_Palette_Data[iTextureStage], g_Xbox_Palette_Size[iTextureStage]); + } + } + } + } else { + // For other resource types, do include their Xbox resource address (TODO : come up with something better) + key.ResourceAddr = (xbaddr)pXboxResource; + } + } + + return key; +} + +void FreeHostResource(resource_key_t key) +{ + // Release the host resource and remove it from the list + auto& ResourceCache = GetResourceCache(key); + auto hostResourceIterator = ResourceCache.find(key); + if (hostResourceIterator != ResourceCache.end()) { + if (hostResourceIterator->second.pHostResource) { + (hostResourceIterator->second.pHostResource)->Release(); + } + + ResourceCache.erase(hostResourceIterator); + } +} + +void ClearResourceCache(resource_cache_t& ResourceCache) +{ + for (auto& hostResourceIterator : ResourceCache) { + if (hostResourceIterator.second.pHostResource) { + (hostResourceIterator.second.pHostResource)->Release(); + } + } + + ResourceCache.clear(); +} + +void PrunePaletizedTexturesCache() +{ + // TODO : Implement a better cache eviction algorithm (like least-recently used, or just at-random) + // Poor mans cache eviction policy: just clear it once it overflows + if (g_Cxbx_Cached_PaletizedTextures.size() >= 1500) { + ClearResourceCache(g_Cxbx_Cached_PaletizedTextures); + } +} + +void ForceResourceRehash(XTL::X_D3DResource* pXboxResource) +{ + auto key = GetHostResourceKey(pXboxResource); // Note : iTextureStage is unknown here! + auto& ResourceCache = GetResourceCache(key); + auto it = ResourceCache.find(key); + if (it != ResourceCache.end() && it->second.pHostResource) { + it->second.forceRehash = true; + } +} + +IDirect3DResource *GetHostResource(XTL::X_D3DResource *pXboxResource, DWORD D3DUsage = 0, int iTextureStage = -1) +{ + if (pXboxResource == xbnullptr || pXboxResource->Data == xbnull) + return nullptr; + + EmuVerifyResourceIsRegistered(pXboxResource, D3DUsage, iTextureStage, /*dwSize=*/0); + + auto key = GetHostResourceKey(pXboxResource, iTextureStage); + auto& ResourceCache = GetResourceCache(key); + auto it = ResourceCache.find(key); + if (it == ResourceCache.end() || !it->second.pHostResource) { + EmuLog(LOG_LEVEL::WARNING, "GetHostResource: Resource not registered or does not have a host counterpart!"); + return nullptr; + } + + return it->second.pHostResource; +} + +// Forward declaration of CxbxGetPixelContainerMeasures to prevent +// polluting the diff too much by reshuffling functions around +VOID CxbxGetPixelContainerMeasures +( + XTL::X_D3DPixelContainer *pPixelContainer, + DWORD dwMipMapLevel, + UINT *pWidth, + UINT *pHeight, + UINT *pDepth, + UINT *pRowPitch, + UINT *pSlicePitch +); + +size_t GetXboxResourceSize(XTL::X_D3DResource* pXboxResource) +{ + // TODO: Smart size calculation based around format of resource + if (IsResourceAPixelContainer(pXboxResource)) { + unsigned int Width, Height, Depth, RowPitch, SlicePitch; + // TODO : Accumulate all mipmap levels!!! + CxbxGetPixelContainerMeasures( + (XTL::X_D3DPixelContainer*)pXboxResource, + 0, // dwMipMapLevel + &Width, + &Height, + &Depth, + &RowPitch, + &SlicePitch + ); + + return SlicePitch * Depth; + } else { + // Fallback to querying the allocation size, if no other calculation was present + return xboxkrnl::MmQueryAllocationSize(GetDataFromXboxResource(pXboxResource)); + } + +} + +bool HostResourceRequiresUpdate(resource_key_t key, DWORD dwSize) +{ + auto& ResourceCache = GetResourceCache(key); + auto it = ResourceCache.find(key); + if (it == ResourceCache.end() || !it->second.pXboxResource) { + return false; + } + + // Currently, we only dynamically update Textures and Surfaces, so if our resource + // isn't of these types, do nothing + if (!IsResourceAPixelContainer(it->second.pXboxResource)) { + return false; + } + + // If the resource size got bigger, we need to re-create it + // if it got smaller, just hashing will suffice + if (dwSize > it->second.szXboxDataSize) { + return true; + } + + // If the resource type changed, we need to re-create it + if (it->second.dwXboxResourceType != GetXboxCommonResourceType(it->second.pXboxResource)) { + return true; + } + + bool modified = false; + + auto now = std::chrono::steady_clock::now(); + if (now > it->second.nextHashTime || it->second.forceRehash) { + uint64_t oldHash = it->second.hash; + it->second.hash = ComputeHash(it->second.pXboxData, it->second.szXboxDataSize); + + if (it->second.hash != oldHash) { + // The data changed, so reset the hash lifetime + it->second.hashLifeTime = 1ms; + it->second.lastUpdate = now; + modified = true; + } else if (it->second.lastUpdate + 1000ms < now) { + // The data did not change, so increase the hash lifetime + // TODO: choose a sensible upper limit + if (it->second.hashLifeTime < 1000ms) { + it->second.hashLifeTime += 10ms; + } + } + + it->second.forceRehash = false; + } + + // Update the next hash time based on the hash lifetime + it->second.nextHashTime = now + it->second.hashLifeTime; + + return modified; +} + +void SetHostResource(XTL::X_D3DResource* pXboxResource, IDirect3DResource* pHostResource, int iTextureStage = -1, DWORD dwSize = 0) +{ + auto key = GetHostResourceKey(pXboxResource, iTextureStage); + auto& ResourceCache = GetResourceCache(key); + auto& resourceInfo = ResourceCache[key]; // Implicitely inserts a new entry if not already existing + + if (resourceInfo.pHostResource) { + EmuLog(LOG_LEVEL::WARNING, "SetHostResource: Overwriting an existing host resource"); + } + + resourceInfo.pHostResource = pHostResource; + resourceInfo.pXboxResource = pXboxResource; + resourceInfo.dwXboxResourceType = GetXboxCommonResourceType(pXboxResource); + resourceInfo.pXboxData = GetDataFromXboxResource(pXboxResource); + resourceInfo.szXboxDataSize = dwSize > 0 ? dwSize : GetXboxResourceSize(pXboxResource); + resourceInfo.hash = ComputeHash(resourceInfo.pXboxData, resourceInfo.szXboxDataSize); + resourceInfo.hashLifeTime = 1ms; + resourceInfo.lastUpdate = std::chrono::steady_clock::now(); + resourceInfo.nextHashTime = resourceInfo.lastUpdate + resourceInfo.hashLifeTime; + resourceInfo.forceRehash = false; +} + +IDirect3DSurface *GetHostSurface(XTL::X_D3DResource *pXboxResource, DWORD D3DUsage = 0) +{ + if (pXboxResource == xbnullptr) + return nullptr; + + if (GetXboxCommonResourceType(pXboxResource) != X_D3DCOMMON_TYPE_SURFACE) // Allows breakpoint below + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_SURFACE); + + return (IDirect3DSurface*) GetHostResource(pXboxResource, D3DUsage); +} + +IDirect3DBaseTexture *GetHostBaseTexture(XTL::X_D3DResource *pXboxResource, DWORD D3DUsage = 0, int iTextureStage = 0) +{ + if (pXboxResource == xbnullptr) + return nullptr; + + if (GetXboxCommonResourceType(pXboxResource) != X_D3DCOMMON_TYPE_TEXTURE) { // Allows breakpoint below + // test-case : Burnout and Outrun 2006 hit this case (retrieving a surface instead of a texture) + // TODO : Surfaces can be set in the texture stages, instead of textures - see preparations in CxbxConvertXboxSurfaceToHostTexture + // We'll need to wrap the surface somehow before using it as a texture + LOG_TEST_CASE("GetHostBaseTexture called on a non-texture object"); + return nullptr; + // Note : We'd like to remove the above and do the following instead, + // but we can't yet since that seems to cause a "CreateCubeTexture Failed!" + // regression. The root cause for that seems to stem from the X_D3DRTYPE_SURFACE + // handling in CreateHostResource. + //assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); + } + + return (IDirect3DBaseTexture*)GetHostResource(pXboxResource, D3DUsage, iTextureStage); +} + +#if 0 // unused +IDirect3DTexture *GetHostTexture(XTL::X_D3DResource *pXboxResource, int iTextureStage = 0) +{ + if (pXboxResource == xbnullptr) + return nullptr; + + return (IDirect3DTexture *)GetHostBaseTexture(pXboxResource, 0, iTextureStage); + + // TODO : Check for 1 face (and 2 dimensions)? +} +#endif + +IDirect3DVolumeTexture *GetHostVolumeTexture(XTL::X_D3DResource *pXboxResource, int iTextureStage = 0) +{ + return (IDirect3DVolumeTexture *)GetHostBaseTexture(pXboxResource, 0, iTextureStage); + + // TODO : Check for 1 face (and 2 dimensions)? +} + +#if 0 // unused +IDirect3DIndexBuffer *GetHostIndexBuffer(XTL::X_D3DResource *pXboxResource) +{ + if (pXboxResource == xbnullptr) + return nullptr; + + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_INDEXBUFFER); + + return (IDirect3DIndexBuffer*)GetHostResource(pXboxResource); +} +#endif + +void SetHostSurface(XTL::X_D3DResource *pXboxResource, IDirect3DSurface *pHostSurface, int iTextureStage) +{ + assert(pXboxResource != xbnullptr); + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_SURFACE); + + SetHostResource(pXboxResource, (IDirect3DResource*)pHostSurface, iTextureStage); +} + +void SetHostTexture(XTL::X_D3DResource *pXboxResource, IDirect3DTexture *pHostTexture, int iTextureStage) +{ + assert(pXboxResource != xbnullptr); + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); + + SetHostResource(pXboxResource, (IDirect3DResource*)pHostTexture, iTextureStage); +} + +void SetHostCubeTexture(XTL::X_D3DResource *pXboxResource, IDirect3DCubeTexture *pHostCubeTexture, int iTextureStage) +{ + assert(pXboxResource != xbnullptr); + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); + + SetHostResource(pXboxResource, (IDirect3DResource*)pHostCubeTexture, iTextureStage); +} + +void SetHostVolumeTexture(XTL::X_D3DResource *pXboxResource, IDirect3DVolumeTexture *pHostVolumeTexture, int iTextureStage) +{ + assert(pXboxResource != xbnullptr); + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); + + SetHostResource(pXboxResource, (IDirect3DResource*)pHostVolumeTexture, iTextureStage); +} + +void SetHostVolume(XTL::X_D3DResource *pXboxResource, IDirect3DVolume *pHostVolume, int iTextureStage) +{ + assert(pXboxResource != xbnullptr); + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_TEXTURE); + + SetHostResource(pXboxResource, (IDirect3DResource*)pHostVolume, iTextureStage); +} + +void SetHostIndexBuffer(XTL::X_D3DResource *pXboxResource, IDirect3DIndexBuffer *pHostIndexBuffer) +{ + assert(pXboxResource != xbnullptr); + assert(GetXboxCommonResourceType(pXboxResource) == X_D3DCOMMON_TYPE_INDEXBUFFER); + + SetHostResource(pXboxResource, (IDirect3DResource*)pHostIndexBuffer); +} + +int XboxD3DPaletteSizeToBytes(const XTL::X_D3DPALETTESIZE Size) +{ +/* + static int lk[4] = + { + 256 * sizeof(D3DCOLOR), // D3DPALETTE_256 + 128 * sizeof(D3DCOLOR), // D3DPALETTE_128 + 64 * sizeof(D3DCOLOR), // D3DPALETTE_64 + 32 * sizeof(D3DCOLOR) // D3DPALETTE_32 + }; + + return lk[Size]; +*/ + return (256 * sizeof(D3DCOLOR)) >> (unsigned)Size; +} + +inline XTL::X_D3DPALETTESIZE GetXboxPaletteSize(const XTL::X_D3DPalette *pPalette) +{ + XTL::X_D3DPALETTESIZE PaletteSize = (XTL::X_D3DPALETTESIZE) + ((pPalette->Common & X_D3DPALETTE_COMMON_PALETTESIZE_MASK) >> X_D3DPALETTE_COMMON_PALETTESIZE_SHIFT); + + return PaletteSize; +} + +int GetD3DResourceRefCount(IDirect3DResource *EmuResource) +{ + if (EmuResource != nullptr) + { + // Get actual reference count by increasing it using AddRef, + // and relying on the return value of Release (which is + // probably more reliable than AddRef) + EmuResource->AddRef(); + return EmuResource->Release(); + } + + return 0; +} + +/* +XTL::X_D3DSurface *EmuNewD3DSurface() +{ + XTL::X_D3DSurface *result = (XTL::X_D3DSurface *)g_VMManager.AllocateZeroed(sizeof(XTL::X_D3DSurface)); + result->Common = X_D3DCOMMON_D3DCREATED | X_D3DCOMMON_TYPE_SURFACE | 1; // Set refcount to 1 + return result; +} +*/ + +VOID CxbxSetPixelContainerHeader +( + XTL::X_D3DPixelContainer* pPixelContainer, + DWORD Common, + UINT Width, + UINT Height, + UINT Levels, + XTL::X_D3DFORMAT Format, + UINT Dimensions, + UINT Pitch +) +{ + // Set X_D3DResource field(s) : + pPixelContainer->Common = Common; + // DON'T SET pPixelContainer->Data + // DON'T SET pPixelContainer->Lock + + // Are Width and Height both a power of two? + DWORD l2w; _BitScanReverse(&l2w, Width); // MSVC intrinsic; GCC has __builtin_clz + DWORD l2h; _BitScanReverse(&l2h, Height); + DWORD l2d = 0; // TODO : Set this via input argument + if (((1 << l2w) == Width) && ((1 << l2h) == Height)) { + Width = Height = Pitch = 1; // When setting Format, clear Size field + } + else { + l2w = l2h = l2d = 0; // When setting Size, clear D3DFORMAT_USIZE, VSIZE and PSIZE + } + + // Set X_D3DPixelContainer field(s) : + pPixelContainer->Format = 0 + | ((Dimensions << X_D3DFORMAT_DIMENSION_SHIFT) & X_D3DFORMAT_DIMENSION_MASK) + | (((DWORD)Format << X_D3DFORMAT_FORMAT_SHIFT) & X_D3DFORMAT_FORMAT_MASK) + | ((Levels << X_D3DFORMAT_MIPMAP_SHIFT) & X_D3DFORMAT_MIPMAP_MASK) + | ((l2w << X_D3DFORMAT_USIZE_SHIFT) & X_D3DFORMAT_USIZE_MASK) + | ((l2h << X_D3DFORMAT_VSIZE_SHIFT) & X_D3DFORMAT_VSIZE_MASK) + | ((l2d << X_D3DFORMAT_PSIZE_SHIFT) & X_D3DFORMAT_PSIZE_MASK) + ; + pPixelContainer->Size = 0 + | (((Width - 1) /*X_D3DSIZE_WIDTH_SHIFT*/) & X_D3DSIZE_WIDTH_MASK) + | (((Height - 1) << X_D3DSIZE_HEIGHT_SHIFT) & X_D3DSIZE_HEIGHT_MASK) + | (((Pitch - 1) << X_D3DSIZE_PITCH_SHIFT) & X_D3DSIZE_PITCH_MASK) + ; +} + +unsigned int CxbxGetPixelContainerDepth +( + XTL::X_D3DPixelContainer *pPixelContainer +) +{ + if (pPixelContainer->Size == 0) { + DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT; + return 1 << l2d; + } + + return 1; +} + +unsigned int CxbxGetPixelContainerMipMapLevels +( + XTL::X_D3DPixelContainer *pPixelContainer +) +{ + if (pPixelContainer->Size == 0) { + return (pPixelContainer->Format & X_D3DFORMAT_MIPMAP_MASK) >> X_D3DFORMAT_MIPMAP_SHIFT; + } + + return 1; +} + +uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer *pPixelContainer) +{ + DWORD Size = pPixelContainer->Size; + uint32_t Result; + + if (Size != 0) { + Result = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1; + } + else { + DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT; + + Result = 1 << l2w; + } + + return Result; +} + +uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer *pPixelContainer) +{ + DWORD Size = pPixelContainer->Size; + uint32_t Result; + + if (Size != 0) { + Result = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1; + } + else { + DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT; + + Result = 1 << l2h; + } + + return Result; +} + +VOID CxbxGetPixelContainerMeasures +( + XTL::X_D3DPixelContainer *pPixelContainer, + // TODO : Add X_D3DCUBEMAP_FACES argument + DWORD dwMipMapLevel, // unused - TODO : Use + UINT *pWidth, + UINT *pHeight, + UINT *pDepth, + UINT *pRowPitch, + UINT *pSlicePitch +) +{ + DWORD Size = pPixelContainer->Size; + XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer); + + if (Size != 0) + { + *pDepth = 1; + *pWidth = ((Size & X_D3DSIZE_WIDTH_MASK) /* >> X_D3DSIZE_WIDTH_SHIFT*/) + 1; + *pHeight = ((Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1; + *pRowPitch = (((Size & X_D3DSIZE_PITCH_MASK) >> X_D3DSIZE_PITCH_SHIFT) + 1) * X_D3DTEXTURE_PITCH_ALIGNMENT; + } + else + { + DWORD l2w = (pPixelContainer->Format & X_D3DFORMAT_USIZE_MASK) >> X_D3DFORMAT_USIZE_SHIFT; + DWORD l2h = (pPixelContainer->Format & X_D3DFORMAT_VSIZE_MASK) >> X_D3DFORMAT_VSIZE_SHIFT; + DWORD l2d = (pPixelContainer->Format & X_D3DFORMAT_PSIZE_MASK) >> X_D3DFORMAT_PSIZE_SHIFT; + DWORD dwBPP = EmuXBFormatBitsPerPixel(X_Format); + + *pDepth = 1 << l2d; + *pHeight = 1 << l2h; + *pWidth = 1 << l2w; + *pRowPitch = (*pWidth) * dwBPP / 8; + } + + *pSlicePitch = (*pRowPitch) * (*pHeight); + + if (EmuXBFormatIsCompressed(X_Format)) { + *pRowPitch *= 4; + } +} + +void GetSurfaceFaceAndLevelWithinTexture(XTL::X_D3DSurface* pSurface, XTL::X_D3DBaseTexture* pTexture, UINT& Level, D3DCUBEMAP_FACES& Face) +{ + auto pSurfaceData = (uintptr_t)GetDataFromXboxResource(pSurface); + auto pTextureData = (uintptr_t)GetDataFromXboxResource(pTexture); + + // Fast path: If the data pointers match, this must be the first surface within the texture + if (pSurfaceData == pTextureData) { + Level = 0; + Face = D3DCUBEMAP_FACE_POSITIVE_X; + return; + } + + int numLevels = CxbxGetPixelContainerMipMapLevels(pTexture); + int numFaces = pTexture->Format & X_D3DFORMAT_CUBEMAP ? 6 : 1; + + CxbxGetPixelContainerMipMapLevels(pTexture); + + // First, we need to fetch the dimensions of both the surface and the texture, for use within our calculations + UINT textureWidth, textureHeight, textureDepth, textureRowPitch, textureSlicePitch; + CxbxGetPixelContainerMeasures(pTexture, 0, &textureWidth, &textureHeight, &textureDepth, &textureRowPitch, &textureSlicePitch); + + UINT surfaceWidth, surfaceHeight, surfaceDepth, surfaceRowPitch, surfaceSlicePitch; + CxbxGetPixelContainerMeasures(pSurface, 0, &surfaceWidth, &surfaceHeight, &surfaceDepth, &surfaceRowPitch, &surfaceSlicePitch); + + // Iterate through all faces and levels, until we find a matching pointer + bool isCompressed = EmuXBFormatIsCompressed(GetXboxPixelContainerFormat(pTexture)); + int minSize = (isCompressed) ? 4 : 1; + int cubeFaceOffset = 0; int cubeFaceSize = 0; + auto pData = pTextureData; + + for (int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= numFaces; face++) { + int mipWidth = textureWidth; + int mipHeight = textureHeight; + int mipDepth = textureDepth; + int mipRowPitch = textureRowPitch; + int mipDataOffset = 0; + + for (int level = 0; level < numLevels; level++) { + if (pData == pSurfaceData) { + Level = level; + Face = (D3DCUBEMAP_FACES)face; + return; + } + + // Calculate size of this mipmap level + UINT dwMipSize = mipRowPitch * mipHeight; + if (isCompressed) { + dwMipSize /= 4; + } + + // If this is the first face, set the cube face size + if (face == D3DCUBEMAP_FACE_POSITIVE_X) { + cubeFaceSize = ROUND_UP(textureDepth * dwMipSize, X_D3DTEXTURE_CUBEFACE_ALIGNMENT); + } + + // Move to the next mip-map and calculate dimensions for the next iteration + mipDataOffset += dwMipSize; + + if (mipWidth > minSize) { + mipWidth /= 2; + mipRowPitch /= 2; + } + + if (mipHeight > minSize) { + mipHeight /= 2; + } + + if (mipDepth > 1) { + mipDepth /= 2; + } + } + + // Move to the next face + pData += cubeFaceSize; + } + + LOG_TEST_CASE("Could not find Surface within Texture, falling back to Level = 0, Face = D3DCUBEMAP_FACE_POSITIVE_X"); + Level = 0; + Face = D3DCUBEMAP_FACE_POSITIVE_X; +} + +// Wrapper function to allow calling without passing a face +void GetSurfaceFaceAndLevelWithinTexture(XTL::X_D3DSurface* pSurface, XTL::X_D3DBaseTexture* pBaseTexture, UINT& Level) +{ + D3DCUBEMAP_FACES face; + GetSurfaceFaceAndLevelWithinTexture(pSurface, pBaseTexture, Level, face); +} + +bool ConvertD3DTextureToARGBBuffer( + XTL::X_D3DFORMAT X_Format, + uint8_t *pSrc, + int SrcWidth, int SrcHeight, int SrcRowPitch, int SrcSlicePitch, + uint8_t *pDst, int DstRowPitch, int DstSlicePitch, + unsigned int uiDepth = 1, + int iTextureStage = 0 +) +{ + const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format); + if (ConvertRowToARGB == nullptr) + return false; // Unhandled conversion + + uint8_t *unswizleBuffer = nullptr; + if (EmuXBFormatIsSwizzled(X_Format)) { + unswizleBuffer = (uint8_t*)malloc(SrcSlicePitch * uiDepth); // TODO : Reuse buffer when performance is important + // First we need to unswizzle the texture data + EmuUnswizzleBox( + pSrc, SrcWidth, SrcHeight, uiDepth, + EmuXBFormatBytesPerPixel(X_Format), + // Note : use src pitch on dest, because this is an intermediate step : + unswizleBuffer, SrcRowPitch, SrcSlicePitch + ); + // Convert colors from the unswizzled buffer + pSrc = unswizleBuffer; + } + + int AdditionalArgument; + if (X_Format == XTL::X_D3DFMT_P8) + AdditionalArgument = (int)g_pXbox_Palette_Data[iTextureStage]; + else + AdditionalArgument = DstRowPitch; + + if (EmuXBFormatIsCompressed(X_Format)) { + // All compressed formats (DXT1, DXT3 and DXT5) encode blocks of 4 pixels on 4 lines + SrcHeight = (SrcHeight + 3) / 4; + DstRowPitch *= 4; + } + + uint8_t *pSrcSlice = pSrc; + uint8_t *pDstSlice = pDst; + for (unsigned int z = 0; z < uiDepth; z++) { + uint8_t *pSrcRow = pSrcSlice; + uint8_t *pDstRow = pDstSlice; + for (int y = 0; y < SrcHeight; y++) { + *(int*)pDstRow = AdditionalArgument; // Dirty hack, to avoid an extra parameter to all conversion callbacks + ConvertRowToARGB(pSrcRow, pDstRow, SrcWidth); + pSrcRow += SrcRowPitch; + pDstRow += DstRowPitch; + } + + pSrcSlice += SrcSlicePitch; + pDstSlice += DstSlicePitch; + } + + if (unswizleBuffer) + free(unswizleBuffer); + + return true; +} + +// Called by WndMain::LoadGameLogo() to load game logo bitmap +uint8_t *ConvertD3DTextureToARGB( + XTL::X_D3DPixelContainer *pXboxPixelContainer, + uint8_t *pSrc, + int *pWidth, int *pHeight, + int TextureStage // default = 0 +) +{ + // Avoid allocating pDest when ConvertD3DTextureToARGBBuffer will fail anyway + XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pXboxPixelContainer); + const FormatToARGBRow ConvertRowToARGB = EmuXBFormatComponentConverter(X_Format); + if (ConvertRowToARGB == nullptr) + return nullptr; // Unhandled conversion + + unsigned int SrcDepth, SrcRowPitch, SrcSlicePitch; + CxbxGetPixelContainerMeasures( + pXboxPixelContainer, + 0, // dwMipMapLevel + (UINT*)pWidth, + (UINT*)pHeight, + &SrcDepth, + &SrcRowPitch, + &SrcSlicePitch + ); + + // Now we know ConvertD3DTextureToARGBBuffer will do it's thing, allocate the resulting buffer + int DstDepth = 1; // for now TODO : Use SrcDepth when supporting volume textures + int DstRowPitch = (*pWidth) * sizeof(DWORD); // = sizeof ARGB pixel. TODO : Is this correct? + int DstSlicePitch = DstRowPitch * (*pHeight); // TODO : Is this correct? + int DstSize = DstSlicePitch * DstDepth; + uint8_t *pDst = (uint8_t *)malloc(DstSize); + + // And convert the source towards that buffer + /*ignore result*/ConvertD3DTextureToARGBBuffer( + X_Format, + pSrc, *pWidth, *pHeight, SrcRowPitch, SrcSlicePitch, + pDst, DstRowPitch, DstSlicePitch, + DstDepth, + TextureStage); + + // NOTE : Caller must take ownership! + return pDst; +} + +// Direct3D initialization (called before emulation begins) +VOID EmuD3DInit() +{ + // create the create device proxy thread + { + DWORD dwThreadId; + + CreateThread(nullptr, 0, EmuCreateDeviceProxy, nullptr, 0, &dwThreadId); + // Ported from Dxbx : + // If possible, assign this thread to another core than the one that runs Xbox1 code : + SetThreadAffinityMask(&dwThreadId, g_CPUOthers); + } + + // Initialise CreateDevice Proxy Data struct + { + g_EmuCDPD = {0}; + g_EmuCDPD.Adapter = g_XBVideo.adapter; + g_EmuCDPD.DeviceType = (g_XBVideo.direct3DDevice == 0) ? D3DDEVTYPE_HAL : D3DDEVTYPE_REF; + g_EmuCDPD.hFocusWindow = g_hEmuWindow; + } + + // create Direct3D8 and retrieve caps + { + // xbox Direct3DCreate8 returns "1" always, so we need our own ptr + g_pDirect3D = Direct3DCreate(D3D_SDK_VERSION); + if(g_pDirect3D == nullptr) + CxbxKrnlCleanup("Could not initialize Direct3D8!"); + + g_pDirect3D->GetDeviceCaps(g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, &g_D3DCaps); + + // Dump Host D3DCaps to log unconditionally + std::cout << "----------------------------------------\n"; + std::cout << "Host D3DCaps : " << g_D3DCaps << "\n"; + std::cout << "----------------------------------------\n"; + } + + // AMD compatibility workaround since VS model 3.0 doesn't work as intended with Direct3D9. + { + D3DADAPTER_IDENTIFIER9 adapter_info; + HRESULT status = g_pDirect3D->GetAdapterIdentifier(g_EmuCDPD.Adapter, 0, &adapter_info); + // 1002 and 1022 are vendor ids of AMD gpus + if (status == D3D_OK && (adapter_info.VendorId == 0x1002 || adapter_info.VendorId == 0x1022)) { + g_vs_model = vs_model_2_a; + EmuLogInit(LOG_LEVEL::WARNING, "AMD GPU Detected, falling back to shader model 2.X to prevent missing polygons"); + } + } +} + +// cleanup Direct3D +VOID EmuD3DCleanup() {} + +// enumeration procedure for locating display device GUIDs +static BOOL WINAPI EmuEnumDisplayDevices(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) +{ + static DWORD dwEnumCount = 0; + + if(dwEnumCount++ == g_EmuCDPD.Adapter + 1) + { + g_hMonitor = hm; + dwEnumCount = 0; + if(lpGUID != 0) + { + memcpy(&g_ddguid, lpGUID, sizeof(GUID)); + } + else + { + memset(&g_ddguid, 0, sizeof(GUID)); + } + + return FALSE; + } + + return TRUE; +} + +// window message processing thread +static DWORD WINAPI EmuRenderWindow(LPVOID lpVoid) +{ + CxbxSetThreadName("Cxbx Render Window"); + SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); + + // register window class + { + LOGBRUSH logBrush = {BS_SOLID, RGB(0,0,0)}; + + g_hBgBrush = CreateBrushIndirect(&logBrush); + + WNDCLASSEX wc = + { + sizeof(WNDCLASSEX), + CS_CLASSDC, + EmuMsgProc, + 0, 0, hActiveModule, // Was GetModuleHandle(nullptr), + 0, // TODO : LoadIcon(hmodule, ?) + LoadCursor(NULL, IDC_ARROW), + (HBRUSH)(g_hBgBrush), NULL, + "CxbxRender", + nullptr + }; + + RegisterClassEx(&wc); + } + + // create the window + { + // Peform selection if running in GUI or kernel mode first. + HWND hwndParent = (!CxbxKrnl_hEmuParent ? GetDesktopWindow() : CxbxKrnl_hEmuParent); + DWORD dwStyle = WS_POPUP; + RECT windowRect = { 0 }; + + // Obtain the selected resolution from GUI or full desktop screen in kernel mode. + if (!GetWindowRect(hwndParent, &windowRect)) { + // Fall back resolution if failed + windowRect = { 0, 0, 640, 480 }; + } + + // Then perform additional checks if not running in full screen. + if (!g_XBVideo.bFullScreen) { + + // If running as kernel mode, force use the xbox's default resolution. + if (!CxbxKrnl_hEmuParent) { + // Xbox default resolution (standalone window is resizable by the way) + windowRect.right = 640; + windowRect.bottom = 480; + dwStyle = WS_OVERLAPPEDWINDOW; + } + else { + dwStyle = WS_CHILD; + } + } + + g_hEmuWindow = CreateWindow + ( + "CxbxRender", "Cxbx-Reloaded", + dwStyle, + windowRect.left, + windowRect.top, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + hwndParent, nullptr, hActiveModule, // Was GetModuleHandle(nullptr), + nullptr + ); + } + + ShowWindow(g_hEmuWindow, ((CxbxKrnl_hEmuParent == 0) || g_XBVideo.bFullScreen) ? SW_SHOWDEFAULT : SW_SHOWMAXIMIZED); + UpdateWindow(g_hEmuWindow); + + if(!g_XBVideo.bFullScreen && (CxbxKrnl_hEmuParent != NULL)) + { + SetFocus(CxbxKrnl_hEmuParent); + } + + EmuLog(LOG_LEVEL::DEBUG, "Message-Pump thread is running."); + + SetFocus(g_hEmuWindow); + +#ifdef INCLUDE_DBG_CONSOLE + DbgConsole *dbgConsole = new DbgConsole(); +#endif + + // message processing loop + { + MSG msg; + + ZeroMemory(&msg, sizeof(msg)); + + bool lPrintfOn = g_bPrintfOn; + + g_bRenderWindowActive = true; + + while(msg.message != WM_QUIT) + { + if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + Sleep(0); + +#ifdef INCLUDE_DBG_CONSOLE + // if we've just switched back to display off, clear buffer & display prompt + if(!g_bPrintfOn && lPrintfOn) + { + dbgConsole->Reset(); + } +#endif + + lPrintfOn = g_bPrintfOn; + +#ifdef INCLUDE_DBG_CONSOLE + dbgConsole->Process(); +#endif + } + } + + g_bRenderWindowActive = false; + +#ifdef INCLUDE_DBG_CONSOLE + delete dbgConsole; +#endif + + CxbxKrnlCleanup(nullptr); + } + + return 0; +} + +// simple helper function +void ToggleFauxFullscreen(HWND hWnd) +{ + if(g_XBVideo.bFullScreen) + return; + + static LONG lRestore = 0, lRestoreEx = 0; + static RECT lRect = {0}; + + if(!g_bIsFauxFullscreen) + { + if(CxbxKrnl_hEmuParent != NULL) + { + SetParent(hWnd, NULL); + } + else + { + lRestore = GetWindowLong(hWnd, GWL_STYLE); + lRestoreEx = GetWindowLong(hWnd, GWL_EXSTYLE); + + GetWindowRect(hWnd, &lRect); + } + + SetWindowLong(hWnd, GWL_STYLE, WS_POPUP); + ShowWindow(hWnd, SW_MAXIMIZE); + SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + } + else + { + if(CxbxKrnl_hEmuParent != NULL) + { + SetParent(hWnd, CxbxKrnl_hEmuParent); + SetWindowLong(hWnd, GWL_STYLE, WS_CHILD); + ShowWindow(hWnd, SW_MAXIMIZE); + SetFocus(CxbxKrnl_hEmuParent); + } + else + { + SetWindowLong(hWnd, GWL_STYLE, lRestore); + SetWindowLong(hWnd, GWL_EXSTYLE, lRestoreEx); + ShowWindow(hWnd, SW_RESTORE); + SetWindowPos(hWnd, HWND_NOTOPMOST, lRect.left, lRect.top, lRect.right - lRect.left, lRect.bottom - lRect.top, 0); + SetFocus(hWnd); + } + } + + g_bIsFauxFullscreen = !g_bIsFauxFullscreen; +} + +// rendering window message procedure +static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool bAutoPaused = false; + + switch(msg) + { + case WM_DESTROY: + { + DeleteObject(g_hBgBrush); + PostQuitMessage(0); + return D3D_OK; // = 0 + } + break; + + case WM_PAINT: + { + if (g_CxbxPrintUEM) + { + DrawUEM(hWnd); + } + } + break; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case ID_SYNC_CONFIG_LOGGING: + { + // Sync run-time config log settings from GUI process. + log_sync_config(); + log_generate_active_filter_output(CXBXR_MODULE::CXBXR); + } + break; + + case ID_SYNC_CONFIG_INPUT: + { + SDL_Event UpdateInputEvent; + SDL_memset(&UpdateInputEvent, 0, sizeof(SDL_Event)); + UpdateInputEvent.type = Sdl::UpdateInputEvent_t; + UpdateInputEvent.user.data1 = new int(lParam); + SDL_PushEvent(&UpdateInputEvent); + } + break; + + default: + break; + } + } + break; + + case WM_SYSKEYDOWN: + { + if(wParam == VK_RETURN) + { + ToggleFauxFullscreen(hWnd); + } + else if(wParam == VK_F4) + { + PostMessage(hWnd, WM_CLOSE, 0, 0); + } + // NOTE: Windows does not send F10 key message to WM_KEYDOWN. + // Source: https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-syskeydown + else if(wParam == VK_F10) + { + ToggleFauxFullscreen(hWnd); + } + else + { + return DefWindowProc(hWnd, msg, wParam, lParam); + } + } + break; + + case WM_KEYDOWN: + { + /*! disable fullscreen if we are set to faux mode, and faux fullscreen is active */ + if(wParam == VK_ESCAPE) + { + if(g_XBVideo.bFullScreen) + { + SendMessage(hWnd, WM_CLOSE, 0, 0); + } + else if(g_bIsFauxFullscreen) + { + ToggleFauxFullscreen(hWnd); + } + } + else if (wParam == VK_F1) + { + VertexBufferConverter.PrintStats(); + } + else if (wParam == VK_F6) + { + // For some unknown reason, F6 isn't handled in WndMain::WndProc + // sometimes, so detect it and stop emulation from here too : + SendMessage(hWnd, WM_CLOSE, 0, 0); // See StopEmulation(); + } + else if(wParam == VK_F8) + { + g_bPrintfOn = !g_bPrintfOn; + g_EmuShared->SetIsKrnlLogEnabled(g_bPrintfOn); + LOG_THREAD_INIT; + std::cout << _logThreadPrefix << g_EnumModules2String[to_underlying(CXBXR_MODULE::CXBXR)] << "Enable log is " << g_bPrintfOn << std::endl; + ipc_send_gui_update(IPC_UPDATE_GUI::LOG_ENABLED, static_cast(g_bPrintfOn)); + } + else if (wParam == VK_F9) + { + // Toggle frame-limiting + g_bHack_UnlockFramerate = !g_bHack_UnlockFramerate; + } + else if(wParam == VK_F11) + { + if (g_iWireframe++ == 2) { + g_iWireframe = 0; + } + + XboxRenderStates.SetWireFrameMode(g_iWireframe); + } + else + { + return DefWindowProc(hWnd, msg, wParam, lParam); + } + } + break; + + case WM_SIZE: + { + switch(wParam) + { + case SIZE_RESTORED: + case SIZE_MAXIMIZED: + { + if(bAutoPaused) + { + bAutoPaused = false; + CxbxKrnlResume(); + } + } + break; + + case SIZE_MINIMIZED: + { + if(g_XBVideo.bFullScreen) + CxbxKrnlCleanup(nullptr); + + if(!g_bEmuSuspended) + { + bAutoPaused = true; + CxbxKrnlSuspend(); + } + } + break; + } + } + break; + + case WM_CLOSE: + DestroyWindow(hWnd); + CxbxKrnlShutDown(); + break; + + case WM_SETFOCUS: + { + if(CxbxKrnl_hEmuParent != NULL) + { + SetFocus(CxbxKrnl_hEmuParent); + } + } + break; + + case WM_SETCURSOR: + { + if(g_XBVideo.bFullScreen || g_bIsFauxFullscreen) + { + SetCursor(NULL); + return S_OK; // = Is not part of D3D8 handling. + } + + return DefWindowProc(hWnd, msg, wParam, lParam); + } + break; + + default: + return DefWindowProc(hWnd, msg, wParam, lParam); + } + + return S_OK; // = Is not part of D3D8 handling. +} + +std::chrono::time_point> GetNextVBlankTime() +{ + // TODO: Read display frequency from Xbox Display Adapter + // This is accessed by calling CMiniport::GetRefreshRate(); + // This reads from the structure located at CMinpPort::m_CurrentAvInfo + // This will require at least Direct3D_CreateDevice being unpatched + // otherwise, m_CurrentAvInfo will never be initialised! + // 20ms should be used in the case of 50hz + return std::chrono::steady_clock::now() + 16.6666666667ms; +} + + +// timing thread procedure +static DWORD WINAPI EmuUpdateTickCount(LPVOID) +{ + CxbxSetThreadName("Cxbx Timing Thread"); + + // since callbacks come from here + InitXboxThread(g_CPUOthers); // avoid Xbox1 core for lowest possible latency + + EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running."); + + // current vertical blank count + int curvb = 0; + + // Calculate Next VBlank time + auto nextVBlankTime = GetNextVBlankTime(); + + while(true) + { + SwitchToThread(); + + // If VBlank Interval has passed, trigger VBlank callback + // Note: This whole code block can be removed once NV2A interrupts are implemented + // And Both Swap and Present can be ran unpatched + // Once that is in place, MiniPort + Direct3D will handle this on it's own! + // We check for LLE flag as NV2A handles it's own VBLANK if LLE is enabled! + if (!(bLLE_GPU) && std::chrono::steady_clock::now() > nextVBlankTime) + { + nextVBlankTime = GetNextVBlankTime(); + + // Increment the VBlank Counter and Wake all threads there were waiting for the VBlank to occur + std::unique_lock lk(g_VBConditionMutex); + g_Xbox_VBlankData.VBlank++; + g_VBConditionVariable.notify_all(); + + // TODO: Fixme. This may not be right... + g_Xbox_SwapData.SwapVBlank = 1; + + if(g_pXbox_VerticalBlankCallback != xbnullptr) + { + + g_pXbox_VerticalBlankCallback(&g_Xbox_VBlankData); + + } + + g_Xbox_VBlankData.Swap = 0; + + // TODO: This can't be accurate... + g_Xbox_SwapData.TimeUntilSwapVBlank = 0; + + // TODO: Recalculate this for PAL version if necessary. + // Also, we should check the D3DPRESENT_INTERVAL value for accurracy. + // g_Xbox_SwapData.TimeBetweenSwapVBlanks = 1/60; + g_Xbox_SwapData.TimeBetweenSwapVBlanks = 0; + } + } +} + +void UpdateDepthStencilFlags(IDirect3DSurface *pDepthStencilSurface) +{ + g_bHasDepth = false; + g_bHasStencil = false; + if (pDepthStencilSurface != nullptr) { + D3DSURFACE_DESC Desc; + pDepthStencilSurface->GetDesc(&Desc); + + switch (Desc.Format) { + case D3DFMT_D16: + g_bHasDepth = true; + break; + case D3DFMT_D15S1: + g_bHasDepth = true; + g_bHasStencil = true; + break; + case D3DFMT_D24X8: + g_bHasDepth = true; + break; + case D3DFMT_D24S8: + g_bHasDepth = true; + g_bHasStencil = true; + break; + case D3DFMT_D24X4S4: + g_bHasDepth = true; + g_bHasStencil = true; + break; + case D3DFMT_D32: + g_bHasDepth = true; + break; + } + } +} +// thread dedicated to create devices +static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) +{ + LOG_FUNC(); + + CxbxSetThreadName("Cxbx CreateDevice Proxy"); + + EmuLog(LOG_LEVEL::DEBUG, "CreateDevice proxy thread is running."); + + while(true) + { + // if we have been signalled, create the device with cached parameters + if(g_EmuCDPD.bReady) + { + EmuLog(LOG_LEVEL::DEBUG, "CreateDevice proxy thread received request."); + + // only one device should be created at once + if (g_pD3DDevice != nullptr) { + EmuLog(LOG_LEVEL::DEBUG, "CreateDevice proxy thread releasing old Device."); + + g_pD3DDevice->EndScene(); + + ClearResourceCache(g_Cxbx_Cached_PaletizedTextures); + ClearResourceCache(g_Cxbx_Cached_Direct3DResources); + + // TODO: ensure all other resources are cleaned up too + + g_EmuCDPD.hRet = g_pD3DDevice->Release(); + g_pD3DDevice = nullptr; + + // cleanup overlay clipper + if (g_pDDClipper != nullptr) { + g_pDDClipper->Release(); + g_pDDClipper = nullptr; + } + + // cleanup directdraw surface + if (g_pDDSPrimary != nullptr) { + g_pDDSPrimary->Release(); + g_pDDSPrimary = nullptr; + } + + // cleanup directdraw + if (g_pDD7 != nullptr) { + g_pDD7->Release(); + g_pDD7 = nullptr; + } + } + + if (g_EmuCDPD.bCreate) { + // Apply render scale factor for high-resolution rendering + g_RenderScaleFactor = g_XBVideo.renderScaleFactor; + + // Setup the HostPresentationParameters + { + g_EmuCDPD.HostPresentationParameters = {}; + g_EmuCDPD.HostPresentationParameters.Windowed = !g_XBVideo.bFullScreen; + + // TODO: Investigate the best option for this + g_EmuCDPD.HostPresentationParameters.SwapEffect = D3DSWAPEFFECT_COPY; + + // Attempt to match backbuffer format, this is not *required*, but leads to faster blitting/swapping + g_EmuCDPD.HostPresentationParameters.BackBufferFormat = EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.BackBufferFormat); + + g_EmuCDPD.HostPresentationParameters.PresentationInterval = g_XBVideo.bVSync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + g_Xbox_PresentationInterval_Default = g_EmuCDPD.XboxPresentationParameters.PresentationInterval; + + // We only want *one* backbuffer on the host, triple buffering, etc should be handled by our Present/Swap impl + g_EmuCDPD.HostPresentationParameters.BackBufferCount = 1; + + // We don't want multisampling on the host backbuffer, it should be applied to Xbox surfaces if required + g_EmuCDPD.HostPresentationParameters.MultiSampleType = D3DMULTISAMPLE_NONE; + g_EmuCDPD.HostPresentationParameters.MultiSampleQuality = 0; + + // We want a lockable backbuffer for swapping/blitting purposes + g_EmuCDPD.HostPresentationParameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; + + // retrieve resolution from configuration + char szBackBufferFormat[16] = {}; + const char* resolution = g_XBVideo.szVideoResolution; + if (4 != sscanf(resolution, "%u x %u %*dbit %s (%u hz)", + &g_EmuCDPD.HostPresentationParameters.BackBufferWidth, + &g_EmuCDPD.HostPresentationParameters.BackBufferHeight, + szBackBufferFormat, + &g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz)) { + EmuLog(LOG_LEVEL::DEBUG, "EmuCreateDeviceProxy: Couldn't parse resolution : %s. Using Xbox Default (%d, %d @ %uhz)", resolution, + g_EmuCDPD.XboxPresentationParameters.BackBufferWidth, g_EmuCDPD.XboxPresentationParameters.BackBufferHeight, + g_EmuCDPD.XboxPresentationParameters.FullScreen_RefreshRateInHz); + g_EmuCDPD.HostPresentationParameters.BackBufferWidth = g_EmuCDPD.XboxPresentationParameters.BackBufferWidth; + g_EmuCDPD.HostPresentationParameters.BackBufferHeight = g_EmuCDPD.XboxPresentationParameters.BackBufferHeight; + g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz = g_EmuCDPD.XboxPresentationParameters.FullScreen_RefreshRateInHz; + } + + if(g_EmuCDPD.HostPresentationParameters.Windowed) + { + D3DDISPLAYMODE D3DDisplayMode; + g_pDirect3D->GetAdapterDisplayMode(g_EmuCDPD.Adapter, &D3DDisplayMode); + + g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DDisplayMode.Format; + g_EmuCDPD.HostPresentationParameters.FullScreen_RefreshRateInHz = 0; + } + else + { + // In exclusive fullscreen mode, make *sure* to use the info that was in the resolution string + if (strcmp(szBackBufferFormat, "x1r5g5b5") == 0) + g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_X1R5G5B5; + else if (strcmp(szBackBufferFormat, "r5g6r5") == 0) + g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_R5G6B5; + else if (strcmp(szBackBufferFormat, "x8r8g8b8") == 0) + g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_X8R8G8B8; + else if (strcmp(szBackBufferFormat, "a8r8g8b8") == 0) + g_EmuCDPD.HostPresentationParameters.BackBufferFormat = D3DFMT_A8R8G8B8; + } + } + + // detect vertex processing capabilities + if((g_D3DCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && g_EmuCDPD.DeviceType == D3DDEVTYPE_HAL) + { + EmuLog(LOG_LEVEL::DEBUG, "Using hardware vertex processing"); + + g_EmuCDPD.BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; + // Unused : g_dwVertexShaderUsage = 0; + } + else + { + EmuLog(LOG_LEVEL::DEBUG, "Using software vertex processing"); + + g_EmuCDPD.BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING; + // Unused : g_dwVertexShaderUsage = D3DUSAGE_SOFTWAREPROCESSING; + } + + // Dxbx addition : Prevent Direct3D from changing the FPU Control word : + g_EmuCDPD.BehaviorFlags |= D3DCREATE_FPU_PRESERVE; + + // Direct3D8: (WARN) :Device that was created without D3DCREATE_MULTITHREADED is being used by a thread other than the creation thread. + g_EmuCDPD.BehaviorFlags |= D3DCREATE_MULTITHREADED; + + // We never want auto-depth stencil on the host, Xbox D3D will handle this for us + g_EmuCDPD.HostPresentationParameters.EnableAutoDepthStencil = FALSE; + + // redirect to windows Direct3D + g_EmuCDPD.hRet = g_pDirect3D->CreateDevice( + g_EmuCDPD.Adapter, + g_EmuCDPD.DeviceType, + g_EmuCDPD.hFocusWindow, + g_EmuCDPD.BehaviorFlags, + &g_EmuCDPD.HostPresentationParameters, + &g_pD3DDevice + ); + DEBUG_D3DRESULT(g_EmuCDPD.hRet, "IDirect3D::CreateDevice"); + + if(FAILED(g_EmuCDPD.hRet)) + CxbxKrnlCleanup("IDirect3D::CreateDevice failed"); + + // Which texture formats does this device support? + memset(g_bSupportsFormatSurface, false, sizeof(g_bSupportsFormatSurface)); + memset(g_bSupportsFormatSurfaceRenderTarget, false, sizeof(g_bSupportsFormatSurfaceRenderTarget)); + memset(g_bSupportsFormatSurfaceDepthStencil, false, sizeof(g_bSupportsFormatSurfaceDepthStencil)); + memset(g_bSupportsFormatTexture, false, sizeof(g_bSupportsFormatTexture)); + memset(g_bSupportsFormatTextureRenderTarget, false, sizeof(g_bSupportsFormatTextureRenderTarget)); + memset(g_bSupportsFormatTextureDepthStencil, false, sizeof(g_bSupportsFormatTextureDepthStencil)); + memset(g_bSupportsFormatVolumeTexture, false, sizeof(g_bSupportsFormatVolumeTexture)); + memset(g_bSupportsFormatCubeTexture, false, sizeof(g_bSupportsFormatCubeTexture)); + for (int X_Format = XTL::X_D3DFMT_L8; X_Format <= XTL::X_D3DFMT_LIN_R8G8B8A8; X_Format++) { + // Only process Xbox formats that are directly mappable to host + if (!EmuXBFormatRequiresConversionToARGB((XTL::X_D3DFORMAT)X_Format)) { + // Convert the Xbox format into host format (without warning, thanks to the above restriction) + D3DFORMAT PCFormat = EmuXB2PC_D3DFormat((XTL::X_D3DFORMAT)X_Format); + if (PCFormat != D3DFMT_UNKNOWN) { + // Index with Xbox D3DFormat, because host FourCC codes are too big to be used as indices + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, + D3DRTYPE_SURFACE, PCFormat)) + g_bSupportsFormatSurface[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_RENDERTARGET, + D3DRTYPE_SURFACE, PCFormat)) + g_bSupportsFormatSurfaceRenderTarget[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_SURFACE, PCFormat)) + g_bSupportsFormatSurfaceDepthStencil[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, + D3DRTYPE_TEXTURE, PCFormat)) + g_bSupportsFormatTexture[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_RENDERTARGET, + D3DRTYPE_TEXTURE, PCFormat)) + g_bSupportsFormatTextureRenderTarget[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, + D3DRTYPE_TEXTURE, PCFormat)) + g_bSupportsFormatTextureDepthStencil[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, + D3DRTYPE_VOLUMETEXTURE, PCFormat)) + g_bSupportsFormatVolumeTexture[X_Format] = true; + if (D3D_OK == g_pDirect3D->CheckDeviceFormat( + g_EmuCDPD.Adapter, g_EmuCDPD.DeviceType, + g_EmuCDPD.HostPresentationParameters.BackBufferFormat, 0, + D3DRTYPE_CUBETEXTURE, PCFormat)) + g_bSupportsFormatCubeTexture[X_Format] = true; + } + } + } + + // default NULL guid + ZeroMemory(&g_ddguid, sizeof(GUID)); + + HRESULT hRet; + + // enumerate device guid for this monitor, for directdraw + hRet = DirectDrawEnumerateExA(EmuEnumDisplayDevices, nullptr, DDENUM_ATTACHEDSECONDARYDEVICES); + DEBUG_D3DRESULT(hRet, "DirectDrawEnumerateExA"); + + // create DirectDraw7 + { + if(FAILED(hRet)) { + hRet = DirectDrawCreateEx(nullptr, (void**)&g_pDD7, IID_IDirectDraw7, nullptr); + DEBUG_D3DRESULT(hRet, "DirectDrawCreateEx(NULL)"); + } else { + hRet = DirectDrawCreateEx(&g_ddguid, (void**)&g_pDD7, IID_IDirectDraw7, nullptr); + DEBUG_D3DRESULT(hRet, "DirectDrawCreateEx(&g_ddguid)"); + } + + if(FAILED(hRet)) + CxbxKrnlCleanup("Could not initialize DirectDraw7"); + + hRet = g_pDD7->GetCaps(&g_DriverCaps, nullptr); + DEBUG_D3DRESULT(hRet, "g_pDD7->GetCaps"); + + hRet = g_pDD7->SetCooperativeLevel(0, DDSCL_NORMAL); + DEBUG_D3DRESULT(hRet, "g_pDD7->SetCooperativeLevel"); + + if(FAILED(hRet)) + CxbxKrnlCleanup("Could not set cooperative level"); + } + + // Dump all supported DirectDraw FourCC format codes + { + DWORD dwCodes = 0; + DWORD *lpCodes = nullptr; + + g_pDD7->GetFourCCCodes(&dwCodes, lpCodes); + lpCodes = (DWORD*)malloc(dwCodes*sizeof(DWORD)); + g_pDD7->GetFourCCCodes(&dwCodes, lpCodes); + for(DWORD v=0;vCreateQuery(D3DQUERYTYPE_EVENT, nullptr))) { + // Is host GPU query creation enabled? + if (!g_bHack_DisableHostGPUQueries) { + // Create a D3D event query to handle "wait-for-idle" with + hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &g_pHostQueryWaitForIdle); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (wait for idle)"); + + // Create a D3D event query to handle "callback events" with + hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &g_pHostQueryCallbackEvent); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (callback event)"); + } + } else { + LOG_TEST_CASE("Can't CreateQuery(D3DQUERYTYPE_EVENT) on host!"); + } + + // Can host driver create occlusion queries? + g_bEnableHostQueryVisibilityTest = false; + if (SUCCEEDED(g_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, nullptr))) { + // Is host GPU query creation enabled? + if (!g_bHack_DisableHostGPUQueries) { + g_bEnableHostQueryVisibilityTest = true; + } else { + LOG_TEST_CASE("Disabled D3DQUERYTYPE_OCCLUSION on host!"); + } + } else { + LOG_TEST_CASE("Can't CreateQuery(D3DQUERYTYPE_OCCLUSION) on host!"); + } + + hRet = g_pD3DDevice->CreateVertexBuffer + ( + 1, 0, 0, D3DPOOL_MANAGED, + &g_pDummyBuffer + , nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateVertexBuffer"); + + for(int Streams = 0; Streams < 16; Streams++) + { + hRet = g_pD3DDevice->SetStreamSource(Streams, g_pDummyBuffer, + 0, // OffsetInBytes + 1); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource"); + } + + // initially, show a black screen + // Only clear depth buffer and stencil if present + // + // Avoids following DirectX Debug Runtime error report + // [424] Direct3D8: (ERROR) :Invalid flag D3DCLEAR_ZBUFFER: no zbuffer is associated with device. Clear failed. + // + hRet = g_pD3DDevice->Clear( + /*Count=*/0, + /*pRects=*/nullptr, + D3DCLEAR_TARGET | (g_bHasDepth ? D3DCLEAR_ZBUFFER : 0) | (g_bHasStencil ? D3DCLEAR_STENCIL : 0), + /*Color=*/0xFF000000, // TODO : Use constant for this + /*Z=*/g_bHasDepth ? 1.0f : 0.0f, + /*Stencil=*/0); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Clear"); + + hRet = g_pD3DDevice->BeginScene(); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->BeginScene"); + + hRet = g_pD3DDevice->EndScene(); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->EndScene"); + + hRet = g_pD3DDevice->Present(0, 0, 0, 0); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Present"); + + // begin scene + hRet = g_pD3DDevice->BeginScene(); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->BeginScene(2nd)"); + + // Set up cache + g_VertexShaderSource.ResetD3DDevice(g_pD3DDevice); + } + + // signal completion + g_EmuCDPD.bReady = false; + } + + Sleep(1); + } + + return 0; +} + +// check if a resource has been registered yet (if not, register it) +void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize); // Forward declartion to prevent restructure of code +static void EmuVerifyResourceIsRegistered(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize) +{ + // Skip resources without data + if (pResource->Data == xbnull) + return; + + auto key = GetHostResourceKey(pResource, iTextureStage); + auto& ResourceCache = GetResourceCache(key); + auto it = ResourceCache.find(key); + if (it != ResourceCache.end()) { + if (D3DUsage == D3DUSAGE_RENDERTARGET && IsResourceAPixelContainer(pResource) && EmuXBFormatIsRenderTarget(GetXboxPixelContainerFormat((XTL::X_D3DPixelContainer*)pResource))) { + // Render targets have special behavior: We can't trash them on guest modification + // this fixes an issue where CubeMaps were broken because the surface Set in GetCubeMapSurface + // would be overwritten by the surface created in SetRenderTarget + // However, if a non-rendertarget surface is used here, we'll need to recreate it as a render target! + auto hostResource = it->second.pHostResource; + auto xboxSurface = (XTL::X_D3DSurface*)pResource; + auto xboxTexture = (XTL::X_D3DTexture*)pResource; + auto xboxResourceType = GetXboxD3DResourceType(pResource); + + // Determine if the associated host resource is a render target already, if so, do nothing + HRESULT hRet = STATUS_INVALID_PARAMETER; // Default to an error condition, so we can use D3D_OK to check for success + D3DSURFACE_DESC surfaceDesc; + if (xboxResourceType == XTL::X_D3DRTYPE_SURFACE) { + hRet = ((IDirect3DSurface*)hostResource)->GetDesc(&surfaceDesc); + } else if (xboxResourceType == XTL::X_D3DRTYPE_TEXTURE) { + hRet = ((IDirect3DTexture*)hostResource)->GetLevelDesc(0, &surfaceDesc); + } + + // Only continue checking if we were able to get the surface desc, if it failed, we fall-through + // to previous resource management behavior + if (SUCCEEDED(hRet)) { + // If this resource is already created as a render target on the host, simply return + if (surfaceDesc.Usage & D3DUSAGE_RENDERTARGET) { + return; + } + + // The host resource is not a render target, but the Xbox surface is + // We need to re-create it as a render target + switch (xboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: { + // Free the host surface + FreeHostResource(key); + + // Free the parent texture, if present + XTL::X_D3DTexture* pParentXboxTexture = (pResource) ? (XTL::X_D3DTexture*)xboxSurface->Parent : xbnullptr; + + if (pParentXboxTexture) { + // Re-create the texture with D3DUSAGE_RENDERTARGET, this will automatically create any child-surfaces + FreeHostResource(GetHostResourceKey(pParentXboxTexture, iTextureStage)); + CreateHostResource(pParentXboxTexture, D3DUsage, iTextureStage, dwSize); + } + + // Re-create the surface with D3DUSAGE_RENDERTARGET + CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); + } break; + case XTL::X_D3DRTYPE_TEXTURE: { + auto xboxTexture = (XTL::X_D3DTexture*)pResource; + + // Free the host texture + FreeHostResource(key); + + // And re-create the texture with D3DUSAGE_RENDERTARGET + CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); + } break; + default: + LOG_TEST_CASE("Unimplemented rendertarget type"); + } + + return; + } + } + + //check if the same key existed in the HostResource map already. if there is a old pXboxResource in the map with the same key but different resource address, it must be freed first. + + if (it->second.pXboxResource != pResource) { + //printf("EmuVerifyResourceIsRegistered passed in XboxResource collides HostResource map!! key : %llX , map pXboxResource : %08X , passed in pResource : %08X \n", key, it->second.pXboxResource, pResource); + } + else { + if (!HostResourceRequiresUpdate(key, dwSize)) { + return; + } + } + + FreeHostResource(key); + } else { + resource_info_t newResource; + ResourceCache[key] = newResource; + } + + CreateHostResource(pResource, D3DUsage, iTextureStage, dwSize); +} + +// TODO : Move to own file +constexpr UINT QuadToTriangleVertexCount(UINT NrOfQuadVertices) +{ + return (NrOfQuadVertices * VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) / VERTICES_PER_QUAD; +} + +// TODO : Move to own file (or argument of all users) +bool bUseClockWiseWindingOrder = true; // TODO : Should this be fetched from X_D3DRS_FRONTFACE (or X_D3DRS_CULLMODE)? + +// TODO : Move to own file +// This function convertes quad to triangle indices. +// When pXboxQuadIndexData is set, original quad indices are read from this buffer +// (this use-case is for when an indexed quad draw is to be emulated). +// When pXboxQuadIndexData is null, quad-emulating indices are generated +// (this use-case is for when a non-indexed quad draw is to be emulated). +// The number of indices to generate is specified through uNrOfTriangleIndices. +// Resulting triangle indices are written to pTriangleIndexData, which must +// be pre-allocated to fit the output data. +// (Note, this function is marked 'constexpr' to allow the compiler to optimize +// the case when pXboxQuadIndexData is null) +constexpr void CxbxConvertQuadListToTriangleListIndices( + INDEX16* pXboxQuadIndexData, + unsigned uNrOfTriangleIndices, + INDEX16* pTriangleIndexData) +{ + assert(uNrOfTriangleIndices > 0); + assert(pTriangleIndexData); + + unsigned i = 0; + unsigned j = 0; + while (i + (VERTICES_PER_TRIANGLE * TRIANGLES_PER_QUAD) <= uNrOfTriangleIndices) { + if (bUseClockWiseWindingOrder) { + // ABCD becomes ABC+CDA, so this is triangle 1 : + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 1] : j + 1; // B + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C + i += VERTICES_PER_TRIANGLE; + // And this is triangle 2 : + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 3] : j + 3; // D + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A + i += VERTICES_PER_TRIANGLE; + } else { + // ABCD becomes ADC+CBA, so this is triangle 1 : + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 3] : j + 3; // D + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C + i += VERTICES_PER_TRIANGLE; + // And this is triangle 2 : + pTriangleIndexData[i + 0] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 2] : j + 2; // C + pTriangleIndexData[i + 1] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 1] : j + 1; // B + pTriangleIndexData[i + 2] = pXboxQuadIndexData ? pXboxQuadIndexData[j + 0] : j + 0; // A + i += VERTICES_PER_TRIANGLE; + } + + // Next quad, please : + j += VERTICES_PER_QUAD; + } +} + +// TODO : Move to own file +// Called from EMUPATCH(D3DDevice_DrawIndexedVerticesUP) when PrimitiveType == X_D3DPT_QUADLIST. +// This API receives the number of vertices to draw (VertexCount), the index data that references +// vertices and a single stream of vertex data. The number of vertices to draw indicates the number +// of indices that are going to be fetched. The vertex data is referenced up to the highest index +// number present in the index data. +// To emulate drawing indexed quads, g_pD3DDevice->DrawIndexedPrimitiveUP is called on host, +// whereby the quad indices are converted to triangle indices. This implies for every four +// quad indices, we have to generate (two times three is) six triangle indices. (Note, that +// vertex data undergoes it's own Xbox-to-host conversion, independent from these indices.) +INDEX16* CxbxCreateQuadListToTriangleListIndexData(INDEX16* pXboxQuadIndexData, unsigned QuadVertexCount) +{ + UINT NrOfTriangleIndices = QuadToTriangleVertexCount(QuadVertexCount); + INDEX16* pQuadToTriangleIndexBuffer = (INDEX16*)malloc(NrOfTriangleIndices * sizeof(INDEX16)); + CxbxConvertQuadListToTriangleListIndices(pXboxQuadIndexData, NrOfTriangleIndices, pQuadToTriangleIndexBuffer); + return pQuadToTriangleIndexBuffer; +} + +// TODO : Move to own file +void CxbxReleaseQuadListToTriangleListIndexData(void* pHostIndexData) +{ + free(pHostIndexData); +} + +class ConvertedIndexBuffer { +public: + uint64_t Hash = 0; + DWORD IndexCount = 0; + IDirect3DIndexBuffer* pHostIndexBuffer = nullptr; + INDEX16 LowIndex = 0; + INDEX16 HighIndex = 0; + + ~ConvertedIndexBuffer() + { + if (pHostIndexBuffer != nullptr) { + pHostIndexBuffer->Release(); + } + } +}; + +std::unordered_map g_IndexBufferCache; + +void CxbxRemoveIndexBuffer(PWORD pData) +{ + // HACK: Never Free +} + +IDirect3DIndexBuffer* CxbxCreateIndexBuffer(unsigned IndexCount) +{ + LOG_INIT; // Allows use of DEBUG_D3DRESULT + + IDirect3DIndexBuffer* Result = nullptr; + + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb147168(v=vs.85).aspx + // "Managing Resources (Direct3D 9)" + // suggests "for resources which change with high frequency" [...] + // "D3DPOOL_DEFAULT along with D3DUSAGE_DYNAMIC should be used." + const D3DPOOL D3DPool = D3DPOOL_DEFAULT; // Was D3DPOOL_MANAGED + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb172625(v=vs.85).aspx + // "Buffers created with D3DPOOL_DEFAULT that do not specify D3DUSAGE_WRITEONLY may suffer a severe performance penalty." + const DWORD D3DUsage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; // Was D3DUSAGE_WRITEONLY + + // Create a new native index buffer of the requested size : + UINT uiIndexBufferSize = IndexCount * sizeof(INDEX16); + HRESULT hRet = g_pD3DDevice->CreateIndexBuffer( + uiIndexBufferSize, // Size of the index buffer, in bytes. + D3DUsage, + /*Format=*/D3DFMT_INDEX16, + D3DPool, + &Result, + nullptr // pSharedHandle + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateIndexBuffer"); + if (FAILED(hRet)) { + assert(Result == nullptr); + } + + return Result; +} + +ConvertedIndexBuffer& CxbxUpdateActiveIndexBuffer +( + INDEX16* pXboxIndexData, + unsigned XboxIndexCount, + bool bConvertQuadListToTriangleList +) +{ + LOG_INIT; // Allows use of DEBUG_D3DRESULT + + uint32_t LookupKey = (uint32_t)pXboxIndexData; + unsigned RequiredIndexCount = XboxIndexCount; + + if (bConvertQuadListToTriangleList) { + LOG_TEST_CASE("bConvertQuadListToTriangleList"); + RequiredIndexCount = QuadToTriangleVertexCount(XboxIndexCount); + // For now, indicate the quad-to-triangles case using the otherwise + // (due to alignment) always-zero least significant bit : + LookupKey |= 1; + } + + // Poor-mans eviction policy : when exceeding treshold, clear entire cache : + if (g_IndexBufferCache.size() > 256) { + g_IndexBufferCache.clear(); // Note : ConvertedIndexBuffer destructor will release any assigned pHostIndexBuffer + } + + // Create a reference to the active buffer + ConvertedIndexBuffer& CacheEntry = g_IndexBufferCache[LookupKey]; + + // If the current index buffer size is too small, free it, so it'll get re-created + bool bNeedRepopulation = CacheEntry.IndexCount < RequiredIndexCount; + if (bNeedRepopulation) { + if (CacheEntry.pHostIndexBuffer != nullptr) + CacheEntry.pHostIndexBuffer->Release(); + + CacheEntry = {}; + } + + // If we need to create an index buffer, do so. + if (CacheEntry.pHostIndexBuffer == nullptr) { + CacheEntry.pHostIndexBuffer = CxbxCreateIndexBuffer(RequiredIndexCount); + if (!CacheEntry.pHostIndexBuffer) + CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: IndexBuffer Create Failed!"); + } + + // TODO : Speeds this up, perhaps by hashing less often, and/or by + // doing two hashes : a small subset regularly, all data less frequently. + uint64_t uiHash = ComputeHash(pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); + + // If the data needs updating, do so + bNeedRepopulation |= (uiHash != CacheEntry.Hash); + if (bNeedRepopulation) { + // Update the Index Count and the hash + CacheEntry.IndexCount = RequiredIndexCount; + CacheEntry.Hash = uiHash; + + // Update the host index buffer + INDEX16* pHostIndexBufferData = nullptr; + HRESULT hRet = CacheEntry.pHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); + DEBUG_D3DRESULT(hRet, "CacheEntry.pHostIndexBuffer->Lock"); + if (pHostIndexBufferData == nullptr) { + CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: Could not lock index buffer!"); + } + + // Determine highest and lowest index in use : + WalkIndexBuffer(CacheEntry.LowIndex, CacheEntry.HighIndex, pXboxIndexData, XboxIndexCount); + if (bConvertQuadListToTriangleList) { + // Note, that LowIndex and HighIndex won't change due to any quad-to-triangle conversion, + // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. + EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Converting quads to %d triangle indices (D3DFMT_INDEX16)", RequiredIndexCount); + CxbxConvertQuadListToTriangleListIndices(pXboxIndexData, RequiredIndexCount, pHostIndexBufferData); + } else { + EmuLog(LOG_LEVEL::DEBUG, "CxbxUpdateActiveIndexBuffer: Copying %d indices (D3DFMT_INDEX16)", XboxIndexCount); + memcpy(pHostIndexBufferData, pXboxIndexData, XboxIndexCount * sizeof(INDEX16)); + } + + CacheEntry.pHostIndexBuffer->Unlock(); + } + + // Activate the new native index buffer : + HRESULT hRet = g_pD3DDevice->SetIndices(CacheEntry.pHostIndexBuffer); + // Note : Under Direct3D 9, the BaseVertexIndex argument is moved towards DrawIndexedPrimitive + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); + + if (FAILED(hRet)) + CxbxKrnlCleanup("CxbxUpdateActiveIndexBuffer: SetIndices Failed!"); + + return CacheEntry; +} + +#define CXBX_D3DMULTISAMPLE_XSCALE(type) (((type) & XTL::X_D3DMULTISAMPLE_XSCALE_MASK) >> XTL::X_D3DMULTISAMPLE_XSCALE_SHIFT) +#define CXBX_D3DMULTISAMPLE_YSCALE(type) (((type) & XTL::X_D3DMULTISAMPLE_YSCALE_MASK) >> XTL::X_D3DMULTISAMPLE_YSCALE_SHIFT) + +void SetXboxMultiSampleType(XTL::X_D3DMULTISAMPLE_TYPE value) +{ + // Validate & correct input, to detect test cases and avoid trouble when using g_Xbox_MultiSampleType : + if (value == 0) { + // Correcting zero to X_D3DMULTISAMPLE_NONE + value = XTL::X_D3DMULTISAMPLE_NONE; + } + if (value & ~XTL::X_D3DMULTISAMPLE_KNOWN_MASK) { + LOG_TEST_CASE("Removing unknown X_D3DMULTISAMPLE bits"); + value &= XTL::X_D3DMULTISAMPLE_KNOWN_MASK; + } + if (CXBX_D3DMULTISAMPLE_XSCALE(value) == 0) { + LOG_TEST_CASE("Correcting XSCALE 0 to 1"); + value |= 1 << XTL::X_D3DMULTISAMPLE_XSCALE_SHIFT; + } + if (CXBX_D3DMULTISAMPLE_YSCALE(value) == 0) { + LOG_TEST_CASE("Correcting YSCALE 0 to 1"); + value |= 1 << XTL::X_D3DMULTISAMPLE_YSCALE_SHIFT; + } + if ((CXBX_D3DMULTISAMPLE_XSCALE(value) == 1) && (CXBX_D3DMULTISAMPLE_YSCALE(value) == 1)) { + if (value & XTL::X_D3DMULTISAMPLE_ALGO_MASK) { + LOG_TEST_CASE("Removing algorithm for 1 by 1 scale"); + value &= ~XTL::X_D3DMULTISAMPLE_ALGO_MASK; + } + if (value & XTL::X_D3DMULTISAMPLE_SAMPLING_MASK) { + LOG_TEST_CASE("Removing sampling for 1 by 1 scale"); + value &= ~XTL::X_D3DMULTISAMPLE_SAMPLING_MASK; + } + } + if (value == XTL::X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN) { + LOG_TEST_CASE("X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN"); // This used to use scale 1.5f + } + // Set the now-validated g_Xbox_MultiSampleType value : + g_Xbox_MultiSampleType = value; +} + +static inline float GetMultiSampleOffsetDelta() +{ + // TODO : What impact does X_D3DMULTISAMPLE_SAMPLING_SUPER have on offset? + return (g_Xbox_MultiSampleType & XTL::X_D3DMULTISAMPLE_SAMPLING_MULTI) ? 0.0f : 0.5f; +} + +static inline void GetMultiSampleOffset(float& xOffset, float& yOffset) +{ + xOffset = yOffset = GetMultiSampleOffsetDelta(); +} + +static inline void GetMultiSampleScale(float& xScale, float& yScale) +{ + xScale = (float)CXBX_D3DMULTISAMPLE_XSCALE(g_Xbox_MultiSampleType); + yScale = (float)CXBX_D3DMULTISAMPLE_YSCALE(g_Xbox_MultiSampleType); +} + +void GetMultiSampleOffsetAndScale(float& xScale, float& yScale, float& xOffset, float& yOffset) +{ + GetMultiSampleScale(xScale, yScale); + GetMultiSampleOffset(xOffset, yOffset); +} + +static void ApplyXboxMultiSampleOffset(float& x, float& y) +{ + float d = GetMultiSampleOffsetDelta(); + x += d; + y += d; +} + +static void ApplyXboxMultiSampleScale(float& x, float& y) +{ + float xs, ys; + GetMultiSampleScale(xs, ys); + x *= xs; + y *= ys; +} + +void ApplyXboxMultiSampleOffsetAndScale(float& x, float& y) +{ + ApplyXboxMultiSampleScale(x, y); + ApplyXboxMultiSampleOffset(x, y); +} + +void Direct3D_CreateDevice_Start +( + XTL::X_D3DPRESENT_PARAMETERS *pPresentationParameters +) +{ + if (!XboxRenderStates.Init()) { + CxbxKrnlCleanup("Failed to init XboxRenderStates"); + } + + if (!XboxTextureStates.Init(&XboxRenderStates)) { + CxbxKrnlCleanup("Failed to init XboxTextureStates"); + } + + SetXboxMultiSampleType(pPresentationParameters->MultiSampleType); + + // create default device *before* calling Xbox Direct3D_CreateDevice trampline + // to avoid hitting EMUPATCH'es that need a valid g_pD3DDevice + { + // Wait until proxy is done with an existing call (i highly doubt this situation will come up) + while (g_EmuCDPD.bReady) + Sleep(10); + + // Cache parameters + memcpy(&(g_EmuCDPD.XboxPresentationParameters), pPresentationParameters, sizeof(XTL::X_D3DPRESENT_PARAMETERS)); + + // Signal proxy thread (this will trigger EmuCreateDeviceProxy to call CreateDevice) + g_EmuCDPD.bCreate = true; + g_EmuCDPD.bReady = true; + + // Wait until host proxy is completed (otherwise, Xbox code could hit patches that need an assigned g_pD3DDevice) + while (g_EmuCDPD.bReady) + Sleep(10); + } +} + +void Direct3D_CreateDevice_End() +{ +#if 0 // Unused : + // Set g_Xbox_D3DDevice to point to the Xbox D3D Device + auto it = g_SymbolAddresses.find("D3DDEVICE"); + if (it != g_SymbolAddresses.end()) { + g_Xbox_D3DDevice = (DWORD*)it->second; + } +#endif + // If the Xbox version of CreateDevice didn't call SetRenderTarget, we must derive the default backbuffer ourselves + // This works because CreateDevice always sets the current render target to the Xbox Backbuffer + // In later XDKs, it does this inline rather than by calling D3DDevice_SetRenderTarget + // meaning our patch doesn't always get called in these cases. + // We fix the situation by calling the Xbox GetRenderTarget function, which immediately after CreateDevice + // WILL always return the Backbuffer! + // Test Case: Shin Megami Tensei: Nine + if (g_pXbox_BackBufferSurface == xbnullptr && g_pXbox_DefaultDepthStencilSurface == xbnullptr) { + // First, log the test case + LOG_TEST_CASE("Xbox CreateDevice did not call SetRenderTarget"); + } + + if (g_pXbox_BackBufferSurface == xbnullptr) { + if (XB_TRMP(D3DDevice_GetRenderTarget)) { + XB_TRMP(D3DDevice_GetRenderTarget)(&g_pXbox_BackBufferSurface); + } + else if (XB_TRMP(D3DDevice_GetRenderTarget2)) { + g_pXbox_BackBufferSurface = XB_TRMP(D3DDevice_GetRenderTarget2)(); + } + + // At this point, pRenderTarget should now point to a valid render target + // if it still doesn't, we cannot continue without crashing at draw time + if (g_pXbox_BackBufferSurface == xbnullptr) { + CxbxKrnlCleanup("Unable to determine default Xbox backbuffer"); + } + + // We must also call our SetRenderTarget patch to properly setup the host state + // Update only the Back buffer + XTL::EMUPATCH(D3DDevice_SetRenderTarget)(g_pXbox_BackBufferSurface, xbnullptr); + } + + // Now do the same, but for the default depth stencil surface + if (g_pXbox_DefaultDepthStencilSurface == xbnullptr) { + if (XB_TRMP(D3DDevice_GetDepthStencilSurface)) { + XB_TRMP(D3DDevice_GetDepthStencilSurface)(&g_pXbox_DefaultDepthStencilSurface); + } + else if (XB_TRMP(D3DDevice_GetDepthStencilSurface2)) { + g_pXbox_DefaultDepthStencilSurface = XB_TRMP(D3DDevice_GetDepthStencilSurface2)(); + } + + // At this point, g_pXbox_DefaultDepthStencilSurface should now point to a valid depth stencil + // If it doesn't, just log and carry on: Unlike RenderTarget, this situation is not fatal + if (g_pXbox_DefaultDepthStencilSurface == xbnullptr) { + LOG_TEST_CASE("Unable to determine default Xbox depth stencil"); + } else { + // Update only the depth stencil + XTL::EMUPATCH(D3DDevice_SetRenderTarget)(xbnullptr, g_pXbox_DefaultDepthStencilSurface); + } + } +} + +// LTCG specific Direct3D_CreateDevice function... +// This uses a custom calling with parameters passed in eax, ecx and the stack +// Test-case: Ninja Gaiden, Halo 2 +HRESULT WINAPI XTL::EMUPATCH(Direct3D_CreateDevice_4) +( + X_D3DPRESENT_PARAMETERS *pPresentationParameters +) +{ + DWORD BehaviorFlags; + IDirect3DDevice **ppReturnedDeviceInterface; + + __asm { + mov BehaviorFlags, eax + mov ppReturnedDeviceInterface, ecx + } + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pPresentationParameters) + LOG_FUNC_END; + + Direct3D_CreateDevice_Start(pPresentationParameters); + + HRESULT hRet = 0; + + // Only then call Xbox CreateDevice function + __asm { + mov eax, BehaviorFlags + mov ecx, ppReturnedDeviceInterface + push pPresentationParameters + call XB_TRMP(Direct3D_CreateDevice_4) + mov hRet, eax + } + + Direct3D_CreateDevice_End(); + + return hRet; +} + +// LTCG specific Direct3D_CreateDevice function... +// This uses a custom calling convention passed unknown parameters +// Test-case: Battle Engine Aquila +HRESULT WINAPI XTL::EMUPATCH(Direct3D_CreateDevice_16) +( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + X_D3DPRESENT_PARAMETERS *pPresentationParameters +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Adapter) + LOG_FUNC_ARG(DeviceType) + LOG_FUNC_ARG(hFocusWindow) + LOG_FUNC_ARG(pPresentationParameters) + LOG_FUNC_END; + + Direct3D_CreateDevice_Start(pPresentationParameters); + + // Only then call Xbox CreateDevice function + HRESULT hRet = XB_TRMP(Direct3D_CreateDevice_16)(Adapter, DeviceType, hFocusWindow, pPresentationParameters); + + Direct3D_CreateDevice_End(); + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_SetIndices_4 +// LTCG specific D3DDevice_SetIndices function... +// This uses a custom calling convention where parameter is passed in EBX and Stack +// Test Case: Conker +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetIndices_4) +( + UINT BaseVertexIndex +) +{ + X_D3DIndexBuffer *pIndexData; + + __asm { + mov pIndexData, ebx + } + // Cache the base vertex index + g_Xbox_BaseVertexIndex = BaseVertexIndex; + + // Call LTCG-specific trampoline + __asm { + mov ebx, pIndexData + push BaseVertexIndex + call XB_TRMP(D3DDevice_SetIndices_4); + } +} + + +// ****************************************************************** +// * patch: D3DDevice_SetIndices +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetIndices) +( + X_D3DIndexBuffer *pIndexData, + UINT BaseVertexIndex +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pIndexData) + LOG_FUNC_ARG(BaseVertexIndex) + LOG_FUNC_END; + + // Cache the base vertex index then call the Xbox function + g_Xbox_BaseVertexIndex = BaseVertexIndex; + + XB_TRMP(D3DDevice_SetIndices)(pIndexData, BaseVertexIndex); +} + +// ****************************************************************** +// * patch: Direct3D_CreateDevice +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(Direct3D_CreateDevice) +( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + X_D3DPRESENT_PARAMETERS *pPresentationParameters, + IDirect3DDevice **ppReturnedDeviceInterface +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Adapter) + LOG_FUNC_ARG(DeviceType) + LOG_FUNC_ARG(hFocusWindow) + LOG_FUNC_ARG(BehaviorFlags) // Xbox ignores BehaviorFlags + LOG_FUNC_ARG(pPresentationParameters) + LOG_FUNC_ARG(ppReturnedDeviceInterface) + LOG_FUNC_END; + + Direct3D_CreateDevice_Start(pPresentationParameters); + + // Only then call Xbox CreateDevice function + HRESULT hRet = XB_TRMP(Direct3D_CreateDevice)(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); + + Direct3D_CreateDevice_End(); + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_Reset +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_Reset) +( + X_D3DPRESENT_PARAMETERS* pPresentationParameters +) +{ + LOG_FUNC_ONE_ARG(pPresentationParameters) + + // Unlike the host version of Reset, The Xbox version does not actually reset the entire device + // Instead, it simply re-creates the backbuffer with a new configuration + + // Store the new multisampling configuration + SetXboxMultiSampleType(pPresentationParameters->MultiSampleType); + + // Since Reset will call create a new backbuffer surface, we can clear our current association + // NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below. + // We must not reset the values to nullptr, since the XDK will re-use the same addresses for the data headers + // (they are members of the Direct3DDevice object). if we overwrite then, the reference to the xbox backbuffer will be lost + // and we'll get a black screen. + FreeHostResource(GetHostResourceKey(g_pXbox_BackBufferSurface)); + FreeHostResource(GetHostResourceKey(g_pXbox_DefaultDepthStencilSurface)); + + // Call the Xbox Reset function to do the rest of the work for us + HRESULT hRet = XB_TRMP(D3DDevice_Reset)(pPresentationParameters); + + // Refresh the current render target and depth stencil, to apply changes made within D3DDevice_Reset + // Some XDKs do this for us, but not all do! + EMUPATCH(D3DDevice_SetRenderTarget)(g_pXbox_RenderTarget, g_pXbox_DepthStencil); + + return hRet; +} + + +// ****************************************************************** +// * patch: D3DDevice_GetDisplayFieldStatus +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetDisplayFieldStatus)(X_D3DFIELD_STATUS *pFieldStatus) +{ + // NOTE: This can be unpatched only when NV2A does it's own VBlank and HLE _Swap function is unpatched + + + LOG_FUNC_ONE_ARG(pFieldStatus); + + // Read AV Flags to determine if Progressive or Interlaced + // The xbox does this by reading from pDevice->m_Miniport.m_CurrentAvInfo + // but we don't have an OOVPA for that. Instead, we call the Xbox implementation of + // D3DDevice_GetDisplayMode and read the result + + X_D3DDISPLAYMODE displayMode; + + // If we can find the Xbox version of GetDisplayMode, use the real data returned, otherwise + // use a sensible default + if (XB_TRMP(D3DDevice_GetDisplayMode) != nullptr) { + XB_TRMP(D3DDevice_GetDisplayMode)(&displayMode); + } else { + // We don't show a warning because doing so pollutes the kernel debug log as this function gets called every frame + displayMode.Flags = X_D3DPRESENTFLAG_INTERLACED; + } + + // Set the VBlank count + pFieldStatus->VBlankCount = g_Xbox_VBlankData.VBlank; + + // If we are interlaced, return the current field, otherwise, return progressive scan + if (displayMode.Flags & X_D3DPRESENTFLAG_INTERLACED) { + pFieldStatus->Field = (g_Xbox_VBlankData.VBlank % 2 == 0) ? X_D3DFIELD_ODD : X_D3DFIELD_EVEN; + } else { + pFieldStatus->Field = X_D3DFIELD_PROGRESSIVE; + } +} + +// ****************************************************************** +// * patch: D3DDevice_BeginPush +// TODO: Find a test case and verify this +// Starting from XDK 4531, this changed to 1 parameter only. +// Is this definition incorrect, or did it change at some point? +// ****************************************************************** +PDWORD WINAPI XTL::EMUPATCH(D3DDevice_BeginPush)(DWORD Count) +{ + LOG_FUNC_ONE_ARG(Count); + + if (g_pXbox_BeginPush_Buffer != nullptr) + { + EmuLog(LOG_LEVEL::WARNING, "D3DDevice_BeginPush called without D3DDevice_EndPush in between?!"); + delete[] g_pXbox_BeginPush_Buffer; // prevent a memory leak + } + + DWORD *pRet = new DWORD[Count]; + + g_pXbox_BeginPush_Buffer = pRet; + + return pRet; +} + +// ****************************************************************** +// * patch: D3DDevice_BeginPush2 +// TODO: Find a test case and verify this: RalliSport Challenge XDK 4134 +// For XDK before 4531 +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_BeginPush2)(DWORD Count, DWORD** ppPush) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Count) + LOG_FUNC_ARG(ppPush) + LOG_FUNC_END; + + if (g_pXbox_BeginPush_Buffer != nullptr) + { + EmuLog(LOG_LEVEL::WARNING, "D3DDevice_BeginPush2 called without D3DDevice_EndPush in between?!"); + delete[] g_pXbox_BeginPush_Buffer; // prevent a memory leak + } + + DWORD *pRet = new DWORD[Count]; + + g_pXbox_BeginPush_Buffer = pRet; + + *ppPush=pRet; +} + +// ****************************************************************** +// * patch: D3DDevice_EndPush +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_EndPush)(DWORD *pPush) +{ + LOG_FUNC_ONE_ARG(pPush); + + if (g_pXbox_BeginPush_Buffer == nullptr) + EmuLog(LOG_LEVEL::WARNING, "D3DDevice_EndPush called without preceding D3DDevice_BeginPush?!"); + else + { + // Note: We don't use the count from BeginPush because that specifies the *maximum* count + // rather than the count actually in the pushbuffer. + EmuExecutePushBufferRaw(g_pXbox_BeginPush_Buffer, (uintptr_t)pPush - (uintptr_t)g_pXbox_BeginPush_Buffer); + + delete[] g_pXbox_BeginPush_Buffer; + g_pXbox_BeginPush_Buffer = nullptr; + } +} + +// ****************************************************************** +// * patch: D3DDevice_BeginVisibilityTest +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_BeginVisibilityTest)() +{ + LOG_FUNC(); + + if (g_bEnableHostQueryVisibilityTest) { + // Create a D3D occlusion query to handle "visibility test" with + IDirect3DQuery* pHostQueryVisibilityTest = nullptr; + HRESULT hRet = g_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pHostQueryVisibilityTest); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateQuery (visibility test)"); + if (pHostQueryVisibilityTest != nullptr) { + hRet = pHostQueryVisibilityTest->Issue(D3DISSUE_BEGIN); + DEBUG_D3DRESULT(hRet, "g_pHostQueryVisibilityTest->Issue(D3DISSUE_BEGIN)"); + if (SUCCEEDED(hRet)) { + g_HostQueryVisibilityTests.push(pHostQueryVisibilityTest); + } else { + LOG_TEST_CASE("Failed to issue query"); + pHostQueryVisibilityTest->Release(); + } + + pHostQueryVisibilityTest = nullptr; + } + } + + return D3D_OK; +} + +// LTCG specific D3DDevice_EndVisibilityTest function... +// This uses a custom calling convention where parameter is passed in EAX +// Test-case: Test Drive: Eve of Destruction +HRESULT __stdcall XTL::EMUPATCH(D3DDevice_EndVisibilityTest_0) +( +) +{ + DWORD Index; + + __asm { + mov Index, eax + } + + return EMUPATCH(D3DDevice_EndVisibilityTest)(Index); +} + +// ****************************************************************** +// * patch: D3DDevice_EndVisibilityTest +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_EndVisibilityTest) +( + DWORD Index +) +{ + LOG_FUNC_ONE_ARG(Index); + + if (g_bEnableHostQueryVisibilityTest) { + // Check that the dedicated storage for the given Index isn't in use + if (g_HostVisibilityTestMap[Index] != nullptr) { + return E_OUTOFMEMORY; + } + + if (g_HostQueryVisibilityTests.empty()) { + return 2088; // visibility test incomplete (a prior BeginVisibilityTest call is needed) + } + + IDirect3DQuery* pHostQueryVisibilityTest = g_HostQueryVisibilityTests.top(); + g_HostQueryVisibilityTests.pop(); + assert(pHostQueryVisibilityTest != nullptr); + + HRESULT hRet = pHostQueryVisibilityTest->Issue(D3DISSUE_END); + DEBUG_D3DRESULT(hRet, "g_pHostQueryVisibilityTest->Issue(D3DISSUE_END)"); + if (hRet == D3D_OK) { + // Associate the result of this call with the given Index + g_HostVisibilityTestMap[Index] = pHostQueryVisibilityTest; + } else { + LOG_TEST_CASE("Failed to issue query"); + pHostQueryVisibilityTest->Release(); + } + } + + return D3D_OK; +} + +// ****************************************************************** +// * patch: D3DDevice_SetBackBufferScale +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetBackBufferScale)(FLOAT x, FLOAT y) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(x) + LOG_FUNC_ARG(y) + LOG_FUNC_END; + + LOG_IGNORED(); +} + +// ****************************************************************** +// * patch: D3DDevice_GetVisibilityTestResult +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVisibilityTestResult) +( + DWORD Index, + UINT *pResult, + ULONGLONG *pTimeStamp +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Index) + LOG_FUNC_ARG(pResult) + LOG_FUNC_ARG(pTimeStamp) + LOG_FUNC_END; + + if (g_bEnableHostQueryVisibilityTest) { + IDirect3DQuery* pHostQueryVisibilityTest = g_HostVisibilityTestMap[Index]; + if (pHostQueryVisibilityTest == nullptr) { + return E_OUTOFMEMORY; + } + + // In order to prevent an endless loop if the D3D device becomes lost, we pass + // the D3DGETDATA_FLUSH flag. This tells GetData to return D3DERR_DEVICELOST if + // such a situation occurs, and break out of the loop as a result. + // Note: By Cxbx's design, we cannot do drawing within this while loop in order + // to further prevent any other endless loop situations. + while (S_FALSE == pHostQueryVisibilityTest->GetData(pResult, sizeof(DWORD), D3DGETDATA_FLUSH)); + + g_HostVisibilityTestMap[Index] = nullptr; + pHostQueryVisibilityTest->Release(); + } else { + // Fallback to old faked result when there's no host occlusion query : + if (pResult != xbnullptr) { + *pResult = 640 * 480; // TODO : Use actual backbuffer dimensions + } + } + + if (pTimeStamp != xbnullptr) { + LOG_TEST_CASE("requested value for pTimeStamp"); + *pTimeStamp = sizeof(DWORD); // TODO : This should be an incrementing GPU-memory based DWORD-aligned memory address + } + + return D3D_OK; +} + +// LTCG specific D3DDevice_LoadVertexShader function... +// This uses a custom calling convention where parameter is passed in EAX, ECX +// Test-case: Aggressive Inline +VOID __stdcall XTL::EMUPATCH(D3DDevice_LoadVertexShader_0) +( +) +{ + DWORD Handle; + DWORD Address; + + __asm { + mov Address, eax + mov Handle, ecx + } + + return EMUPATCH(D3DDevice_LoadVertexShader)(Handle, Address); +} + +// This uses a custom calling convention where parameter is passed in EAX +// Test-case: Ninja Gaiden +VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShader_4) +( + DWORD Address +) +{ + DWORD Handle; + __asm mov Handle, eax; + + LOG_FORWARD("D3DDevice_LoadVertexShader"); + return EMUPATCH(D3DDevice_LoadVertexShader)(Handle, Address); +} + +// ****************************************************************** +// * patch: D3DDevice_LoadVertexShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShader) +( + DWORD Handle, + DWORD Address +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(Address) + LOG_FUNC_END; + + CxbxImpl_LoadVertexShader(Handle, Address); +} + +// LTCG specific D3DDevice_SelectVertexShader function... +// This uses a custom calling convention where parameter is passed in EAX, EBX +// Test-case: Star Wars - Battlefront +VOID __stdcall XTL::EMUPATCH(D3DDevice_SelectVertexShader_0) +( +) +{ + DWORD Handle; + DWORD Address; + + __asm { + mov Handle, eax + mov Address, ebx + } + + return EMUPATCH(D3DDevice_SelectVertexShader)(Handle, Address); +} + +// LTCG specific D3DDevice_SelectVertexShader function... +// This uses a custom calling convention where parameter is passed in EAX +// Test-case: Aggressive Inline +VOID __stdcall XTL::EMUPATCH(D3DDevice_SelectVertexShader_4) +( + DWORD Address +) +{ + DWORD Handle; + __asm mov Handle, eax; + + return EMUPATCH(D3DDevice_SelectVertexShader)(Handle, Address); +} + +// ****************************************************************** +// * patch: D3DDevice_SelectVertexShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SelectVertexShader) +( + DWORD Handle, + DWORD Address +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(Address) + LOG_FUNC_END; + + CxbxImpl_SelectVertexShader(Handle, Address); +} + +// ****************************************************************** +// * patch: D3DDevice_SetGammaRamp +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetGammaRamp) +( + DWORD dwFlags, + CONST X_D3DGAMMARAMP *pRamp +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(dwFlags) + LOG_FUNC_ARG(pRamp) + LOG_FUNC_END; + + // remove D3DSGR_IMMEDIATE + DWORD dwPCFlags = dwFlags & (~0x00000002); + D3DGAMMARAMP PCRamp; + + for(int v=0;v<255;v++) + { + PCRamp.red[v] = pRamp->red[v]; + PCRamp.green[v] = pRamp->green[v]; + PCRamp.blue[v] = pRamp->blue[v]; + } + +#if 0 // TODO : Why is this disabled? + g_pD3DDevice->SetGammaRamp( + 0, // iSwapChain + dwPCFlags, &PCRamp); +#endif +} + +// ****************************************************************** +// * patch: D3DDevice_GetGammaRamp +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetGammaRamp) +( + X_D3DGAMMARAMP *pRamp +) +{ + LOG_FUNC_ONE_ARG(pRamp); + + D3DGAMMARAMP *pGammaRamp = (D3DGAMMARAMP *)malloc(sizeof(D3DGAMMARAMP)); + + g_pD3DDevice->GetGammaRamp( + 0, // iSwapChain + pGammaRamp); + + for(int v=0;v<256;v++) + { + pRamp->red[v] = (BYTE)pGammaRamp->red[v]; + pRamp->green[v] = (BYTE)pGammaRamp->green[v]; + pRamp->blue[v] = (BYTE)pGammaRamp->blue[v]; + } + + free(pGammaRamp); +} + +// ****************************************************************** +// * patch: D3DDevice_GetBackBuffer2 +// ****************************************************************** +#define COPY_BACKBUFFER_TO_XBOX_SURFACE // Uncomment to enable writing Host Backbuffers back to Xbox surfaces +XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2) +( + INT BackBuffer +) +{ + LOG_FUNC_ONE_ARG(BackBuffer); + + X_D3DSurface* pXboxBackBuffer = nullptr; + + +#ifndef COPY_BACKBUFFER_TO_XBOX_SURFACE + /** unsafe, somehow + HRESULT hRet = D3D_OK; + + X_D3DSurface *pXboxBackBuffer = EmuNewD3DSurface(); + + if(BackBuffer == -1) { + static IDirect3DSurface *pCachedPrimarySurface = nullptr; + + if(pCachedPrimarySurface == nullptr) { + // create a buffer to return + // TODO: Verify the surface is always 640x480 + hRet = g_pD3DDevice->CreateOffscreenPlainSurface(640, 480, D3DFMT_A8R8G8B8, /*D3DPool=* /0, &pCachedPrimarySurface, nullptr); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateOffscreenPlainSurface"); + } + + SetHostSurface(pXboxBackBuffer, pCachedPrimarySurface); // No iTextureStage! + + hRet = g_pD3DDevice->GetFrontBuffer(pCachedPrimarySurface); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetFrontBuffer"); + + if (FAILED(hRet)) { + EmuLog(LOG_LEVEL::WARNING, "Could not retrieve primary surface, using backbuffer"); + SetHostSurface(pXboxBackBuffer, nullptr); // No iTextureStage! + pCachedPrimarySurface->Release(); + pCachedPrimarySurface = nullptr; + BackBuffer = 0; + } + + // Debug: Save this image temporarily + //D3DXSaveSurfaceToFile("C:\\Aaron\\Textures\\FrontBuffer.bmp", D3DXIFF_BMP, GetHostSurface(pXboxBackBuffer), nullptr, nullptr); + } + + if(BackBuffer != -1) { + hRet = g_pD3DDevice->GetBackBuffer( + 0, // iSwapChain + BackBuffer, D3DBACKBUFFER_TYPE_MONO, &pCachedPrimarySurface); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer"); + } + //*/ + + static X_D3DSurface *pXboxBackBuffer = EmuNewD3DSurface(); + IDirect3DSurface *pCurrentHostBackBuffer = nullptr; + + STATUS_SUCCESS; + + if (BackBuffer == -1) { + BackBuffer = 0; + } + + HRESULT hRet = g_pD3DDevice->GetBackBuffer( + 0, // iSwapChain + BackBuffer, D3DBACKBUFFER_TYPE_MONO, &pCurrentHostBackBuffer); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer"); + + if (FAILED(hRet)) + CxbxKrnlCleanup("Unable to retrieve back buffer"); + + SetHostSurface(pXboxBackBuffer, pCurrentHostBackBuffer); // No iTextureStage! + + // Increment reference count + pXboxBackBuffer->Common++; // EMUPATCH(D3DResource_AddRef)(pXboxBackBuffer); + + return pXboxBackBuffer; +#else // COPY_BACKBUFFER_TO_XBOX_SURFACE + // Rather than create a new surface, we should forward to the Xbox version of GetBackBuffer, + // This gives us the correct Xbox surface to update. + // We get signatures for both backbuffer functions as it changed in later XDKs + + // This also updates the reference count, so we don't need to do this ourselves + if (XB_TRMP(D3DDevice_GetBackBuffer) != nullptr) { + XB_TRMP(D3DDevice_GetBackBuffer)(BackBuffer, D3DBACKBUFFER_TYPE_MONO, &pXboxBackBuffer); + } + else { + pXboxBackBuffer = XB_TRMP(D3DDevice_GetBackBuffer2)(BackBuffer); + } + + // Now pXboxBackbuffer points to the requested Xbox backbuffer + if (pXboxBackBuffer == nullptr) { + CxbxKrnlCleanup("D3DDevice_GetBackBuffer2: Could not get Xbox backbuffer"); + } + + + // HACK: Disabled: Enabling this breaks DOA3 at native res/without hacks+ + // Also likely to effect Other games, but it has no known benefit at this point in time + // There are currently no known games that depend on backbuffer readback on the CPU! +#if 0 + // TODO: Downscale the host surface to the same size as the Xbox surface during copy + // Otherwise, we will overflow memory and crash + // HACK: For now, when using a non-zero scale factor, we can just skip the copy to prevent a crash + if (g_RenderScaleFactor == 1) { + auto pCopySrcSurface = GetHostSurface(pXboxBackBuffer, D3DUSAGE_RENDERTARGET); + if (pCopySrcSurface == nullptr) { + EmuLog(LOG_LEVEL::WARNING, "Failed to get Host Resource for Xbox Back Buffer"); + return pXboxBackBuffer; + } + + D3DLOCKED_RECT copyLockedRect; + HRESULT hRet = pCopySrcSurface->LockRect(©LockedRect, NULL, D3DLOCK_READONLY); + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Could not lock Host Resource for Xbox Back Buffer"); + return pXboxBackBuffer; + } + + D3DSURFACE_DESC copySurfaceDesc; + hRet = pCopySrcSurface->GetDesc(©SurfaceDesc); + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Could not get Xbox Back Buffer Host Surface Desc"); + } + else { + DWORD Size = copyLockedRect.Pitch * copySurfaceDesc.Height; // TODO : What about mipmap levels? (Backbuffer does not support mipmap) + // Finally, do the copy from the converted host resource to the xbox resource + memcpy((void*)GetDataFromXboxResource(pXboxBackBuffer), copyLockedRect.pBits, Size); + } + + pCopySrcSurface->UnlockRect(); + } +#endif + + return pXboxBackBuffer; +#endif // COPY_BACKBUFFER_TO_XBOX_SURFACE +} + +// ****************************************************************** +// * patch: D3DDevice_GetBackBuffer +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer) +( + INT BackBuffer, + D3DBACKBUFFER_TYPE Type, + X_D3DSurface **ppBackBuffer +) +{ + LOG_FORWARD("D3DDevice_GetBackBuffer2"); + + *ppBackBuffer = EMUPATCH(D3DDevice_GetBackBuffer2)(BackBuffer); +} + +bool GetHostRenderTargetDimensions(DWORD *pHostWidth, DWORD *pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr) +{ + bool shouldRelease = false; + if (pHostRenderTarget == nullptr) { + g_pD3DDevice->GetRenderTarget( + 0, // RenderTargetIndex + &pHostRenderTarget); + + shouldRelease = true; + } + + // The following can only work if we could retrieve a host render target + if (!pHostRenderTarget) { + return false; + } + + // Get current host render target dimensions + D3DSURFACE_DESC HostRenderTarget_Desc; + pHostRenderTarget->GetDesc(&HostRenderTarget_Desc); + + if (shouldRelease) { + pHostRenderTarget->Release(); + } + + *pHostWidth = HostRenderTarget_Desc.Width; + *pHostHeight = HostRenderTarget_Desc.Height; + + return true; +} + +DWORD ScaleDWORD(DWORD Value, DWORD FromMax, DWORD ToMax) +{ + uint64_t tmp = Value; + tmp *= ToMax; + tmp /= FromMax; + return (DWORD)tmp; +} + +void ValidateRenderTargetDimensions(DWORD HostRenderTarget_Width, DWORD HostRenderTarget_Height, DWORD XboxRenderTarget_Width, DWORD XboxRenderTarget_Height) +{ + // This operation is often used to change the display resolution without calling SetRenderTarget! + // This works by updating the underlying Width & Height of the Xbox surface, without reallocating the data + // Because of this, we need to validate that the associated host resource still matches the dimensions of the Xbox Render Target + // If not, we must force them to be re-created + // TEST CASE: Chihiro Factory Test Program + DWORD XboxRenderTarget_Width_Scaled = XboxRenderTarget_Width * g_RenderScaleFactor; + DWORD XboxRenderTarget_Height_Scaled = XboxRenderTarget_Height * g_RenderScaleFactor; + if (HostRenderTarget_Width != XboxRenderTarget_Width_Scaled || HostRenderTarget_Height != XboxRenderTarget_Height_Scaled) { + LOG_TEST_CASE("Existing RenderTarget width/height changed"); + + if (g_pXbox_RenderTarget == g_pXbox_BackBufferSurface) { + FreeHostResource(GetHostResourceKey(g_pXbox_RenderTarget)); g_pD3DDevice->SetRenderTarget(0, GetHostSurface(g_pXbox_RenderTarget, D3DUSAGE_RENDERTARGET)); + FreeHostResource(GetHostResourceKey(g_pXbox_DepthStencil)); g_pD3DDevice->SetDepthStencilSurface(GetHostSurface(g_pXbox_DepthStencil, D3DUSAGE_DEPTHSTENCIL)); + } + } +} + +float GetZScaleForSurface(XTL::X_D3DSurface* pSurface) +{ + // If no surface was present, fallback to 1 + if (pSurface == xbnullptr) { + return 1; + } + + auto format = GetXboxPixelContainerFormat(pSurface); + switch (format) { + case XTL::X_D3DFMT_D16: + case XTL::X_D3DFMT_LIN_D16: + return 65535.0f; + + case XTL::X_D3DFMT_D24S8: + case XTL::X_D3DFMT_LIN_D24S8: + return 16777215.0f; + + case XTL::X_D3DFMT_F16: + case XTL::X_D3DFMT_LIN_F16: + return 511.9375f; + + case XTL::X_D3DFMT_F24S8: + case XTL::X_D3DFMT_LIN_F24S8: + // 24bit floating point is close to precision maximum, so a lower value is used + // We can't use a double here since the vertex shader is only at float precision + return 1.0e30f; + } + + // Default to 1 if unknown depth format + LOG_TEST_CASE("GetZScaleForSurface: Unknown Xbox Depth Format"); + return 1; +} + +void GetViewPortOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) +{ + // Store viewport offset and scale in constant registers + // used in shaders to transform back from screen space (Xbox Shader Output) to Clip space (Host Shader Output) + D3DVIEWPORT ViewPort; + g_pD3DDevice->GetViewport(&ViewPort); + + // NOTE: Due to how our GPU emulation works, we need to account for MSAA here, by adjusting the ViewPort dimensions + // This fixes the 'offset' models in GTA3 + float xScale, yScale; + float xOffset, yOffset; + GetMultiSampleOffsetAndScale(xScale, yScale, xOffset, yOffset); + // Since Width and Height are DWORD, subtracting MultiSampleOffset 0.0f or 0.5f makes no sense + //ViewPort.Width -= xOffset; + //ViewPort.Height -= yOffset; + ViewPort.Width /= (DWORD)xScale; + ViewPort.Height /= (DWORD)yScale; + + // Calculate Width/Height scale & offset + float scaleWidth = (2.0f / ViewPort.Width) * g_RenderScaleFactor; + float scaleHeight = (2.0f / ViewPort.Height) * g_RenderScaleFactor; + float offsetWidth = scaleWidth; + float offsetHeight = scaleHeight; + + // Calculate Z scale & offset + float zScale = GetZScaleForSurface(g_pXbox_DepthStencil); + float scaleZ = zScale * (ViewPort.MaxZ - ViewPort.MinZ); + float offsetZ = zScale * ViewPort.MinZ; + + // TODO will we need to do something here to support upscaling? + // TODO remove the code above as required + + // Reset to default scale (as we accounted for MSAA scale above) + // But don't reset the offset + xScale = 1.0f; + yScale = 1.0f; + + // Xbox correct values? + xOffset = xOffset + (1.0f / 32.0f); + yOffset = yOffset + (1.0f / 32.0f); + xScale = xScale * ViewPort.Width; + yScale = yScale * ViewPort.Height; + + // HACK: Add a host correction factor to these values + // So that after we reverse the screenspace transformation + // Pre-transformed 2d geometry is in the same space as the 3d geometry...? + + // Offset with a host correction + vOffset[0] = xOffset + (0.5f * (float)ViewPort.Width / (float)g_RenderScaleFactor); + vOffset[1] = yOffset + (0.5f * (float)ViewPort.Height / (float)g_RenderScaleFactor); + vOffset[2] = 0.0f; //offsetZ; + vOffset[3] = 0.0f; + + // Scale with a host correction + vScale[0] = xScale * (1.0f / ( 2.0f * (float)g_RenderScaleFactor)); + vScale[1] = yScale * (1.0f / (-2.0f * (float)g_RenderScaleFactor)); + vScale[2] = scaleZ; // ? + vScale[3] = 1.0f; // ? +} + +void UpdateViewPortOffsetAndScaleConstants() +{ + float vOffset[4], vScale[4]; + GetViewPortOffsetAndScale(vOffset, vScale); + + g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_VIEWPORT_SCALE_MIRROR, vScale, 1); + g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR, vOffset, 1); + + // Store viewport offset and scale in constant registers 58 (c-38) and + // 59 (c-37) used for screen space transformation. + // We only do this if X_D3DSCM_NORESERVEDCONSTANTS is not set, since enabling this flag frees up these registers for shader used + // Treat this as a flag + // Test Case: GTA III, Soldier of Fortune II + if (!(g_Xbox_VertexShaderConstantMode & X_D3DSCM_NORESERVEDCONSTANTS)) { + g_pD3DDevice->SetVertexShaderConstantF(X_D3DSCM_RESERVED_CONSTANT_SCALE + X_D3DSCM_CORRECTION, vScale, 1); + g_pD3DDevice->SetVertexShaderConstantF(X_D3DSCM_RESERVED_CONSTANT_OFFSET + X_D3DSCM_CORRECTION, vOffset, 1); + } +} + +// ****************************************************************** +// * patch: D3DDevice_SetViewport +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetViewport) +( + CONST X_D3DVIEWPORT8 *pViewport +) +{ + LOG_FUNC_ONE_ARG(pViewport); + + // Always call the Xbox SetViewPort to update D3D Internal State + XB_TRMP(D3DDevice_SetViewport)(pViewport); + + // Host does not support pViewPort = nullptr + if (pViewport == nullptr) { + LOG_TEST_CASE("pViewport = null"); + return; + } + + D3DVIEWPORT XboxViewPort = *pViewport; + D3DVIEWPORT HostViewPort = *pViewport; + + if (g_pXbox_RenderTarget) { + // Clip the Xbox Viewport to the render target dimensions + // This is required because during SetRenderTarget, Xbox calls SetViewPort with impossibly large values + DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXbox_RenderTarget); + DWORD XboxRenderTarget_Height = GetPixelContainerHeight(g_pXbox_RenderTarget); + + DWORD left = std::max((int)pViewport->X, 0); + DWORD top = std::max((int)pViewport->Y, 0); + DWORD right = std::min((int)pViewport->X + (int)pViewport->Width, (int)XboxRenderTarget_Width); + DWORD bottom = std::min((int)pViewport->Y + (int)pViewport->Height, (int)XboxRenderTarget_Height); + DWORD width = right - left; + DWORD height = bottom - top; + + XboxViewPort.X = left; + XboxViewPort.Y = top; + XboxViewPort.Width = width; + XboxViewPort.Height = height; + XboxViewPort.MinZ = pViewport->MinZ; + XboxViewPort.MaxZ = pViewport->MaxZ; + + + // Store the updated viewport data ready to pass to host SetViewPort + HostViewPort = XboxViewPort; + + DWORD HostRenderTarget_Width = 0, HostRenderTarget_Height = 0; + if (GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) { + ValidateRenderTargetDimensions(HostRenderTarget_Width, HostRenderTarget_Height, XboxRenderTarget_Width, XboxRenderTarget_Height); + + // We *must* always scale the viewport to the associated host surface + // Otherwise, we only get partial screen updates, or broken upscaling + // Scale Xbox to host dimensions (avoiding hard-coding 640 x 480) + HostViewPort.X = ScaleDWORD(XboxViewPort.X, XboxRenderTarget_Width, HostRenderTarget_Width); + HostViewPort.Y = ScaleDWORD(XboxViewPort.Y, XboxRenderTarget_Height, HostRenderTarget_Height); + HostViewPort.Width = ScaleDWORD(XboxViewPort.Width, XboxRenderTarget_Width, HostRenderTarget_Width); + HostViewPort.Height = ScaleDWORD(XboxViewPort.Height, XboxRenderTarget_Height, HostRenderTarget_Height); + // TODO : Fix test-case Shenmue 2 (which halves height, leaving the bottom half unused) + HostViewPort.MinZ = XboxViewPort.MinZ; // No need scale Z for now + HostViewPort.MaxZ = XboxViewPort.MaxZ; + } else { + LOG_TEST_CASE("SetViewPort: Unable to fetch host render target dimensions"); + } + } + + // Apply MSAA scale and offset + float xScale, yScale; + GetMultiSampleScale(xScale, yScale); + HostViewPort.Width *= (DWORD)xScale; + HostViewPort.Height *= (DWORD)yScale; + // Since Width and Height are DWORD, adding GetMultiSampleOffset 0.0f or 0.5f makes no sense + + HRESULT hRet = g_pD3DDevice->SetViewport(&HostViewPort); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetViewport"); + + UpdateViewPortOffsetAndScaleConstants(); +} + +// LTCG specific D3DDevice_SetShaderConstantMode function... +// This uses a custom calling convention where parameter is passed in EAX +VOID __stdcall XTL::EMUPATCH(D3DDevice_SetShaderConstantMode_0) +( +) +{ + XTL::X_VERTEXSHADERCONSTANTMODE param; + __asm { + mov param, eax; + } + + return EMUPATCH(D3DDevice_SetShaderConstantMode)(param); +} + +// ****************************************************************** +// * patch: D3DDevice_SetShaderConstantMode +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetShaderConstantMode) +( + XTL::X_VERTEXSHADERCONSTANTMODE Mode +) +{ + LOG_FUNC_ONE_ARG(Mode); + + g_Xbox_VertexShaderConstantMode = Mode; +} + +// ****************************************************************** +// * patch: D3DDevice_CreateVertexShader +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_CreateVertexShader) +( + CONST DWORD *pDeclaration, + CONST DWORD *pFunction, + DWORD *pHandle, + DWORD Usage +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pDeclaration) + LOG_FUNC_ARG(pFunction) + LOG_FUNC_ARG(pHandle) + LOG_FUNC_ARG_TYPE(X_D3DUSAGE, Usage) + LOG_FUNC_END; + + // First, we must call the Xbox CreateVertexShader function and check for success + // This does the following: + // Allocates an Xbox VertexShader struct + // Sets reference count to 1 + // Puts Usage in VertexShader->Flags + // If pFunction is not null, it points to DWORDS shader type, length and a binary compiled xbox vertex shader + // If pDeclaration is not null, it's parsed, resulting in a number of constants + // Parse results are pushed to the push buffer + // Sets other fields + // pHandle recieves the addres of the new shader, or-ed with 1 (D3DFVF_RESERVED0) + + HRESULT hRet = D3D_OK; + + if (XB_TRMP(D3DDevice_CreateVertexShader)) { + HRESULT hRet = XB_TRMP(D3DDevice_CreateVertexShader)(pDeclaration, pFunction, pHandle, Usage); + if (FAILED(hRet)) { + LOG_TEST_CASE("D3DDevice_CreateVertexShader trampoline call returned failure"); + RETURN(hRet); + } + } else { + // Due to how our LoadVertexShader patch is implemented, it may call this function without the Xbox version existing + // As a result, we have to build our own vertex shader handle if the trampoline was not found + // We don't do the full steps listed above intentionally so: If this situation is reached, the game + // does not have a CreateVertexShader function, so those actions should not happen anyway! + LOG_TEST_CASE("CreateVertexShader with no trampoline"); + *pHandle = ((DWORD)malloc(sizeof(X_D3DVertexShader)) & D3DFVF_RESERVED0); + } + + return CxbxImpl_CreateVertexShader(pDeclaration, pFunction, pHandle, Usage); +} + +// LTCG specific D3DDevice_SetVertexShaderConstant function... +// This uses a custom calling convention where parameter is passed in EDX +// Test-case: Murakumo +VOID __stdcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant_8) +( +) +{ + static uint32_t returnAddr; + +#ifdef _DEBUG_TRACE + __asm add esp, 4 +#endif + + __asm { + pop returnAddr + push edx + call EmuPatch_D3DDevice_SetVertexShaderConstant + mov eax, 0 + push returnAddr + ret + } +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderConstant +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant) +( + INT Register, + CONST PVOID pConstantData, + DWORD ConstantCount +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Register) + LOG_FUNC_ARG(pConstantData) + LOG_FUNC_ARG(ConstantCount) + LOG_FUNC_END; + + CxbxImpl_SetVertexShaderConstant(Register, pConstantData, ConstantCount); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderConstant1 +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant1) +( + INT Register, + CONST PVOID pConstantData +) +{ + LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); + + // The XDK uses a macro to automatically adjust to 0..191 range + // but D3DDevice_SetVertexShaderConstant expects -96..95 range + // so we adjust before forwarding + EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, 1); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderConstant1Fast +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant1Fast) +( + INT Register, + CONST PVOID pConstantData +) +{ + LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); + + // The XDK uses a macro to automatically adjust to 0..191 range + // but D3DDevice_SetVertexShaderConstant expects -96..95 range + // so we adjust before forwarding + EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, 1); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderConstant4 +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstant4) +( + INT Register, + CONST PVOID pConstantData +) +{ + LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); + + // The XDK uses a macro to automatically adjust to 0..191 range + // but D3DDevice_SetVertexShaderConstant expects -96..95 range + // so we adjust before forwarding + EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, 4); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderConstantNotInline +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline) +( + INT Register, + CONST PVOID pConstantData, + DWORD ConstantCount +) +{ + LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); + + // The XDK uses a macro to automatically adjust to 0..191 range + // but D3DDevice_SetVertexShaderConstant expects -96..95 range + // so we adjust before forwarding + EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, ConstantCount / 4); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderConstantNotInlineFast +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInlineFast) +( + INT Register, + CONST PVOID pConstantData, + DWORD ConstantCount +) +{ + LOG_FORWARD("D3DDevice_SetVertexShaderConstant"); + + // The XDK uses a macro to automatically adjust to 0..191 range + // but D3DDevice_SetVertexShaderConstant expects -96..95 range + // so we adjust before forwarding + EMUPATCH(D3DDevice_SetVertexShaderConstant)(Register - X_D3DSCM_CORRECTION, pConstantData, ConstantCount / 4); +} + +// LTCG specific D3DDevice_SetTexture function... +// This uses a custom calling convention where parameter is passed in EAX +// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX. +// Test-case: Metal Wolf Chaos +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetTexture_4) +( + X_D3DBaseTexture *pTexture +) +{ + DWORD Stage; + __asm mov Stage, eax; + + //LOG_FUNC_BEGIN + // LOG_FUNC_ARG(Stage) + // LOG_FUNC_ARG(pTexture) + // LOG_FUNC_END; + EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetTexture_4(Stage : %d pTexture : %08x);", Stage, pTexture); + + // Call the Xbox implementation of this function, to properly handle reference counting for us + //XB_TRMP(D3DDevice_SetTexture_4)(pTexture); + + g_pXbox_SetTexture[Stage] = pTexture; +} + +// ****************************************************************** +// * patch: D3DDevice_SetTexture +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetTexture) +( + DWORD Stage, + X_D3DBaseTexture *pTexture +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Stage) + LOG_FUNC_ARG(pTexture) + LOG_FUNC_END; + + // Call the Xbox implementation of this function, to properly handle reference counting for us + XB_TRMP(D3DDevice_SetTexture)(Stage, pTexture); + + g_pXbox_SetTexture[Stage] = pTexture; +} + +// ****************************************************************** +// * patch: D3DDevice_SwitchTexture +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SwitchTexture) +( + DWORD Method, + DWORD Data, + DWORD Format +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Method) + LOG_FUNC_ARG(Data) + LOG_FUNC_ARG(Format) + LOG_FUNC_END; + + DWORD StageLookup[XTL::X_D3DTS_STAGECOUNT] = { 0x00081b00, 0x00081b40, 0x00081b80, 0x00081bc0 }; + // This array contains D3DPUSH_ENCODE(NV2A_TX_OFFSET(v), 2) = 2 DWORD's, shifted left PUSH_COUNT_SHIFT (18) left + DWORD Stage = -1; + + for (int v = 0; v < XTL::X_D3DTS_STAGECOUNT; v++) { + if (StageLookup[v] == Method) { + Stage = v; + break; + } + } + + if (Stage == -1) { + LOG_TEST_CASE("D3DDevice_SwitchTexture Unknown Method"); + EmuLog(LOG_LEVEL::WARNING, "Unknown Method (0x%.08X)", Method); + } + else { + // Switch Texture updates the data pointer of an active texture using pushbuffer commands + if (g_pXbox_SetTexture[Stage] == xbnullptr) { + LOG_TEST_CASE("D3DDevice_SwitchTexture without an active texture"); + } + else { + //LOG_TEST_CASE("Using CxbxActiveTextureCopies"); + // See https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/1159 + // Test-case : Arena Football + // Test-case : Call of Duty 2: Big Red One + // Test-case : Crimson Skies + // Test-case : Freedom Fighters - see https://www.youtube.com/watch?v=_NDCoLY8V3I + // Test-case : Freestyle MetalX + // Test-case : GENMA ONIMUSHA + // Test-case : Gun + // Test-case : Harry Potter : Quidditch World Cup + // Test-case : King Arthur + // Test-case : Madden NFL 2002 + // Test-case : Madden NFL 2005 + // Test-case : Madden NFL 07 + // Test-case : Need For Speed Most Wanted + // Test-case : Need For Speed Underground + // Test-case : PocketBike Racer + // Test-case : Project Gotham Racing 2 + // Test-case : Richard Burns Rally + // Test-case : Spider - Man 2 + + // Update data and format separately, instead of via GetDataFromXboxResource() + CxbxActiveTextureCopies[Stage].Common = g_pXbox_SetTexture[Stage]->Common; + CxbxActiveTextureCopies[Stage].Data = Data; + CxbxActiveTextureCopies[Stage].Format = Format; + CxbxActiveTextureCopies[Stage].Lock = 0; + CxbxActiveTextureCopies[Stage].Size = g_pXbox_SetTexture[Stage]->Size; + + // Use the above modified copy, instead of altering the active Xbox texture + g_pXbox_SetTexture[Stage] = &CxbxActiveTextureCopies[Stage]; + // Note : Since g_pXbox_SetTexture and CxbxActiveTextureCopies are host-managed, + // Xbox code should never alter these members (so : no reference counting, etc). + // As long as that's guaranteed, this is a safe way to emulate SwitchTexture. + // (GetHostResourceKey also avoids using any Xbox texture resource memory address.) + } + } +} + +// ****************************************************************** +// * patch: D3DDevice_Begin +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_Begin) +( + X_D3DPRIMITIVETYPE PrimitiveType +) +{ + LOG_FUNC_ONE_ARG(PrimitiveType); + + g_InlineVertexBuffer_PrimitiveType = PrimitiveType; + g_InlineVertexBuffer_TableOffset = 0; + g_InlineVertexBuffer_FVF = 0; +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexData2f +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData2f) +( + int Register, + FLOAT a, + FLOAT b +) +{ + LOG_FORWARD("D3DDevice_SetVertexData4f"); + + EMUPATCH(D3DDevice_SetVertexData4f)(Register, a, b, 0.0f, 1.0f); +} + +static inline DWORD FtoDW(FLOAT f) { return *((DWORD*)&f); } +static inline FLOAT DWtoF(DWORD f) { return *((FLOAT*)&f); } + +// ****************************************************************** +// * patch: D3DDevice_SetVertexData2s +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData2s) +( + int Register, + SHORT a, + SHORT b +) +{ + LOG_FORWARD("D3DDevice_SetVertexData4f"); + + float fa, fb; + + // Test case: Halo + // Note : XQEMU verified that the int16_t arguments + // must be mapped to floats in the range [-32768.0, 32767.0] + // (See https://github.com/xqemu/xqemu/pull/176) + fa = (float)a; + fb = (float)b; + + EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, 0.0f, 1.0f); +} + +extern uint32_t HLE_read_NV2A_pgraph_register(const int reg); // Declared in PushBuffer.cpp +extern void HLE_write_NV2A_vertex_attribute_slot(unsigned slot, uint32_t parameter); // Declared in PushBuffer.cpp +extern uint32_t HLE_read_NV2A_vertex_attribute_slot(unsigned VertexSlot); // Declared in PushBuffer.cpp + +extern NV2ADevice* g_NV2A; + +// ****************************************************************** +// * patch: D3DDevice_SetVertexData4f_16 +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4f_16) +( + FLOAT a, + FLOAT b, + FLOAT c, + FLOAT d +) +{ + // This is an LTCG specific version of SetVertexData4f where the first param is passed in edi + int Register = 0; + __asm{ + mov Register, edi + } + + LOG_FORWARD("D3DDevice_SetVertexData4f"); + + EMUPATCH(D3DDevice_SetVertexData4f)(Register, a, b, c, d); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexData4f +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4f) +( + int Register, + FLOAT a, + FLOAT b, + FLOAT c, + FLOAT d +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Register) + LOG_FUNC_ARG(a) + LOG_FUNC_ARG(b) + LOG_FUNC_ARG(c) + LOG_FUNC_ARG(d) + LOG_FUNC_END; + + HRESULT hRet = D3D_OK; + + // Get the vertex shader flags (if any is active) : + uint32_t ActiveVertexAttributeFlags = 0; + if (VshHandleIsVertexShader(g_Xbox_VertexShader_Handle)) { + LOG_TEST_CASE("D3DDevice_SetVertexData4f with active VertexShader"); + X_D3DVertexShader *pXboxVertexShader = VshHandleToXboxVertexShader(g_Xbox_VertexShader_Handle); + if (!(pXboxVertexShader->Flags & 0x10/*=X_VERTEXSHADER_PROGRAM*/)) { + ActiveVertexAttributeFlags = pXboxVertexShader->Flags; + } + + // If we have an active vertex shader, we also write the input to a vertex shader constant + // This allows us to implement Xbox functionality where SetVertexData4f can be used to specify attributes + // not present in the vertex declaration. + // We use range 193 and up to store these values, as Xbox shaders stop at c192! + FLOAT values[] = {a,b,c,d}; + if (Register < 0) LOG_TEST_CASE("Register < 0"); + if (Register >= 16) LOG_TEST_CASE("Register >= 16"); + g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + Register, values, 1); + } + + // Grow g_InlineVertexBuffer_Table to contain at least current, and a potentially next vertex + if (g_InlineVertexBuffer_TableLength <= g_InlineVertexBuffer_TableOffset + 1) { + if (g_InlineVertexBuffer_TableLength == 0) { + g_InlineVertexBuffer_TableLength = PAGE_SIZE / sizeof(struct _D3DIVB); + } else { + g_InlineVertexBuffer_TableLength *= 2; + } + + g_InlineVertexBuffer_Table = (struct _D3DIVB*)realloc(g_InlineVertexBuffer_Table, sizeof(struct _D3DIVB) * g_InlineVertexBuffer_TableLength); + EmuLog(LOG_LEVEL::DEBUG, "Reallocated g_InlineVertexBuffer_Table to %d entries", g_InlineVertexBuffer_TableLength); + } + + // Is this the initial call after D3DDevice_Begin() ? + if (g_InlineVertexBuffer_FVF == 0) { + // Set first vertex to zero (preventing leaks from prior Begin/End calls) + g_InlineVertexBuffer_Table[0] = {}; + + // Handle persistent vertex attribute flags, by resetting non-persistent colors + // to their default value (and leaving the persistent colors alone - see the + // "Copy all attributes of the previous vertex" comment below) : + static const uint32_t ColorBlack = D3DCOLOR_ARGB(0, 0, 0, 0); + static const uint32_t ColorWhite = D3DCOLOR_ARGB(255, 255, 255, 255); + + // If needed, write default vertex colors to HLE NV2A pgraph : + if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTDIFFUSE)) { + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_DIFFUSE, ColorWhite); + } + + if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTSPECULAR)) { + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_SPECULAR, ColorBlack); + } + + if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTBACKDIFFUSE)) { + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKDIFFUSE, ColorWhite); + } + + if (!(ActiveVertexAttributeFlags & X_D3DUSAGE_PERSISTENTBACKSPECULAR)) { + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKSPECULAR, ColorBlack); + } + + // Read starting vertex colors from HLE NV2A pgraph : + g_InlineVertexBuffer_Table[0].Diffuse = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_DIFFUSE); + g_InlineVertexBuffer_Table[0].Specular = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_SPECULAR); + g_InlineVertexBuffer_Table[0].BackDiffuse = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKDIFFUSE); + g_InlineVertexBuffer_Table[0].BackSpecular = HLE_read_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKSPECULAR); + } + + int o = g_InlineVertexBuffer_TableOffset; + unsigned int FVFPosType = g_InlineVertexBuffer_FVF & D3DFVF_POSITION_MASK; + + switch(Register) + { + case X_D3DVSDE_VERTEX: + case X_D3DVSDE_POSITION: + { + // Note : Setting position signals completion of a vertex + g_InlineVertexBuffer_Table[o].Position.x = a; + g_InlineVertexBuffer_Table[o].Position.y = b; + g_InlineVertexBuffer_Table[o].Position.z = c; + g_InlineVertexBuffer_Table[o].Rhw = d; // Was : 1.0f; // Dxbx note : Why set Rhw to 1.0? And why ignore d? + + switch (g_InlineVertexBuffer_FVF & D3DFVF_POSITION_MASK) { + case 0: + // No position mask given yet, set it now : + if (g_InlineVertexBuffer_FVF & D3DFVF_NORMAL) { + // See https://msdn.microsoft.com/ru-ru/library/windows/desktop/bb172559(v=vs.85).aspx and DxbxFVFToVertexSizeInBytes + // D3DFVF_NORMAL cannot be combined with D3DFVF_XYZRHW : + g_InlineVertexBuffer_FVF |= D3DFVF_XYZ; + g_InlineVertexBuffer_Table[o].Rhw = 1.0f; // This, just to stay close to prior behaviour + } + else { + // Without D3DFVF_NORMAL, assume D3DFVF_XYZRHW + g_InlineVertexBuffer_FVF |= D3DFVF_XYZRHW; + } + break; + case D3DFVF_XYZ: + case D3DFVF_XYZRHW: + case D3DFVF_XYZB1: + // These are alright + break; + default: + EmuLog(LOG_LEVEL::WARNING, "D3DDevice_SetVertexData4f unexpected FVF when selecting D3DFVF_XYZ(RHW) : %x", g_InlineVertexBuffer_FVF); + // TODO : How to resolve this? + } + + // Start a new vertex + g_InlineVertexBuffer_TableOffset++; + // Copy all attributes of the previous vertex (if any) to the new vertex + g_InlineVertexBuffer_Table[g_InlineVertexBuffer_TableOffset] = g_InlineVertexBuffer_Table[o]; + + break; + } + + case X_D3DVSDE_BLENDWEIGHT: + { + g_InlineVertexBuffer_Table[o].Blend[0] = a; + g_InlineVertexBuffer_Table[o].Blend[1] = b; + g_InlineVertexBuffer_Table[o].Blend[2] = c; + g_InlineVertexBuffer_Table[o].Blend[3] = d; + // TODO: Test the above. + // Xbox supports up to 4 blendweights + + switch (g_InlineVertexBuffer_FVF & D3DFVF_POSITION_MASK) { + case 0: + // No position mask given yet, set it now : + g_InlineVertexBuffer_FVF |= D3DFVF_XYZB1; + // TODO: How to select blendweight D3DFVF_XYZB2 or up? + break; + case D3DFVF_XYZB1: + // These are alright + break; + default: + EmuLog(LOG_LEVEL::WARNING, "D3DDevice_SetVertexData4f unexpected FVF when processing X_D3DVSDE_BLENDWEIGHT : %x", g_InlineVertexBuffer_FVF); + g_InlineVertexBuffer_FVF &= ~D3DFVF_POSITION_MASK; // for now, remove prior position mask, leading to blending below + g_InlineVertexBuffer_FVF |= D3DFVF_XYZB1; + // TODO: How to select blendweight D3DFVF_XYZB2 or up? + // TODO : How to resolve this? + } + + break; + } + + case X_D3DVSDE_NORMAL: + { + g_InlineVertexBuffer_Table[o].Normal.x = a; + g_InlineVertexBuffer_Table[o].Normal.y = b; + g_InlineVertexBuffer_Table[o].Normal.z = c; + g_InlineVertexBuffer_FVF |= D3DFVF_NORMAL; + break; + } + + case X_D3DVSDE_DIFFUSE: + { + g_InlineVertexBuffer_Table[o].Diffuse = D3DCOLOR_COLORVALUE(a, b, c, d); + g_InlineVertexBuffer_FVF |= D3DFVF_DIFFUSE; + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_DIFFUSE, g_InlineVertexBuffer_Table[o].Diffuse); + break; + } + + case X_D3DVSDE_SPECULAR: + { + g_InlineVertexBuffer_Table[o].Specular = D3DCOLOR_COLORVALUE(a, b, c, d); + g_InlineVertexBuffer_FVF |= D3DFVF_SPECULAR; + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_SPECULAR, g_InlineVertexBuffer_Table[o].Specular); + break; + } + + case X_D3DVSDE_FOG: // Xbox extension + { + g_InlineVertexBuffer_Table[o].Fog = a; // TODO : What about the other (b, c and d) arguments? + //EmuLog(LOG_LEVEL::WARNING, "Host Direct3D8 doesn''t support FVF FOG"); + break; + } + + // Note : X_D3DVSDE_POINTSIZE: Maps to D3DFVF_PSIZE, which is not available on Xbox FVF's + + case X_D3DVSDE_BACKDIFFUSE: // Xbox extension + { + g_InlineVertexBuffer_Table[o].BackDiffuse = D3DCOLOR_COLORVALUE(a, b, c, d); + //EmuLog(LOG_LEVEL::WARNING, "Host Direct3D8 doesn''t support FVF BACKDIFFUSE"); + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKDIFFUSE, g_InlineVertexBuffer_Table[o].BackDiffuse); + break; + } + + case X_D3DVSDE_BACKSPECULAR: // Xbox extension + { + g_InlineVertexBuffer_Table[o].BackSpecular = D3DCOLOR_COLORVALUE(a, b, c, d); + //EmuLog(LOG_LEVEL::WARNING, "Host Direct3D8 doesn''t support FVF BACKSPECULAR"); + HLE_write_NV2A_vertex_attribute_slot(X_D3DVSDE_BACKSPECULAR, g_InlineVertexBuffer_Table[o].BackSpecular); + break; + } + + case X_D3DVSDE_TEXCOORD0: + { + g_InlineVertexBuffer_Table[o].TexCoord[0].x = a; + g_InlineVertexBuffer_Table[o].TexCoord[0].y = b; + g_InlineVertexBuffer_Table[o].TexCoord[0].z = c; + g_InlineVertexBuffer_Table[o].TexCoord[0].w = d; + if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX1) { + // Dxbx fix : Use mask, else the format might get expanded incorrectly : + g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; + g_InlineVertexBuffer_FVF |= D3DFVF_TEX1; + // Dxbx note : Correct usage of D3DFVF_TEX1 (and the other cases below) + // can be tested with "Daphne Xbox" (the Laserdisc Arcade Game Emulator). + } + + break; + } + + case X_D3DVSDE_TEXCOORD1: + { + g_InlineVertexBuffer_Table[o].TexCoord[1].x = a; + g_InlineVertexBuffer_Table[o].TexCoord[1].y = b; + g_InlineVertexBuffer_Table[o].TexCoord[1].z = c; + g_InlineVertexBuffer_Table[o].TexCoord[1].w = d; + if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX2) { + g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; + g_InlineVertexBuffer_FVF |= D3DFVF_TEX2; + } + + break; + } + + case X_D3DVSDE_TEXCOORD2: + { + g_InlineVertexBuffer_Table[o].TexCoord[2].x = a; + g_InlineVertexBuffer_Table[o].TexCoord[2].y = b; + g_InlineVertexBuffer_Table[o].TexCoord[2].z = c; + g_InlineVertexBuffer_Table[o].TexCoord[2].w = d; + if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX3) { + g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; + g_InlineVertexBuffer_FVF |= D3DFVF_TEX3; + } + + break; + } + + case X_D3DVSDE_TEXCOORD3: + { + g_InlineVertexBuffer_Table[o].TexCoord[3].x = a; + g_InlineVertexBuffer_Table[o].TexCoord[3].y = b; + g_InlineVertexBuffer_Table[o].TexCoord[3].z = c; + g_InlineVertexBuffer_Table[o].TexCoord[3].w = d; + if ((g_InlineVertexBuffer_FVF & D3DFVF_TEXCOUNT_MASK) < D3DFVF_TEX4) { + g_InlineVertexBuffer_FVF &= ~D3DFVF_TEXCOUNT_MASK; + g_InlineVertexBuffer_FVF |= D3DFVF_TEX4; + } + + break; + } + + default: + EmuLog(LOG_LEVEL::WARNING, "Unknown IVB Register : %d", Register); + } +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexData4ub +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4ub) +( + INT Register, + BYTE a, + BYTE b, + BYTE c, + BYTE d +) +{ + LOG_FORWARD("D3DDevice_SetVertexData4f"); + + float fa = a / 255.0f; + float fb = b / 255.0f; + float fc = c / 255.0f; + float fd = d / 255.0f; + + EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, fc, fd); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexData4s +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4s) +( + INT Register, + SHORT a, + SHORT b, + SHORT c, + SHORT d +) +{ + LOG_FORWARD("D3DDevice_SetVertexData4f"); + + float fa, fb, fc, fd; + + // Test case: Halo + // Note : XQEMU verified that the int16_t arguments + // must be mapped to floats in the range [-32768.0, 32767.0] + // (See https://github.com/xqemu/xqemu/pull/176) + fa = (float)a; + fb = (float)b; + fc = (float)c; + fd = (float)d; + + EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, fc, fd); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexDataColor +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexDataColor) +( + int Register, + D3DCOLOR Color +) +{ + LOG_FORWARD("D3DDevice_SetVertexData4f"); + + D3DXCOLOR XColor = Color; + + EMUPATCH(D3DDevice_SetVertexData4f)(Register, XColor.r, XColor.g, XColor.b, XColor.a); +} + +// ****************************************************************** +// * patch: D3DDevice_End +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_End)() +{ + LOG_FUNC(); + + if(g_InlineVertexBuffer_TableOffset > 0) + EmuFlushIVB(); + + // TODO: Should technically clean this up at some point..but on XP doesnt matter much +// g_VMManager.Deallocate((VAddr)g_InlineVertexBuffer_pData); +// g_VMManager.Deallocate((VAddr)g_InlineVertexBuffer_Table); +} + +// ****************************************************************** +// * patch: D3DDevice_RunPushBuffer +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_RunPushBuffer) +( + X_D3DPushBuffer *pPushBuffer, + X_D3DFixup *pFixup +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pPushBuffer) + LOG_FUNC_ARG(pFixup) + LOG_FUNC_END; + + EmuExecutePushBuffer(pPushBuffer, pFixup); +} + +// ****************************************************************** +// * patch: D3DDevice_Clear +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_Clear) +( + DWORD Count, + CONST D3DRECT *pRects, + DWORD Flags, + D3DCOLOR Color, + float Z, + DWORD Stencil +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Count) + LOG_FUNC_ARG(pRects) + LOG_FUNC_ARG(Flags) + LOG_FUNC_ARG(Color) + LOG_FUNC_ARG(Z) + LOG_FUNC_ARG(Stencil) + LOG_FUNC_END; + + DWORD HostFlags = 0; + + // make adjustments to parameters to make sense with windows d3d + { + if (Flags & X_D3DCLEAR_TARGET) { + // TODO: D3DCLEAR_TARGET_A, *R, *G, *B don't exist on windows + if ((Flags & X_D3DCLEAR_TARGET) != X_D3DCLEAR_TARGET) + EmuLog(LOG_LEVEL::WARNING, "Unsupported : Partial D3DCLEAR_TARGET flag(s) for D3DDevice_Clear : 0x%.08X", Flags & X_D3DCLEAR_TARGET); + + HostFlags |= D3DCLEAR_TARGET; + } + + // Do not needlessly clear Z Buffer + if (Flags & X_D3DCLEAR_ZBUFFER) { + if (g_bHasDepth) + HostFlags |= D3DCLEAR_ZBUFFER; + else + EmuLog(LOG_LEVEL::WARNING, "Unsupported : D3DCLEAR_ZBUFFER flag for D3DDevice_Clear without ZBuffer"); + } + + // Only clear depth buffer and stencil if present + // + // Avoids following DirectX Debug Runtime error report + // [424] Direct3D8: (ERROR) :Invalid flag D3DCLEAR_ZBUFFER: no zbuffer is associated with device. Clear failed. + if (Flags & X_D3DCLEAR_STENCIL) { + if (g_bHasStencil) + HostFlags |= D3DCLEAR_STENCIL; + else + EmuLog(LOG_LEVEL::WARNING, "Unsupported : D3DCLEAR_STENCIL flag for D3DDevice_Clear without ZBuffer"); + } + + if(Flags & ~(X_D3DCLEAR_TARGET | X_D3DCLEAR_ZBUFFER | X_D3DCLEAR_STENCIL)) + EmuLog(LOG_LEVEL::WARNING, "Unsupported Flag(s) for D3DDevice_Clear : 0x%.08X", Flags & ~(X_D3DCLEAR_TARGET | X_D3DCLEAR_ZBUFFER | X_D3DCLEAR_STENCIL)); + } + + HRESULT hRet; + + if (pRects != nullptr) { + // Scale the fill based on our scale factor + D3DRECT rect = *pRects; + rect.x1 *= g_RenderScaleFactor; + rect.x2 *= g_RenderScaleFactor; + rect.y1 *= g_RenderScaleFactor; + rect.y2 *= g_RenderScaleFactor; + hRet = g_pD3DDevice->Clear(Count, &rect, HostFlags, Color, Z, Stencil); + } else { + hRet = g_pD3DDevice->Clear(Count, pRects, HostFlags, Color, Z, Stencil); + } + + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Clear"); +} + + +// ****************************************************************** +// * patch: D3DDevice_CopyRects +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_CopyRects) +( + X_D3DSurface* pSourceSurface, + CONST RECT* pSourceRectsArray, + UINT cRects, + X_D3DSurface* pDestinationSurface, + CONST POINT* pDestPointsArray +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pSourceSurface); + LOG_FUNC_ARG(pSourceRectsArray); + LOG_FUNC_ARG(cRects); + LOG_FUNC_ARG(pDestinationSurface); + LOG_FUNC_ARG(pDestPointsArray); + LOG_FUNC_END; + + // We skip the trampoline to prevent unnecessary work + // As our surfaces remain on the GPU, calling the trampoline would just + // result in a memcpy from an empty Xbox surface to another empty Xbox Surface + D3DSURFACE_DESC SourceDesc, DestinationDesc; + auto pHostSourceSurface = GetHostSurface(pSourceSurface); + auto pHostDestSurface = GetHostSurface(pDestinationSurface); + + if (pHostSourceSurface == nullptr || pHostDestSurface == nullptr) { + // Test Case: DOA2 attempts to copy from an index buffer resource type + // TODO: What should we do here? + LOG_TEST_CASE("D3DDevice-CopyRects: Failed to fetch host surfaces"); + return; + } + + pHostSourceSurface->GetDesc(&SourceDesc); + pHostDestSurface->GetDesc(&DestinationDesc); + + // If the source is a render-target and the destination is not, we need force it to be re-created as one + // This is because StrechRects cannot copy from a Render-Target to a Non-Render Target + // Test Case: Crash Bandicoot: Wrath of Cortex attemps to copy the render-target to a texture + // This fixes an issue on the pause screen where the screenshot of the current scene was not displayed correctly + if ((SourceDesc.Usage & D3DUSAGE_RENDERTARGET) != 0 && (DestinationDesc.Usage & D3DUSAGE_RENDERTARGET) == 0) { + pHostDestSurface = GetHostSurface(pDestinationSurface, D3DUSAGE_RENDERTARGET); + pHostDestSurface->GetDesc(&DestinationDesc); + } + + // If no rectangles were given, default to 1 (entire surface) + if (cRects == 0) { + cRects = 1; + } + + for (UINT i = 0; i < cRects; i++) { + RECT SourceRect, DestRect; + + if (pSourceRectsArray != nullptr) { + SourceRect = pSourceRectsArray[i]; + } else { + SourceRect.left = 0; + SourceRect.right = SourceDesc.Width; + SourceRect.top = 0; + SourceRect.bottom = SourceDesc.Height; + } + + if (pDestPointsArray != nullptr) { + DestRect.left = pDestPointsArray[i].x; + DestRect.right = DestRect.left + (SourceRect.right - SourceRect.left); + DestRect.top = pDestPointsArray[i].y; + DestRect.bottom = DestRect.top + (SourceRect.bottom - SourceRect.top); + } else if (pSourceRectsArray) { + DestRect = SourceRect; + } else { + DestRect.left = 0; + DestRect.right = DestinationDesc.Width; + DestRect.top = 0; + DestRect.bottom = DestinationDesc.Height; + } + + HRESULT hRet = g_pD3DDevice->StretchRect(pHostSourceSurface, &SourceRect, pHostDestSurface, &DestRect, D3DTEXF_NONE); + if (FAILED(hRet)) { + LOG_TEST_CASE("D3DDevice_CopyRects: Failed to copy surface"); + } + } +} + +#define CXBX_SWAP_PRESENT_FORWARD (256 + 4 + 1) // = CxbxPresentForwardMarker + D3DSWAP_FINISH + D3DSWAP_COPY + +// ****************************************************************** +// * patch: D3DDevice_Present +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_Present) +( + CONST RECT* pSourceRect, + CONST RECT* pDestRect, + PVOID pDummy1, + PVOID pDummy2 +) +{ + // LOG_FORWARD("D3DDevice_Swap"); + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pSourceRect) + LOG_FUNC_ARG(pDestRect) + LOG_FUNC_ARG(pDummy1) + LOG_FUNC_ARG(pDummy2) + LOG_FUNC_END; + + EMUPATCH(D3DDevice_Swap)(CXBX_SWAP_PRESENT_FORWARD); // Xbox present ignores +} + +std::chrono::time_point frameStartTime; + +// LTCG specific swap function... +// Massive hack, but could coax some more LTCG titles into booting with HLE +// This uses a custom calling convention where parameter is passed in EAX +DWORD XTL::EMUPATCH(D3DDevice_Swap_0) +( +) +{ + uint32_t param; + __asm { + mov param, eax; + } + + return EMUPATCH(D3DDevice_Swap)(param); +} + +// ****************************************************************** +// * patch: D3DDevice_Swap +// ****************************************************************** +DWORD WINAPI XTL::EMUPATCH(D3DDevice_Swap) +( + DWORD Flags +) +{ + LOG_FUNC_ONE_ARG(Flags); + + // TODO: Ensure this flag is always the same across library versions + if (Flags != 0 && Flags != CXBX_SWAP_PRESENT_FORWARD) + EmuLog(LOG_LEVEL::WARNING, "XTL::EmuD3DDevice_Swap: Flags != 0"); + + // Fetch the host backbuffer + IDirect3DSurface *pCurrentHostBackBuffer = nullptr; + HRESULT hRet = g_pD3DDevice->GetBackBuffer( + 0, // iSwapChain + 0, D3DBACKBUFFER_TYPE_MONO, &pCurrentHostBackBuffer); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetBackBuffer - Unable to get backbuffer surface!"); + if (hRet == D3D_OK) { + assert(pCurrentHostBackBuffer != nullptr); + + // Get backbuffer dimensions; TODO : remember this once, at creation/resize time + D3DSURFACE_DESC BackBufferDesc; + pCurrentHostBackBuffer->GetDesc(&BackBufferDesc); + + // TODO: Implement a hot-key to change the filter? + // Note: LoadSurfaceFilter Must be D3DTEXF_NONE, D3DTEXF_POINT or D3DTEXF_LINEAR + // Before StretchRects we used D3DX_FILTER_POINT here, but that gave jagged edges in Dashboard. + // Dxbx note : D3DX_FILTER_LINEAR gives a smoother image, but 'bleeds' across borders + // LoadOverlayFilter must be a D3DX filter DWORD value + const D3DTEXTUREFILTERTYPE LoadSurfaceFilter = D3DTEXF_LINEAR; + const DWORD LoadOverlayFilter = D3DX_DEFAULT; + + auto pXboxBackBufferHostSurface = GetHostSurface(g_pXbox_BackBufferSurface, D3DUSAGE_RENDERTARGET); + if (pXboxBackBufferHostSurface) { + // Blit Xbox BackBuffer to host BackBuffer + // TODO: Respect aspect ratio + hRet = g_pD3DDevice->StretchRect( + /* pSourceSurface = */ pXboxBackBufferHostSurface, + /* pSourceRect = */ nullptr, + /* pDestSurface = */ pCurrentHostBackBuffer, + /* pDestRect = */ nullptr, + /* Filter = */ LoadSurfaceFilter + ); + + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Couldn't blit Xbox BackBuffer to host BackBuffer : %X", hRet); + } + } + + // Is there an overlay to be presented too? + if (g_OverlayProxy.Surface.Common) { + X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(&g_OverlayProxy.Surface); + if (X_Format != X_D3DFMT_YUY2) { + LOG_TEST_CASE("Xbox overlay surface isn't using X_D3DFMT_YUY2"); + } + + // Interpret the Xbox overlay data (depending the color space conversion render state) + // as either YUV or RGB format (note that either one must be a 3 bytes per pixel format) + D3DFORMAT PCFormat; + // TODO : Before reading from pgraph, flush all pending push-buffer commands + switch (GET_MASK(HLE_read_NV2A_pgraph_register(NV_PGRAPH_CONTROL_0), NV_PGRAPH_CONTROL_0_CSCONVERT)) { + case 0: // = pass-through + PCFormat = D3DFMT_YUY2; + break; + case 1: // = CRYCB_TO_RGB + PCFormat = D3DFMT_YUY2; // Test-case : Turok (intro movie) + break; + case 2: // = SCRYSCB_TO_RGB + LOG_TEST_CASE("SCRYSCB_TO_RGB"); + PCFormat = D3DFMT_YUY2; + break; + default: + LOG_TEST_CASE("Unrecognized NV_PGRAPH_CONTROL_0_CSCONVERT"); + PCFormat = D3DFMT_YUY2; + break; + } + + // Blit Xbox overlay to host backbuffer + uint8_t *pOverlayData = (uint8_t*)GetDataFromXboxResource(&g_OverlayProxy.Surface); + UINT OverlayWidth, OverlayHeight, OverlayDepth, OverlayRowPitch, OverlaySlicePitch; + CxbxGetPixelContainerMeasures( + &g_OverlayProxy.Surface, + 0, // dwMipMapLevel + &OverlayWidth, &OverlayHeight, &OverlayDepth, &OverlayRowPitch, &OverlaySlicePitch); + + RECT EmuSourRect = { 0 }; + RECT EmuDestRect = { 0 }; + + if (g_OverlayProxy.SrcRect.right > 0) { + EmuSourRect = g_OverlayProxy.SrcRect; + } + else { + SetRect(&EmuSourRect, 0, 0, OverlayWidth, OverlayHeight); + } + + if (g_OverlayProxy.DstRect.right > 0) { + // If there's a destination rectangle given, copy that into our local variable : + EmuDestRect = g_OverlayProxy.DstRect; + + // Make sure to scale the values based on the difference between the Xbox and Host backbuffer + // We can't use the scale factor here because we are blitting directly to the host backbuffer + // NOT an Xbox surface! + DWORD XboxBackBufferWidth = GetPixelContainerWidth(g_pXbox_BackBufferSurface); + DWORD XboxBackBufferHeight = GetPixelContainerHeight(g_pXbox_BackBufferSurface); + + // We also need to account for any MSAA which may have enlarged the Xbox Backbuffer + float xScale, yScale; + GetMultiSampleScale(xScale, yScale); + + xScale = (float)BackBufferDesc.Width / ((float)XboxBackBufferWidth / xScale); + yScale = (float)BackBufferDesc.Height / ((float)XboxBackBufferHeight / yScale); + + EmuDestRect.top = (LONG)(EmuDestRect.top * yScale); + EmuDestRect.left = (LONG)(EmuDestRect.left * xScale); + EmuDestRect.bottom = (LONG)(EmuDestRect.bottom * yScale); + EmuDestRect.right = (LONG)(EmuDestRect.right * xScale); + } else { + // Use backbuffer width/height since that may differ from the Window size + EmuDestRect.right = BackBufferDesc.Width; + EmuDestRect.bottom = BackBufferDesc.Height; + } + + // load the YUY2 into the backbuffer + + // Limit the width and height of the output to the backbuffer dimensions. + // This will (hopefully) prevent exceptions in Blinx - The Time Sweeper + // (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/285) + { + // Use our (bounded) copy when bounds exceed : + if (EmuDestRect.right > (LONG)BackBufferDesc.Width) { + EmuDestRect.right = (LONG)BackBufferDesc.Width; + } + + if (EmuDestRect.bottom > (LONG)BackBufferDesc.Height) { + EmuDestRect.bottom = (LONG)BackBufferDesc.Height; + } + } + + // Create a temporary surface to hold the overlay + // This is faster than loading directly into the backbuffer because it offloads scaling to the GPU + // Without this, upscaling tanks the frame-rate! + IDirect3DSurface* pTemporaryOverlaySurface; + HRESULT hRet = g_pD3DDevice->CreateOffscreenPlainSurface( + OverlayWidth, + OverlayHeight, + D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, + &pTemporaryOverlaySurface, + nullptr + ); + + if (FAILED(hRet)) { + EmuLog(LOG_LEVEL::WARNING, "Couldn't create temporary overlay surface : %X", hRet); + } else { + RECT doNotScaleRect = { 0, 0, (LONG)OverlayWidth, (LONG)OverlayHeight }; + + // Use D3DXLoadSurfaceFromMemory() to do conversion, we don't stretch at this moment in time + // avoiding the need for YUY2toARGB() (might become relevant when porting to D3D9 or OpenGL) + // see https://msdn.microsoft.com/en-us/library/windows/desktop/bb172902(v=vs.85).aspx + hRet = D3DXLoadSurfaceFromMemory( + /* pDestSurface = */ pTemporaryOverlaySurface, + /* pDestPalette = */ nullptr, + /* pDestRect = */ &doNotScaleRect, + /* pSrcMemory = */ pOverlayData, // Source buffer + /* SrcFormat = */ PCFormat, + /* SrcPitch = */ OverlayRowPitch, + /* pSrcPalette = */ nullptr, + /* pSrcRect = */ &doNotScaleRect, // This parameter cannot be NULL + /* Filter = */ LoadOverlayFilter, + /* ColorKey = */ g_OverlayProxy.EnableColorKey ? g_OverlayProxy.ColorKey : 0); + + DEBUG_D3DRESULT(hRet, "D3DXLoadSurfaceFromMemory - UpdateOverlay could not convert buffer!\n"); + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Couldn't load Xbox overlay to host surface : %X", hRet); + } else { + // TODO: Respect aspect ratio + hRet = g_pD3DDevice->StretchRect( + /* pSourceSurface = */ pTemporaryOverlaySurface, + /* pSourceRect = */ &EmuSourRect, + /* pDestSurface = */ pCurrentHostBackBuffer, + /* pDestRect = */ &EmuDestRect, + /* Filter = */ LoadSurfaceFilter + ); + + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Couldn't load Xbox overlay to host back buffer : %X", hRet); + } + } + + pTemporaryOverlaySurface->Release(); + } + } + + pCurrentHostBackBuffer->Release(); + } + + g_pD3DDevice->EndScene(); + + hRet = g_pD3DDevice->Present(0, 0, 0, 0); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->Present"); + + hRet = g_pD3DDevice->BeginScene(); + + // RenderStates need reapplying each frame, but can be re-used between draw calls + // This forces them to be reset + XboxRenderStates.SetDirty(); + + // Check if we need to enable our frame-limiter + DWORD presentationInverval = g_Xbox_PresentationInterval_Override > 0 ? g_Xbox_PresentationInterval_Override : g_Xbox_PresentationInterval_Default; + if ((presentationInverval != D3DPRESENT_INTERVAL_IMMEDIATE) && !g_bHack_UnlockFramerate) { + // If the last frame completed faster than the Xbox target swap rate, wait for it + + auto targetRefreshRate = 60.0f; // TODO: Read from Xbox Display Mode + + // Determine how many 'frames' worth of time we need to wait for + // This allows games that require a locked framerate (eg JSRF) to function correctly + // While allowing titles with an unlocked frame-rate to not be limited + auto multiplier = 1.0f; + switch (presentationInverval) { + case D3DPRESENT_INTERVAL_ONE: + case 0x80000001: // D3DPRESENT_INTERVAL_ONE_OR_IMMEDIATE: + multiplier = 1.0f; + break; + case D3DPRESENT_INTERVAL_TWO: + case 0x80000002: // D3DPRESENT_INTERVAL_TWO_OR_IMMEDIATE: + multiplier = 2.0f; + break; + case D3DPRESENT_INTERVAL_THREE: + multiplier = 3.0f; + break; + case D3DPRESENT_INTERVAL_FOUR: + multiplier = 4.0f; + break; + } + + auto targetDuration = std::chrono::duration(((1000.0f / targetRefreshRate) * multiplier)); + auto targetTimestamp = frameStartTime + targetDuration; + auto actualDuration = std::chrono::duration(std::chrono::steady_clock::now() - frameStartTime); + auto startTimeAjustment = actualDuration - targetDuration; + + // Only enter the wait loop if the frame took too long + if (actualDuration < targetDuration) { + // If we need to wait for a larger amount of time (>= 1 frame at 60FPS), we can just sleep + if ((targetTimestamp - std::chrono::steady_clock::now()) > std::chrono::duration(16.0)) { + std::this_thread::sleep_until(targetTimestamp); + } else { + // Otherwise, we fall-through and just keep polling + // This prevents large waits from hogging CPU power, but allows small waits/ to remain precice. + while (std::chrono::steady_clock::now() < targetTimestamp) { + ; + } + } + } + } + + frameStartTime = std::chrono::steady_clock::now(); + + UpdateFPSCounter(); + + if (Flags == CXBX_SWAP_PRESENT_FORWARD) // Only do this when forwarded from Present + { + // Put primitives per frame in the title + /*{ + char szString[64]; + + sprintf( szString, "Cxbx: PPF(%d)", g_dwPrimPerFrame ); + + SetWindowText( CxbxKrnl_hEmuParent, szString ); + + g_dwPrimPerFrame = 0; + }*/ + + // TODO : Check if this should be done at Swap-not-Present-time too : + // not really accurate because you definately dont always present on every vblank + g_Xbox_VBlankData.Swap = g_Xbox_VBlankData.VBlank; + + if (g_Xbox_VBlankData.VBlank == g_VBLastSwap + 1) + g_Xbox_VBlankData.Flags = 1; // D3DVBLANK_SWAPDONE + else + { + g_Xbox_VBlankData.Flags = 2; // D3DVBLANK_SWAPMISSED + g_Xbox_SwapData.MissedVBlanks++; + } + } + + // Handle Swap Callback function + { + g_Xbox_SwapData.Swap++; + + if(g_pXbox_SwapCallback != xbnullptr) + { + + g_pXbox_SwapCallback(&g_Xbox_SwapData); + + } + } + + DWORD result; + if (Flags == CXBX_SWAP_PRESENT_FORWARD) // Only do this when forwarded from Present + result = D3D_OK; // Present always returns success + else + result = g_Xbox_SwapData.Swap; // Swap returns number of swaps + + return result; +} + +bool IsSupportedFormat(XTL::X_D3DFORMAT X_Format, XTL::X_D3DRESOURCETYPE XboxResourceType, DWORD D3DUsage) { + // TODO : Nuance the following, because the Direct3D 8 docs states + // CheckDeviceFormat is needed when D3DUSAGE_RENDERTARGET or + // D3DUSAGE_DYNAMNIC is specified. + // Otherwise, lookup resource type and accompanying 'SupportedFormat' array + bool *pbSupportedFormats = g_bSupportsFormatTexture; + + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: { + if (D3DUsage & D3DUSAGE_RENDERTARGET) { + pbSupportedFormats = g_bSupportsFormatSurfaceRenderTarget; + } else if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + pbSupportedFormats = g_bSupportsFormatSurfaceDepthStencil; + } else { + pbSupportedFormats = g_bSupportsFormatSurface; + } + break; + } + case XTL::X_D3DRTYPE_VOLUME: { + pbSupportedFormats = g_bSupportsFormatTexture; // TODO : Complete + break; + } + case XTL::X_D3DRTYPE_TEXTURE: { + if (D3DUsage & D3DUSAGE_RENDERTARGET) { + pbSupportedFormats = g_bSupportsFormatTextureRenderTarget; + } else if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + pbSupportedFormats = g_bSupportsFormatTextureDepthStencil; + } else { + pbSupportedFormats = g_bSupportsFormatTexture; + } + break; + } + case XTL::X_D3DRTYPE_VOLUMETEXTURE: { + pbSupportedFormats = g_bSupportsFormatVolumeTexture; // TODO : Complete + break; + } + case XTL::X_D3DRTYPE_CUBETEXTURE: { + pbSupportedFormats = g_bSupportsFormatCubeTexture; // TODO : Complete + break; + } + } // switch XboxResourceType + + return pbSupportedFormats[X_Format]; +} + +// Was patch: IDirect3DResource8_Register +void CreateHostResource(XTL::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize) +{ + if (pResource == xbnullptr) + return; + + // Determine the resource type name + const char *ResourceTypeName; + XTL::X_D3DRESOURCETYPE XboxResourceType = GetXboxD3DResourceType(pResource); + + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_NONE: ResourceTypeName = "None"; break; + case XTL::X_D3DRTYPE_SURFACE: ResourceTypeName = "Surface"; break; + case XTL::X_D3DRTYPE_VOLUME: ResourceTypeName = "Volume"; break; + case XTL::X_D3DRTYPE_TEXTURE: ResourceTypeName = "Texture"; break; + case XTL::X_D3DRTYPE_VOLUMETEXTURE: ResourceTypeName = "VolumeTexture"; break; + case XTL::X_D3DRTYPE_CUBETEXTURE: ResourceTypeName = "CubeTexture"; break; + case XTL::X_D3DRTYPE_VERTEXBUFFER: ResourceTypeName = "VertexBuffer"; break; + case XTL::X_D3DRTYPE_INDEXBUFFER: ResourceTypeName = "IndexBuffer"; break; + case XTL::X_D3DRTYPE_PUSHBUFFER: ResourceTypeName = "PushBuffer"; break; + case XTL::X_D3DRTYPE_PALETTE: ResourceTypeName = "Palette"; break; + case XTL::X_D3DRTYPE_FIXUP: ResourceTypeName = "Fixup"; break; + default: + EmuLog(LOG_LEVEL::WARNING, "CreateHostResource :-> Unrecognized Xbox Resource Type 0x%.08X", XboxResourceType); + return; + } + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pResource) + LOG_FUNC_ARG(iTextureStage) + LOG_FUNC_ARG(dwSize) + LOG_FUNC_ARG(ResourceTypeName) + LOG_FUNC_END; + + // Retrieve and test the xbox resource buffer address + VAddr VirtualAddr = (VAddr)GetDataFromXboxResource(pResource); + if ((VirtualAddr & ~PHYSICAL_MAP_BASE) == 0) { + // TODO: Fix or handle this situation..? + LOG_TEST_CASE("CreateHostResource : VirtualAddr == 0"); + // This is probably an unallocated resource, mapped into contiguous memory (0x80000000 OR 0xF0000000) + EmuLog(LOG_LEVEL::WARNING, "CreateHostResource :-> %s carries no data - skipping conversion", ResourceTypeName); + return; + } + + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_NONE: { + break; + } + + case XTL::X_D3DRTYPE_SURFACE: { + XTL::X_D3DSurface *pXboxSurface = (XTL::X_D3DSurface *)pResource; + XTL::X_D3DBaseTexture *pParentXboxTexture = (pXboxSurface) ? (XTL::X_D3DBaseTexture*)pXboxSurface->Parent : xbnullptr; + + // Don't init the Parent if the Surface and Surface Parent formats differ + // Happens in some Outrun 2006 SetRenderTarget calls + if (pParentXboxTexture && (pXboxSurface->Format == pParentXboxTexture->Format)) { + // For surfaces with a parent texture, map these to a host texture first + // TODO : Investigate how it's possible (and how we could fix) the case when + // the following call to GetHostBaseTexture would reject non-texture resources, + // which would seem to trigger a "CreateCubeTexture Failed!" regression. + IDirect3DBaseTexture *pParentHostBaseTexture = GetHostBaseTexture(pParentXboxTexture, D3DUsage, iTextureStage); + IDirect3DSurface* pNewHostSurface; + switch (pParentHostBaseTexture->GetType()) { + case D3DRTYPE_VOLUMETEXTURE: { + LOG_TEST_CASE("Using child surface of VolumeTexture"); + // TODO + break; + } + case D3DRTYPE_CUBETEXTURE: { + // test-case : Burnout + auto pParentHostTexture = (IDirect3DCubeTexture*)pParentHostBaseTexture; + + D3DCUBEMAP_FACES CubeMapFace = D3DCUBEMAP_FACE_POSITIVE_X; + UINT SurfaceLevel = 0; + GetSurfaceFaceAndLevelWithinTexture(pXboxSurface, pParentXboxTexture, SurfaceLevel, CubeMapFace); + + HRESULT hRet = pParentHostTexture->GetCubeMapSurface(CubeMapFace, SurfaceLevel, &pNewHostSurface); + + DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetCubeMapSurface"); + if (hRet == D3D_OK) { + SetHostSurface(pXboxSurface, pNewHostSurface, iTextureStage); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created CubeTexture surface level (Face: %u, Level: %u, pResource: 0x%.08X, pNewHostSurface: 0x%.08X)", + CubeMapFace, SurfaceLevel, pResource, pNewHostSurface); + return; + } + + break; + } + case D3DRTYPE_TEXTURE: { + IDirect3DTexture* pParentHostTexture = (IDirect3DTexture*)pParentHostBaseTexture; + + UINT SurfaceLevel = 0; + GetSurfaceFaceAndLevelWithinTexture(pXboxSurface, pParentXboxTexture, SurfaceLevel); + HRESULT hRet = pParentHostTexture->GetSurfaceLevel(SurfaceLevel, &pNewHostSurface); + + DEBUG_D3DRESULT(hRet, "pHostParentTexture->GetSurfaceLevel"); + if (hRet == D3D_OK) { + SetHostSurface(pXboxSurface, pNewHostSurface, iTextureStage); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created Texture surface level (Level: %u, pResource: 0x%.08X, pNewHostSurface: 0x%.08X)", + SurfaceLevel, pResource, pNewHostSurface); + return; + } + break; + } + } + + EmuLog(LOG_LEVEL::WARNING, "Failed getting host surface level - falling through to regular surface creation"); + } + // fall through + } + case XTL::X_D3DRTYPE_VOLUME: { + // Note : Use and check for null, since X_D3DRTYPE_SURFACE might fall through here (by design) + XTL::X_D3DVolume *pXboxVolume = (XboxResourceType == XTL::X_D3DRTYPE_VOLUME) ? (XTL::X_D3DVolume *)pResource : xbnullptr; + XTL::X_D3DVolumeTexture *pParentXboxVolumeTexture = (pXboxVolume) ? (XTL::X_D3DVolumeTexture *)pXboxVolume->Parent : xbnullptr; + if (pParentXboxVolumeTexture) { + // For volumes with a parent volume texture, map these to a host volume texture first + IDirect3DVolumeTexture *pParentHostVolumeTexture = GetHostVolumeTexture(pParentXboxVolumeTexture, iTextureStage); + UINT VolumeLevel = 0; // TODO : Derive actual level based on pXboxVolume->Data delta to pParentXboxVolumeTexture->Data + IDirect3DVolume *pNewHostVolume; + HRESULT hRet = pParentHostVolumeTexture->GetVolumeLevel(VolumeLevel, &pNewHostVolume); + DEBUG_D3DRESULT(hRet, "pParentHostVolumeTexture->GetVolumeLevel"); + if (hRet == D3D_OK) { + SetHostVolume(pXboxVolume, pNewHostVolume, iTextureStage); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created volume level (%u, 0x%.08X, 0x%.08X)", + VolumeLevel, pResource, pNewHostVolume); + return; + } + + EmuLog(LOG_LEVEL::WARNING, "Failed getting host volume level - falling through to regular volume creation"); + } + // fall through + } + case XTL::X_D3DRTYPE_TEXTURE: + case XTL::X_D3DRTYPE_VOLUMETEXTURE: + case XTL::X_D3DRTYPE_CUBETEXTURE: { + XTL::X_D3DPixelContainer *pPixelContainer = (XTL::X_D3DPixelContainer*)pResource; + XTL::X_D3DFORMAT X_Format = GetXboxPixelContainerFormat(pPixelContainer); + D3DPOOL D3DPool = D3DPOOL_DEFAULT; // Was: D3DPOOL_MANAGED TODO : Nuance D3DPOOL where/when needed + + if (EmuXBFormatIsDepthBuffer(X_Format)) { + D3DUsage |= D3DUSAGE_DEPTHSTENCIL; + } + else if (pPixelContainer == g_pXbox_RenderTarget) { + if (EmuXBFormatIsRenderTarget(X_Format)) + D3DUsage |= D3DUSAGE_RENDERTARGET; + else + EmuLog(LOG_LEVEL::WARNING, "Updating RenderTarget %s with an incompatible format!", ResourceTypeName); + } + + // Determine the format we'll be using on host D3D + D3DFORMAT PCFormat; + bool bConvertToARGB = false; + + if (EmuXBFormatRequiresConversionToARGB(X_Format)) { + bConvertToARGB = true; + PCFormat = D3DFMT_A8R8G8B8; + + // Unset D3DUSAGE_DEPTHSTENCIL: It's not possible for ARGB textures to be depth stencils + // Fixes CreateTexture error in Virtua Cop 3 (Chihiro) + D3DUsage &= ~D3DUSAGE_DEPTHSTENCIL; + } + else { + // Does host CheckDeviceFormat() succeed on this format? + if (IsSupportedFormat(X_Format, XboxResourceType, D3DUsage)) { + // Then use matching host format + PCFormat = EmuXB2PC_D3DFormat(X_Format); + + // If, and ONLY if this is the default backbuffer, make sure the format matches the host backbuffer + if (pResource == g_pXbox_BackBufferSurface) { + PCFormat = g_EmuCDPD.HostPresentationParameters.BackBufferFormat; + } + } + else { + if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + // If it was a depth stencil, fall back to a known supported depth format + EmuLog(LOG_LEVEL::WARNING, "Xbox %s Format %x will be converted to D3DFMT_D24S8", ResourceTypeName, X_Format); + PCFormat = D3DFMT_D24S8; + } else if (EmuXBFormatCanBeConvertedToARGB(X_Format)) { + EmuLog(LOG_LEVEL::WARNING, "Xbox %s Format %x will be converted to ARGB", ResourceTypeName, X_Format); + bConvertToARGB = true; + PCFormat = D3DFMT_A8R8G8B8; + } else { + // Otherwise, use a best matching format + /*CxbxKrnlCleanup*/EmuLog(LOG_LEVEL::WARNING, "Encountered a completely incompatible %s format!", ResourceTypeName); + PCFormat = EmuXB2PC_D3DFormat(X_Format); + } + } + } + + // Update D3DPool accordingly to the active D3DUsage flags + if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + D3DPool = D3DPOOL_DEFAULT; + } + if (D3DUsage & D3DUSAGE_RENDERTARGET) { + D3DPool = D3DPOOL_DEFAULT; + } + if (D3DUsage & D3DUSAGE_DYNAMIC) { + D3DPool = D3DPOOL_DEFAULT; + } + + // Interpret Width/Height/BPP + bool bCubemap = pPixelContainer->Format & X_D3DFORMAT_CUBEMAP; + bool bSwizzled = EmuXBFormatIsSwizzled(X_Format); + bool bCompressed = EmuXBFormatIsCompressed(X_Format); + DWORD dwMinSize = (bCompressed) ? 4 : 1; + UINT dwBPP = EmuXBFormatBytesPerPixel(X_Format); + UINT dwMipMapLevels = CxbxGetPixelContainerMipMapLevels(pPixelContainer); + UINT dwWidth, dwHeight, dwDepth, dwRowPitch, dwSlicePitch; + + // Interpret Width/Height/BPP + CxbxGetPixelContainerMeasures(pPixelContainer, 0, &dwWidth, &dwHeight, &dwDepth, &dwRowPitch, &dwSlicePitch); + + // Each mip-map level is 1/2 the size of the previous level + // D3D9 forbids creation of a texture with more mip-map levels than it is divisible + // EG: A 256x256 texture cannot have more than 8 levels, since that would create a texture smaller than 1x1 + // Because of this, we need to cap dwMipMapLevels when required + if (dwMipMapLevels > 0) { + // Calculate how many mip-map levels it takes to get to a texture of 1 pixels in either dimension + UINT highestMipMapLevel = 1; + UINT width = dwWidth; UINT height = dwHeight; + while (width > 1 || height > 1) { + width /= 2; + height /= 2; + highestMipMapLevel++; + } + + // If the desired mip-map level was higher than the maximum possible, cap it + // Test case: Shin Megami Tensei: Nine + if (dwMipMapLevels > highestMipMapLevel) { + LOG_TEST_CASE("Too many mip-map levels"); + dwMipMapLevels = highestMipMapLevel; + } + } + + if (dwDepth != 1) { + LOG_TEST_CASE("CreateHostResource : Depth != 1"); + } + + // The following is necessary for DXT* textures (4x4 blocks minimum) + // TODO: Figure out if this is necessary under other circumstances? + if (bCompressed) { + if (dwWidth < dwMinSize) { + LOG_TEST_CASE("CreateHostResource : dwWidth < dwMinSize"); + EmuLog(LOG_LEVEL::WARNING, "Expanding %s width (%d->%d)", ResourceTypeName, dwWidth, dwMinSize); + dwWidth = dwMinSize; + } + + if (dwHeight < dwMinSize) { + LOG_TEST_CASE("CreateHostResource : dwHeight < dwMinSize"); + EmuLog(LOG_LEVEL::WARNING, "Expanding %s height (%d->%d)", ResourceTypeName, dwHeight, dwMinSize); + dwHeight = dwMinSize; + } + } + + // One of these will be created : each also has an intermediate copy to allow UpdateTexture to work + // This means we don't need to lock the GPU resource anymore, so we can use D3DPOOL_DEFAULT to allow Stretch/CopyRects to work! + IDirect3DSurface *pNewHostSurface = nullptr; // for X_D3DRTYPE_SURFACE + IDirect3DVolume *pNewHostVolume = nullptr; // for X_D3DRTYPE_VOLUME + IDirect3DTexture *pNewHostTexture = nullptr; // for X_D3DRTYPE_TEXTURE + IDirect3DTexture *pIntermediateHostTexture = nullptr; + IDirect3DVolumeTexture *pNewHostVolumeTexture = nullptr; // for X_D3DRTYPE_VOLUMETEXTURE + IDirect3DVolumeTexture *pIntermediateHostVolumeTexture = nullptr; + IDirect3DCubeTexture *pNewHostCubeTexture = nullptr; // for X_D3DRTYPE_CUBETEXTURE + IDirect3DCubeTexture *pIntermediateHostCubeTexture = nullptr; + + HRESULT hRet; + + // Create the surface/volume/(volume/cube/)texture + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: { + if (D3DUsage & D3DUSAGE_RENDERTARGET) { + hRet = g_pD3DDevice->CreateRenderTarget(dwWidth * g_RenderScaleFactor, dwHeight * g_RenderScaleFactor, PCFormat, + g_EmuCDPD.HostPresentationParameters.MultiSampleType, + 0, // MultisampleQuality + true, // Lockable + &pNewHostSurface, + nullptr // pSharedHandle + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateRenderTarget"); + } else + if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + hRet = g_pD3DDevice->CreateDepthStencilSurface(dwWidth * g_RenderScaleFactor, dwHeight * g_RenderScaleFactor, PCFormat, + g_EmuCDPD.HostPresentationParameters.MultiSampleType, + 0, // MultisampleQuality + false, // Discard + &pNewHostSurface, + nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateDepthStencilSurface"); + } + else { + D3DPool = D3DPOOL_SYSTEMMEM; + hRet = g_pD3DDevice->CreateOffscreenPlainSurface(dwWidth, dwHeight, PCFormat, D3DPool, &pNewHostSurface, nullptr); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateOffscreenPlainSurface"); + } + + // First fail, retry with a fallback format + // If this succeeds, the surface may not render correctly, but it won't crash + if (hRet != D3D_OK) { + if (D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + EmuLog(LOG_LEVEL::WARNING, "CreateDepthStencilSurface Failed\n\nError: %s\nDesc: %s", + DXGetErrorString(hRet), DXGetErrorDescription(hRet)); + } + else { + EmuLog(LOG_LEVEL::WARNING, "CreateImageSurface Failed\n\nError: %s\nDesc: %s", + DXGetErrorString(hRet), DXGetErrorDescription(hRet)); + } + + EmuLog(LOG_LEVEL::WARNING, "Trying Fallback"); + hRet = g_pD3DDevice->CreateOffscreenPlainSurface(dwWidth, dwHeight, PCFormat, D3DPool, &pNewHostSurface, nullptr); + } + + // If the fallback failed, show an error and exit execution. + if (hRet != D3D_OK) { + // We cannot safely continue in this state. + CxbxKrnlCleanup("CreateImageSurface Failed!\n\nError: %s\nDesc: %s", + DXGetErrorString(hRet), DXGetErrorDescription(hRet)); + } + + SetHostSurface(pResource, pNewHostSurface, iTextureStage); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", + ResourceTypeName, pResource, pNewHostSurface); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Width : %d, Height : %d, Format : %d", + dwWidth, dwHeight, PCFormat); + break; + } + + case XTL::X_D3DRTYPE_VOLUME: { + LOG_UNIMPLEMENTED(); + // Note : Host D3D can only(?) retrieve a volume like this : + // hRet = pNewHostVolumeTexture->GetVolumeLevel(level, &pNewHostVolume); + // So, we need to do this differently - we need to step up to the containing VolumeTexture, + // and retrieve and convert all of it's GetVolumeLevel() slices. + pNewHostVolume = nullptr; + // SetHostVolume(pResource, pNewHostVolume, iTextureStage); + // EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", + // ResourceTypeName, pResource, pNewHostVolume); + break; + } + + case XTL::X_D3DRTYPE_TEXTURE: { + hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, + D3DUsage, PCFormat, D3DPool, &pNewHostTexture, + nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture"); + + // If the above failed, we might be able to use an ARGB texture instead + if ((hRet != D3D_OK) && (PCFormat != D3DFMT_A8R8G8B8) && EmuXBFormatCanBeConvertedToARGB(X_Format)) { + hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, + D3DUsage, D3DFMT_A8R8G8B8, D3DPool, &pNewHostTexture, + nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture(D3DFMT_A8R8G8B8)"); + + if (hRet == D3D_OK) { + // Okay, now this works, make sure the texture gets converted + bConvertToARGB = true; + PCFormat = D3DFMT_A8R8G8B8; + } + } + + // Now create our intermediate texture for UpdateTexture, but not for render targets or depth stencils + if (hRet == D3D_OK && (D3DUsage & D3DUSAGE_RENDERTARGET) == 0 && (D3DUsage & D3DUSAGE_DEPTHSTENCIL) == 0) { + hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, + 0, PCFormat, D3DPOOL_SYSTEMMEM, &pIntermediateHostTexture, + nullptr + ); + } + + /*if(FAILED(hRet)) + { + hRet = g_pD3DDevice->CreateTexture + ( + dwWidth, dwHeight, dwMipMapLevels, D3DUsage, PCFormat, + D3DPOOL_SYSTEMMEM, &pNewHostTexture, + nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture(D3DPOOL_SYSTEMMEM)"); + }*/ + + + if (hRet != D3D_OK) { + CxbxKrnlCleanup("CreateTexture Failed!\n\n" + "Error: 0x%X\nFormat: %d\nDimensions: %dx%d", hRet, PCFormat, dwWidth, dwHeight); + } + + SetHostTexture(pResource, pNewHostTexture, iTextureStage); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", + ResourceTypeName, pResource, pNewHostTexture); + break; + } + + case XTL::X_D3DRTYPE_VOLUMETEXTURE: { + hRet = g_pD3DDevice->CreateVolumeTexture(dwWidth, dwHeight, dwDepth, + dwMipMapLevels, D3DUsage, PCFormat, D3DPool, &pNewHostVolumeTexture, + nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateVolumeTexture"); + + // Now create our intermediate texture for UpdateTexture, but not for render targets or depth stencils + if (hRet == D3D_OK && (D3DUsage & D3DUSAGE_RENDERTARGET) == 0 && (D3DUsage & D3DUSAGE_DEPTHSTENCIL) == 0) { + hRet = g_pD3DDevice->CreateVolumeTexture(dwWidth, dwHeight, dwDepth, + dwMipMapLevels, 0, PCFormat, D3DPOOL_SYSTEMMEM, &pIntermediateHostVolumeTexture, + nullptr + ); + } + + if (hRet != D3D_OK) { + CxbxKrnlCleanup("CreateVolumeTexture Failed!\n\nError: %s\nDesc: %s", + DXGetErrorString(hRet), DXGetErrorDescription(hRet)); + } + + SetHostVolumeTexture(pResource, pNewHostVolumeTexture, iTextureStage); + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", + ResourceTypeName, pResource, pNewHostVolumeTexture); + break; + } + + case XTL::X_D3DRTYPE_CUBETEXTURE: { + EmuLog(LOG_LEVEL::DEBUG, "CreateCubeTexture(%d, %d, 0, %d, D3DPOOL_MANAGED)", dwWidth, + dwMipMapLevels, PCFormat); + + hRet = g_pD3DDevice->CreateCubeTexture(dwWidth, dwMipMapLevels, D3DUsage, + PCFormat, D3DPool, &pNewHostCubeTexture, + nullptr + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateCubeTexture"); + + // Now create our intermediate texture for UpdateTexture, but not for render targets or depth stencils + if (hRet == D3D_OK && (D3DUsage & D3DUSAGE_RENDERTARGET) == 0 && (D3DUsage & D3DUSAGE_DEPTHSTENCIL) == 0) { + hRet = g_pD3DDevice->CreateCubeTexture(dwWidth, dwMipMapLevels, 0, + PCFormat, D3DPOOL_SYSTEMMEM, &pIntermediateHostCubeTexture, + nullptr + ); + } + + if (hRet != D3D_OK) { + CxbxKrnlCleanup("CreateCubeTexture Failed!\n\nError: \nDesc: "/*, + DXGetErrorString(hRet), DXGetErrorDescription(hRet)*/); + } + + SetHostCubeTexture(pResource, pNewHostCubeTexture, iTextureStage); + // TODO : Cube face surfaces can be used as a render-target, + // so we need to associate host surfaces to each surface of this cube texture + // However, we can't do it here: On Xbox, a new Surface is created on every call to + // GetCubeMapSurface, so it needs to be done at surface conversion time by looking up + // the parent CubeTexture + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %s (0x%.08X, 0x%.08X)", + ResourceTypeName, pResource, pNewHostCubeTexture); + break; + } + } // switch XboxResourceType + + // If this resource is a render target or depth stencil, don't attempt to lock/copy it as it won't work anyway + // In this case, we simply return + if (D3DUsage & D3DUSAGE_RENDERTARGET || D3DUsage & D3DUSAGE_DEPTHSTENCIL) { + return; + } + + DWORD D3DLockFlags = D3DLOCK_NOSYSLOCK; + + DWORD dwCubeFaceOffset = 0; + DWORD dwCubeFaceSize = 0; + D3DCUBEMAP_FACES last_face = (bCubemap) ? D3DCUBEMAP_FACE_NEGATIVE_Z : D3DCUBEMAP_FACE_POSITIVE_X; + for (int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= last_face; face++) { + // As we iterate through mipmap levels, we'll adjust the source resource offset + DWORD dwMipOffset = 0; + DWORD dwMipWidth = dwWidth; + DWORD dwMipHeight = dwHeight; + DWORD dwMipDepth = dwDepth; + DWORD dwMipRowPitch = dwRowPitch; + DWORD dwSrcSlicePitch = dwMipRowPitch * dwMipHeight; // TODO + + for (unsigned int mipmap_level = 0; mipmap_level < dwMipMapLevels; mipmap_level++) { + + // Calculate size of this mipmap level + DWORD dwMipSize = dwMipRowPitch * dwMipHeight; + if (bCompressed) { + dwMipSize /= 4; + } + + // Lock the host resource + D3DLOCKED_RECT LockedRect = {}; + D3DLOCKED_BOX LockedBox = {}; + + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: + hRet = pNewHostSurface->LockRect(&LockedRect, nullptr, D3DLockFlags); + break; + case XTL::X_D3DRTYPE_VOLUME: + hRet = pNewHostVolume->LockBox(&LockedBox, nullptr, D3DLockFlags); + break; + case XTL::X_D3DRTYPE_TEXTURE: + hRet = pIntermediateHostTexture->LockRect(mipmap_level, &LockedRect, nullptr, D3DLockFlags); + break; + case XTL::X_D3DRTYPE_VOLUMETEXTURE: + hRet = pIntermediateHostVolumeTexture->LockBox(mipmap_level, &LockedBox, nullptr, D3DLockFlags); + break; + case XTL::X_D3DRTYPE_CUBETEXTURE: + hRet = pIntermediateHostCubeTexture->LockRect((D3DCUBEMAP_FACES)face, mipmap_level, &LockedRect, nullptr, D3DLockFlags); + break; + default: + assert(false); + } // switch XboxResourceType + + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Locking host %s failed!", ResourceTypeName); + continue; // This often happens on depth-stencil surfaces - let's ignore their copies for now + } + + // Determine destination buffer attributes + uint8_t *pDst; + DWORD dwDstRowPitch; + DWORD dwDstSlicePitch; + + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_VOLUME: + case XTL::X_D3DRTYPE_VOLUMETEXTURE: + pDst = (uint8_t *)LockedBox.pBits; + dwDstRowPitch = LockedBox.RowPitch; + dwDstSlicePitch = LockedBox.SlicePitch; + break; + default: + pDst = (uint8_t *)LockedRect.pBits; + dwDstRowPitch = LockedRect.Pitch; + dwDstSlicePitch = 0; + } + + uint8_t *pSrc = (uint8_t *)VirtualAddr + dwMipOffset; + + // Do we need to convert to ARGB? + if (bConvertToARGB) { + EmuLog(LOG_LEVEL::DEBUG, "Unsupported texture format, expanding to D3DFMT_A8R8G8B8"); + + // Convert a row at a time, using a libyuv-like callback approach : + if (!ConvertD3DTextureToARGBBuffer( + X_Format, + pSrc, dwMipWidth, dwMipHeight, dwMipRowPitch, dwSrcSlicePitch, + pDst, dwDstRowPitch, dwDstSlicePitch, + dwDepth, + iTextureStage)) { + CxbxKrnlCleanup("Unhandled conversion!"); + } + } + else if (bSwizzled) { + // First we need to unswizzle the texture data + EmuUnswizzleBox( + pSrc, dwMipWidth, dwMipHeight, dwMipDepth, + dwBPP, + pDst, dwDstRowPitch, dwDstSlicePitch + ); + } + else if (bCompressed) { + memcpy(pDst, pSrc, dwMipSize); + } + else { + /* TODO : // Let DirectX convert the surface (including palette formats) : + if(!EmuXBFormatRequiresConversionToARGB) { + D3DXLoadSurfaceFromMemory( + GetHostSurface(pResource), + nullptr, // no destination palette + &destRect, + pSrc, // Source buffer + dwMipPitch, // Source pitch + g_pXbox_Palette_Data, + &SrcRect, + D3DX_DEFAULT, // D3DX_FILTER_NONE, + 0 // No ColorKey? + ); + } else { + */ + if ((dwDstRowPitch == dwMipRowPitch) && (dwMipRowPitch == dwMipWidth * dwBPP)) { + memcpy(pDst, pSrc, dwMipSize); + } + else { + for (DWORD v = 0; v < dwMipHeight; v++) { + memcpy(pDst, pSrc, dwMipWidth * dwBPP); + pDst += dwDstRowPitch; + pSrc += dwMipRowPitch; + } + } + } + + // Unlock the host resource + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: + hRet = pNewHostSurface->UnlockRect(); + break; + case XTL::X_D3DRTYPE_VOLUME: + hRet = pNewHostVolume->UnlockBox(); + break; + case XTL::X_D3DRTYPE_TEXTURE: + hRet = pIntermediateHostTexture->UnlockRect(mipmap_level); + break; + case XTL::X_D3DRTYPE_VOLUMETEXTURE: + hRet = pIntermediateHostVolumeTexture->UnlockBox(mipmap_level); + break; + case XTL::X_D3DRTYPE_CUBETEXTURE: + hRet = pIntermediateHostCubeTexture->UnlockRect((D3DCUBEMAP_FACES)face, mipmap_level); + break; + default: + assert(false); + } + + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Unlocking host %s failed!", ResourceTypeName); + } + + if (face == D3DCUBEMAP_FACE_POSITIVE_X) { + dwCubeFaceSize += dwDepth * dwMipSize; + } + + // Calculate the next mipmap level dimensions + dwMipOffset += dwMipSize; + if (dwMipWidth > dwMinSize) { + dwMipWidth /= 2; + dwMipRowPitch /= 2; + } + + if (dwMipHeight > dwMinSize) { + dwMipHeight /= 2; + } + + if (dwMipDepth > 1) { + dwMipDepth /= 2; + } + } // for mipmap levels + + if (face == D3DCUBEMAP_FACE_POSITIVE_X) { + dwCubeFaceSize = ROUND_UP(dwCubeFaceSize, X_D3DTEXTURE_CUBEFACE_ALIGNMENT); + } + + dwCubeFaceOffset += dwCubeFaceSize; + } // for cube faces + + + // Copy from the intermediate resource to the final host resource + // This is necessary because CopyRects/StretchRects only works on resources in the DEFAULT pool + // But resources in this pool are not lockable: We must use UpdateSurface/UpdateTexture instead! + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: + case XTL::X_D3DRTYPE_VOLUME: + // We didn't use a copy for Surfaces or Volumes + break; + case XTL::X_D3DRTYPE_TEXTURE: + g_pD3DDevice->UpdateTexture(pIntermediateHostTexture, pNewHostTexture); + pIntermediateHostTexture->Release(); + break; + case XTL::X_D3DRTYPE_VOLUMETEXTURE: + g_pD3DDevice->UpdateTexture(pIntermediateHostVolumeTexture, pNewHostVolumeTexture); + pIntermediateHostVolumeTexture->Release(); + break; + case XTL::X_D3DRTYPE_CUBETEXTURE: + g_pD3DDevice->UpdateTexture(pIntermediateHostCubeTexture, pNewHostCubeTexture); + pIntermediateHostCubeTexture->Release(); + break; + default: + assert(false); + } + + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Updating host %s failed!", ResourceTypeName); + } + + // Debug resource dumping +//#define _DEBUG_DUMP_TEXTURE_REGISTER "D:\\" +#ifdef _DEBUG_DUMP_TEXTURE_REGISTER + bool bDumpConvertedTextures = true; // TODO : Make this a runtime changeable setting + if (bDumpConvertedTextures) { + char szFilePath[MAX_PATH]; + + switch (XboxResourceType) { + case XTL::X_D3DRTYPE_SURFACE: { + static int dwDumpSurface = 0; + sprintf(szFilePath, _DEBUG_DUMP_TEXTURE_REGISTER "%.03d-Surface%.03d.dds", X_Format, dwDumpSurface++); + D3DXSaveSurfaceToFileA(szFilePath, D3DXIFF_DDS, pNewHostSurface, nullptr, nullptr); + break; + } + case XTL::X_D3DRTYPE_VOLUME: { + // TODO + break; + } + case XTL::X_D3DRTYPE_TEXTURE: { + static int dwDumpTexure = 0; + sprintf(szFilePath, _DEBUG_DUMP_TEXTURE_REGISTER "%.03d-Texture%.03d.dds", X_Format, dwDumpTexure++); + D3DXSaveTextureToFileA(szFilePath, D3DXIFF_DDS, pNewHostTexture, nullptr); + break; + } + case XTL::X_D3DRTYPE_VOLUMETEXTURE: { + // TODO + break; + } + case XTL::X_D3DRTYPE_CUBETEXTURE: { + static int dwDumpCubeTexture = 0; + for (unsigned int face = D3DCUBEMAP_FACE_POSITIVE_X; face <= D3DCUBEMAP_FACE_NEGATIVE_Z; face++) { + IDirect3DSurface *pSurface; + if (D3D_OK == pNewHostCubeTexture->GetCubeMapSurface((D3DCUBEMAP_FACES)face, 0, &pSurface)) { + sprintf(szFilePath, _DEBUG_DUMP_TEXTURE_REGISTER "%.03d-CubeTexure%.03d-%d.dds", X_Format, dwDumpCubeTexture, face); + D3DXSaveSurfaceToFileA(szFilePath, D3DXIFF_DDS, pSurface, nullptr, nullptr); + pSurface->Release(); + } + } + dwDumpCubeTexture++; + break; + } + } // switch XboxResourceType + } +#endif + + break; + } + + // case X_D3DRTYPE_VERTEXBUFFER: { break; } // TODO + + // case X_D3DRTYPE_INDEXBUFFER: { break; } // TODO + + case XTL::X_D3DRTYPE_PUSHBUFFER: { + XTL::X_D3DPushBuffer *pPushBuffer = (XTL::X_D3DPushBuffer*)pResource; + + // create push buffer + dwSize = g_VMManager.QuerySize(VirtualAddr); + if (dwSize == 0) { + // TODO: once this is known to be working, remove the warning + EmuLog(LOG_LEVEL::WARNING, "Push buffer allocation size unknown"); + pPushBuffer->Lock = X_D3DRESOURCE_LOCK_FLAG_NOSIZE; + break; + } + + EmuLog(LOG_LEVEL::DEBUG, "CreateHostResource : Successfully created %S (0x%.08X, 0x%.08X, 0x%.08X)", ResourceTypeName, pResource->Data, pPushBuffer->Size, pPushBuffer->AllocationSize); + break; + } + + //case X_D3DRTYPE_PALETTE: { break; } + + case XTL::X_D3DRTYPE_FIXUP: { + XTL::X_D3DFixup *pFixup = (XTL::X_D3DFixup*)pResource; + + EmuLog(LOG_LEVEL::WARNING, "X_D3DRTYPE_FIXUP is not yet supported\n" + "0x%.08X (pFixup->Common) \n" + "0x%.08X (pFixup->Data) \n" + "0x%.08X (pFixup->Lock) \n" + "0x%.08X (pFixup->Run) \n" + "0x%.08X (pFixup->Next) \n" + "0x%.08X (pFixup->Size) \n", pFixup->Common, pFixup->Data, pFixup->Lock, pFixup->Run, pFixup->Next, pFixup->Size); + break; + } + } // switch XboxResourceType +} + +// ****************************************************************** +// * patch: D3DDevice_EnableOverlay +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_EnableOverlay) +( + BOOL Enable +) +{ + LOG_FUNC_ONE_ARG(Enable); + + // The Xbox D3DDevice_EnableOverlay call merely resets the active + // NV2A overlay state, it doesn't actually enable or disable anything. + // Thus, we should just reset our overlay state here too. A title will + // show new overlay data via D3DDevice_UpdateOverlay (see below). + g_OverlayProxy = {}; +} + +// ****************************************************************** +// * patch: D3DDevice_UpdateOverlay +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_UpdateOverlay) +( + X_D3DSurface *pSurface, + CONST RECT *SrcRect, + CONST RECT *DstRect, + BOOL EnableColorKey, + D3DCOLOR ColorKey +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pSurface) + LOG_FUNC_ARG(SrcRect) + LOG_FUNC_ARG(DstRect) + LOG_FUNC_ARG(EnableColorKey) + LOG_FUNC_ARG(ColorKey) + LOG_FUNC_END; + + // Reset and remember the overlay arguments, so our D3DDevice_Swap patch + // can correctly show this overlay surface data. + g_OverlayProxy = {}; + if (pSurface) { + g_OverlayProxy.Surface = *pSurface; + if (SrcRect) + g_OverlayProxy.SrcRect = *SrcRect; + + if (DstRect) + g_OverlayProxy.DstRect = *DstRect; + + g_OverlayProxy.EnableColorKey = EnableColorKey; + g_OverlayProxy.ColorKey = ColorKey; + // Update overlay if present was not called since the last call to + // EmuD3DDevice_UpdateOverlay. + if (g_OverlaySwap != g_Xbox_SwapData.Swap - 1) { + EMUPATCH(D3DDevice_Swap)(CXBX_SWAP_PRESENT_FORWARD); + } + + g_OverlaySwap = g_Xbox_SwapData.Swap; + } +} + +// ****************************************************************** +// * patch: D3DDevice_GetOverlayUpdateStatus +// ****************************************************************** +BOOL WINAPI XTL::EMUPATCH(D3DDevice_GetOverlayUpdateStatus)() +{ + LOG_FUNC(); + + LOG_UNIMPLEMENTED(); + + // TODO: Actually check for update status + return TRUE; +} + +// ****************************************************************** +// * patch: D3DDevice_BlockUntilVerticalBlank +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)() +{ + LOG_FUNC(); + + std::unique_lock lk(g_VBConditionMutex); + g_VBConditionVariable.wait(lk); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVerticalBlankCallback +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVerticalBlankCallback) +( + X_D3DVBLANKCALLBACK pCallback +) +{ + LOG_FUNC_ONE_ARG(pCallback); + + g_pXbox_VerticalBlankCallback = pCallback; +} + + +// ****************************************************************** +// * patch: D3DDevice_SetRenderState_Simple +// ****************************************************************** +VOID __fastcall XTL::EMUPATCH(D3DDevice_SetRenderState_Simple) +( + DWORD Method, + DWORD Value +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Method) + LOG_FUNC_ARG(Value) + LOG_FUNC_END; + + XB_TRMP(D3DDevice_SetRenderState_Simple)(Method, Value); + + // Fetch the RenderState conversion info for the given input + int XboxRenderStateIndex = -1; + for (int i = X_D3DRS_FIRST; i <= X_D3DRS_LAST; i++) { + if (GetDxbxRenderStateInfo(i).M == PUSH_METHOD(Method)) { + XboxRenderStateIndex = i; + break; + } + } + + // If we could not map it, log and return + if (XboxRenderStateIndex == -1) { + EmuLog(LOG_LEVEL::WARNING, "RenderState_Simple(0x%.08X (%s), 0x%.08X) could not be found in RenderState table", Method, GetDxbxRenderStateInfo(XboxRenderStateIndex).S, Value); + return; + } + + EmuLog(LOG_LEVEL::DEBUG, "RenderState_Simple: %s = 0x%08X", GetDxbxRenderStateInfo(XboxRenderStateIndex).S, Value); + + XboxRenderStates.SetXboxRenderState(XboxRenderStateIndex, Value); +} + +// LTCG specific D3DDevice_SetTransform function... +// This uses a custom calling convention where parameter is passed in EAX, EDX +VOID __stdcall XTL::EMUPATCH(D3DDevice_SetTransform_0) +( +) +{ + D3DTRANSFORMSTATETYPE param1; + CONST D3DMATRIX *param2; + + __asm { + mov param1, eax + mov param2, edx + } + + return EMUPATCH(D3DDevice_SetTransform)(param1, param2); +} + +// ****************************************************************** +// * patch: D3DDevice_SetTransform +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetTransform) +( + D3DTRANSFORMSTATETYPE State, + CONST D3DMATRIX *pMatrix +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(State) + LOG_FUNC_ARG(pMatrix) + LOG_FUNC_END; + + /* + printf("pMatrix (%d)\n", State); + printf("{\n"); + printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_11, pMatrix->_12, pMatrix->_13, pMatrix->_14); + printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_21, pMatrix->_22, pMatrix->_23, pMatrix->_24); + printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_31, pMatrix->_32, pMatrix->_33, pMatrix->_34); + printf(" %.08f,%.08f,%.08f,%.08f\n", pMatrix->_41, pMatrix->_42, pMatrix->_43, pMatrix->_44); + printf("}\n"); + + if(State == 6 && (pMatrix->_11 == 1.0f) && (pMatrix->_22 == 1.0f) && (pMatrix->_33 == 1.0f) && (pMatrix->_44 == 1.0f)) + { + g_bSkipPush = TRUE; + printf("SkipPush ON\n"); + } + else + { + g_bSkipPush = FALSE; + printf("SkipPush OFF\n"); + } + */ + + State = EmuXB2PC_D3DTS(State); + + HRESULT hRet = g_pD3DDevice->SetTransform(State, pMatrix); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTransform"); +} + +// ****************************************************************** +// * patch: D3DDevice_MultiplyTransform +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_MultiplyTransform) +( + D3DTRANSFORMSTATETYPE State, + CONST D3DMATRIX *pMatrix +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(State) + LOG_FUNC_ARG(pMatrix) + LOG_FUNC_END; + + State = EmuXB2PC_D3DTS(State); + + HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform"); +} + + +// ****************************************************************** +// * patch: D3DDevice_GetTransform +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetTransform) +( + D3DTRANSFORMSTATETYPE State, + D3DMATRIX *pMatrix +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(State) + LOG_FUNC_ARG(pMatrix) + LOG_FUNC_END; + + State = EmuXB2PC_D3DTS(State); + + HRESULT hRet = g_pD3DDevice->GetTransform(State, pMatrix); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetTransform"); +} + +// ****************************************************************** +// * patch: Lock2DSurface +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(Lock2DSurface) +( + X_D3DPixelContainer *pPixelContainer, + D3DCUBEMAP_FACES FaceType, + UINT Level, + D3DLOCKED_RECT *pLockedRect, + RECT *pRect, + DWORD Flags + ) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pPixelContainer) + LOG_FUNC_ARG(FaceType) + LOG_FUNC_ARG(Level) + LOG_FUNC_ARG(pLockedRect) + LOG_FUNC_ARG(pRect) + LOG_FUNC_ARG(Flags) + LOG_FUNC_END; + + + // Pass through to the Xbox implementation of this function + XB_TRMP(Lock2DSurface)(pPixelContainer, FaceType, Level, pLockedRect, pRect, Flags); + + // Mark the resource as modified + ForceResourceRehash(pPixelContainer); +} + + +// ****************************************************************** +// * patch: Lock3DSurface +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(Lock3DSurface) +( + X_D3DPixelContainer *pPixelContainer, + UINT Level, + D3DLOCKED_BOX *pLockedVolume, + D3DBOX *pBox, + DWORD Flags + ) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pPixelContainer) + LOG_FUNC_ARG(Level) + LOG_FUNC_ARG(pLockedVolume) + LOG_FUNC_ARG(pBox) + LOG_FUNC_ARG(Flags) + LOG_FUNC_END; + + // Pass through to the Xbox implementation of this function + XB_TRMP(Lock3DSurface)(pPixelContainer, Level, pLockedVolume, pBox, Flags); + + // Mark the resource as modified + ForceResourceRehash(pPixelContainer); +} + + +// LTCG specific D3DDevice_SetStreamSource function... +// This uses a custom calling convention where parameter is passed in EBX, EAX +// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX. +// Test-case: Ninja Gaiden +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource_4) +( + UINT Stride +) +{ + UINT StreamNumber; + X_D3DVertexBuffer *pStreamData; + + __asm { + mov pStreamData, ebx + mov StreamNumber, eax + } + + //LOG_FUNC_BEGIN + // LOG_FUNC_ARG(StreamNumber) + // LOG_FUNC_ARG(pStreamData) + // LOG_FUNC_ARG(Stride) + // LOG_FUNC_END; + EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetStreamSource_4(StreamNumber : %08X pStreamData : %08X Stride : %08X);", StreamNumber, pStreamData, Stride); + + CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); + + // TODO : Forward to Xbox implementation + // This should stop us having to patch GetStreamSource! + //XB_TRMP(D3DDevice_SetStreamSource_4)(StreamNumber, pStreamData, Stride); +} + +// This uses a custom calling convention where parameter is passed in EAX +// TODO: XB_trampoline plus Log function is not working due lost parameter in EAX. +// Test-case: Superman - The Man Of Steel +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource_8) +( + X_D3DVertexBuffer *pStreamData, + UINT Stride +) +{ + UINT StreamNumber; + + __asm { + mov StreamNumber, eax + } + + //LOG_FUNC_BEGIN + // LOG_FUNC_ARG(StreamNumber) + // LOG_FUNC_ARG(pStreamData) + // LOG_FUNC_ARG(Stride) + // LOG_FUNC_END; + EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetStreamSource_8(StreamNumber : %08X pStreamData : %08X Stride : %08X);", StreamNumber, pStreamData, Stride); + + CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); + + // TODO : Forward to Xbox implementation + // This should stop us having to patch GetStreamSource! + //XB_TRMP(D3DDevice_SetStreamSource_8)(pStreamData, Stride); +} + +// ****************************************************************** +// * patch: D3DDevice_SetStreamSource +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource) +( + UINT StreamNumber, + X_D3DVertexBuffer *pStreamData, + UINT Stride +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(StreamNumber) + LOG_FUNC_ARG(pStreamData) + LOG_FUNC_ARG(Stride) + LOG_FUNC_END; + + CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); + + // Forward to Xbox implementation + // This should stop us having to patch GetStreamSource! + XB_TRMP(D3DDevice_SetStreamSource)(StreamNumber, pStreamData, Stride); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShader) +( + DWORD Handle +) +{ + LOG_FUNC_ONE_ARG(Handle); + + CxbxImpl_SetVertexShader(Handle); + + UpdateViewPortOffsetAndScaleConstants(); +} + +// TODO : Move to own file +constexpr unsigned int IndicesPerPage = PAGE_SIZE / sizeof(INDEX16); +constexpr unsigned int InputQuadsPerPage = ((IndicesPerPage * VERTICES_PER_QUAD) / VERTICES_PER_TRIANGLE) / TRIANGLES_PER_QUAD; + +// TODO : Move to own file +// Called by CxbxDrawPrimitiveUP (indirectly by D3DDevice_DrawVerticesUP, +// EmuExecutePushBufferRaw and EmuFlushIVB) when PrimitiveType == X_D3DPT_QUADLIST. +// Emulated by calling g_pD3DDevice->DrawIndexedPrimitiveUP with index data that maps +// quads to triangles. This function creates the index buffer that is needed for this; +// For every quad that must be drawn, we generate indices for two triangles. +// Note, that the resulting index data can be re-used for later comparable draw calls +// and only needs to grow when current length doesn't suffices for a larger draw. +INDEX16 *CxbxAssureQuadListIndexData(UINT NrOfQuadIndices) +{ + if (g_QuadToTriangleIndexData_Size < NrOfQuadIndices) + { + g_QuadToTriangleIndexData_Size = RoundUp(NrOfQuadIndices, InputQuadsPerPage); + UINT NrOfTriangleIndices = QuadToTriangleVertexCount(g_QuadToTriangleIndexData_Size); + if (g_pQuadToTriangleIndexData != nullptr) { + free(g_pQuadToTriangleIndexData); + } + + g_pQuadToTriangleIndexData = (INDEX16 *)malloc(NrOfTriangleIndices * sizeof(INDEX16)); + CxbxConvertQuadListToTriangleListIndices(nullptr, NrOfTriangleIndices, g_pQuadToTriangleIndexData); + } + + return g_pQuadToTriangleIndexData; +} + +// TODO : Move to own file +// Makes a D3D IndexBuffer active that contains quadlist-to-trianglelist indices. +// Uses CxbxAssureQuadListIndexData to populate the index buffer with. +// Note, that the resulting index buffer can be re-used for later comparable draw calls +// and only needs to grow when current length doesn't sufficesw for a larger draw. +void CxbxAssureQuadListD3DIndexBuffer(UINT NrOfQuadIndices) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + HRESULT hRet; + + if (g_QuadToTriangleHostIndexBuffer_Size < NrOfQuadIndices) + { + // Round the number of indices up so we'll allocate whole pages + g_QuadToTriangleHostIndexBuffer_Size = RoundUp(NrOfQuadIndices, InputQuadsPerPage); + UINT NrOfTriangleIndices = QuadToTriangleVertexCount(g_QuadToTriangleHostIndexBuffer_Size); // 4 > 6 + + // Create a new native index buffer of the above determined size : + if (g_pQuadToTriangleHostIndexBuffer != nullptr) { + g_pQuadToTriangleHostIndexBuffer->Release(); // test-case : XDK PointSprites + g_pQuadToTriangleHostIndexBuffer = nullptr; + } + + // Create a new native index buffer of the above determined size : + g_pQuadToTriangleHostIndexBuffer = CxbxCreateIndexBuffer(NrOfTriangleIndices); + if (g_pQuadToTriangleHostIndexBuffer == nullptr) + CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : IndexBuffer Create Failed!"); + + // Put quadlist-to-triangle-list index mappings into this buffer : + INDEX16* pHostIndexBufferData = nullptr; + hRet = g_pQuadToTriangleHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pHostIndexBufferData, D3DLOCK_DISCARD); + DEBUG_D3DRESULT(hRet, "g_pQuadToTriangleHostIndexBuffer->Lock"); + if (pHostIndexBufferData == nullptr) + CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : Could not lock index buffer!"); + + memcpy(pHostIndexBufferData, CxbxAssureQuadListIndexData(NrOfQuadIndices), NrOfTriangleIndices * sizeof(INDEX16)); + + g_pQuadToTriangleHostIndexBuffer->Unlock(); + } + + // Activate the new native index buffer : + hRet = g_pD3DDevice->SetIndices(g_pQuadToTriangleHostIndexBuffer); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); + + if (FAILED(hRet)) + CxbxKrnlCleanup("CxbxAssureQuadListD3DIndexBuffer : SetIndices Failed!"); // +DxbxD3DErrorString(hRet)); +} + +// TODO : Move to own file +// Calls SetIndices with a separate index-buffer, that's populated with the supplied indices. +void CxbxDrawIndexedClosingLine(INDEX16 LowIndex, INDEX16 HighIndex) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + HRESULT hRet; + + if (g_pClosingLineLoopHostIndexBuffer == nullptr) { + g_pClosingLineLoopHostIndexBuffer = CxbxCreateIndexBuffer(VERTICES_PER_LINE); + if (g_pClosingLineLoopHostIndexBuffer == nullptr) + CxbxKrnlCleanup("Unable to create g_pClosingLineLoopHostIndexBuffer for D3DPT_LINELOOP emulation"); + } + + INDEX16 *pCxbxClosingLineLoopIndexBufferData = nullptr; + hRet = g_pClosingLineLoopHostIndexBuffer->Lock(0, /*entire SizeToLock=*/0, (D3DLockData **)&pCxbxClosingLineLoopIndexBufferData, D3DLOCK_DISCARD); + DEBUG_D3DRESULT(hRet, "g_pClosingLineLoopHostIndexBuffer->Lock"); + + // Set the indices for the two VERTICES_PER_LINE : + pCxbxClosingLineLoopIndexBufferData[0] = LowIndex; + pCxbxClosingLineLoopIndexBufferData[1] = HighIndex; + + hRet = g_pClosingLineLoopHostIndexBuffer->Unlock(); + DEBUG_D3DRESULT(hRet, "g_pClosingLineLoopHostIndexBuffer->Unlock"); + + hRet = g_pD3DDevice->SetIndices(g_pClosingLineLoopHostIndexBuffer); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetIndices"); + + hRet = g_pD3DDevice->DrawIndexedPrimitive( + /*PrimitiveType=*/D3DPT_LINELIST, + /*BaseVertexIndex=*/0, // Note : Callers must apply BaseVertexIndex to the LowIndex and HighIndex argument values + /*MinVertexIndex=*/LowIndex, + /*NumVertices=*/VERTICES_PER_LINE, + /*startIndex=*/0, + /*primCount=*/1 + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(CxbxDrawIndexedClosingLine)"); + + g_dwPrimPerFrame++; +} + +// TODO : Move to own file +void CxbxDrawIndexedClosingLineUP(INDEX16 LowIndex, INDEX16 HighIndex, void *pHostVertexStreamZeroData, UINT uiHostVertexStreamZeroStride) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + +#if 0 + // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than + // via CxbxDrawIndexedClosingLine, by drawing two indices via DrawIndexedPrimitiveUP. + // (This is simpler because we use just indices and don't need to copy the vertices.) + INDEX16 CxbxClosingLineIndices[2] = { LowIndex, HighIndex }; + + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( + /*PrimitiveType=*/D3DPT_LINELIST, + /*MinVertexIndex=*/LowIndex, + /*NumVertices=*/(HighIndex - LowIndex) + 1, + /*PrimitiveCount=*/1, + /*pIndexData=*/CxbxClosingLineIndices, + /*IndexDataFormat=*/D3DFMT_INDEX16, + pHostVertexStreamZeroData, + uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP(CxbxDrawIndexedClosingLineUP)"); +#else // TODO : If NumVertices is high, performance might suffer - drawing a copy of the two vertices could be faster + // Since we can use pHostVertexStreamZeroData here, we can close the line simpler than + // via CxbxDrawIndexedClosingLine, by drawing two vertices via DrawPrimitiveUP. + // (This is simpler because we just copy the vertices, and don't need a separate index buffer.) + uint8_t VertexData[512]; assert(512 >= 2 * uiHostVertexStreamZeroStride); + uint8_t *FirstVertex = (uint8_t *)pHostVertexStreamZeroData + (LowIndex * uiHostVertexStreamZeroStride); + uint8_t *SecondVertex = (uint8_t *)pHostVertexStreamZeroData + (HighIndex * uiHostVertexStreamZeroStride); + + memcpy(VertexData, FirstVertex, uiHostVertexStreamZeroStride); + memcpy(VertexData + uiHostVertexStreamZeroStride, SecondVertex, uiHostVertexStreamZeroStride); + + HRESULT hRet = g_pD3DDevice->DrawPrimitiveUP( + /*PrimitiveType=*/D3DPT_LINELIST, + /*PrimitiveCount=*/1, + /*pVertexStreamZeroData=*/VertexData, + uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitiveUP(CxbxDrawIndexedClosingLineUP)"); +#endif + + g_dwPrimPerFrame++; +} + +// Requires assigned pXboxIndexData +// Called by D3DDevice_DrawIndexedVertices and EmuExecutePushBufferRaw (twice) +void CxbxDrawIndexed(CxbxDrawContext &DrawContext) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + assert(DrawContext.dwStartVertex == 0); + assert(DrawContext.pXboxIndexData != nullptr); + assert(DrawContext.dwVertexCount > 0); // TODO : If this fails, make responsible callers do an early-exit + assert(IsValidCurrentShader()); + + bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST); + ConvertedIndexBuffer& CacheEntry = CxbxUpdateActiveIndexBuffer(DrawContext.pXboxIndexData, DrawContext.dwVertexCount, bConvertQuadListToTriangleList); + // Note : CxbxUpdateActiveIndexBuffer calls SetIndices + + // Set LowIndex and HighIndex *before* VerticesInBuffer gets derived + DrawContext.LowIndex = CacheEntry.LowIndex; + DrawContext.HighIndex = CacheEntry.HighIndex; + + VertexBufferConverter.Apply(&DrawContext); // Sets dwHostPrimitiveCount + + INT BaseVertexIndex = DrawContext.dwBaseVertexIndex; + UINT primCount = DrawContext.dwHostPrimitiveCount; + if (bConvertQuadListToTriangleList) { + if (DrawContext.dwVertexCount == 4) + LOG_TEST_CASE("X_D3DPT_QUADLIST (single quad)"); // breakpoint location + else + LOG_TEST_CASE("X_D3DPT_QUADLIST"); + + if (BaseVertexIndex > 0) + LOG_TEST_CASE("X_D3DPT_QUADLIST (BaseVertexIndex > 0)"); + + // Convert draw arguments from quads to triangles : + // Note : BaseVertexIndex must NOT be mapped through QuadToTriangleVertexCount(BaseVertexIndex)! + primCount *= TRIANGLES_PER_QUAD; + } + + // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers + // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, NumVertices and StartIndex arguments. + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( + /* PrimitiveType = */EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), + BaseVertexIndex, + /* MinVertexIndex = */CacheEntry.LowIndex, + /* NumVertices = */(CacheEntry.HighIndex - CacheEntry.LowIndex) + 1, + /* startIndex = DrawContext.dwStartVertex = */0, + primCount); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive"); + + g_dwPrimPerFrame += primCount; + if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { + // Close line-loops using a final single line, drawn from the end to the start vertex + if (BaseVertexIndex == 0) { + LOG_TEST_CASE("X_D3DPT_LINELOOP"); + } else { + LOG_TEST_CASE("X_D3DPT_LINELOOP (BaseVertexIndex > 0)"); + // Note : This is to find test-cases for the BaseVertexIndex addition below: + } + // Read the end and start index from the supplied index data + INDEX16 LowIndex = BaseVertexIndex + DrawContext.pXboxIndexData[0]; + INDEX16 HighIndex = BaseVertexIndex + DrawContext.pXboxIndexData[DrawContext.dwHostPrimitiveCount]; + // If needed, swap so highest index is higher than lowest (duh) + if (HighIndex < LowIndex) { + std::swap(HighIndex, LowIndex); + } + + // Draw the closing line using a helper function (which will SetIndices) + CxbxDrawIndexedClosingLine(LowIndex, HighIndex); + // NOTE : We don't restore the previously active index buffer + } +} + +// TODO : Move to own file +// Drawing function specifically for rendering Xbox draw calls supplying a 'User Pointer'. +// Called by D3DDevice_DrawVerticesUP, EmuExecutePushBufferRaw and EmuFlushIVB +void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + assert(DrawContext.dwStartVertex == 0); + assert(DrawContext.pXboxVertexStreamZeroData != xbnullptr); + assert(DrawContext.uiXboxVertexStreamZeroStride > 0); + assert(DrawContext.dwBaseVertexIndex == 0); // No IndexBase under Draw*UP + + VertexBufferConverter.Apply(&DrawContext); + if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_QUADLIST) { + // LOG_TEST_CASE("X_D3DPT_QUADLIST"); // test-case : X-Marbles and XDK Sample PlayField + // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : + INDEX16 *pIndexData = CxbxAssureQuadListIndexData(DrawContext.dwVertexCount); + // Convert quad vertex-count to triangle vertex count : + UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; + + // Instead of calling WalkIndexBuffer on pQuadToTriangleIndexBuffer, + // we can derive the LowIndex and HighIndexes ourselves here + INDEX16 LowIndex = 0; + INDEX16 HighIndex = (INDEX16)(DrawContext.dwVertexCount - 1); + + // Draw indexed triangles instead of quads + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( + /*PrimitiveType=*/D3DPT_TRIANGLELIST, + /*MinVertexIndex=*/LowIndex, + /*NumVertexIndices=*/(HighIndex - LowIndex) + 1, + PrimitiveCount, + pIndexData, + /*IndexDataFormat=*/D3DFMT_INDEX16, + DrawContext.pHostVertexStreamZeroData, + DrawContext.uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitieUP(X_D3DPT_QUADLIST)"); + + g_dwPrimPerFrame += PrimitiveCount; + } + else { + // Primitives other than X_D3DPT_QUADLIST can be drawn using one DrawPrimitiveUP call : + HRESULT hRet = g_pD3DDevice->DrawPrimitiveUP( + EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), + DrawContext.dwHostPrimitiveCount, + DrawContext.pHostVertexStreamZeroData, + DrawContext.uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitiveUP"); + + g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; + if (DrawContext.XboxPrimitiveType == XTL::X_D3DPT_LINELOOP) { + // test-case : XDK samples reaching this case : DebugKeyboard, Gamepad, Tiling, ShadowBuffer + // Close line-loops using a final single line, drawn from the end to the start vertex : + CxbxDrawIndexedClosingLineUP( + (INDEX16)0, // LowIndex + (INDEX16)DrawContext.dwHostPrimitiveCount, // HighIndex, + DrawContext.pHostVertexStreamZeroData, + DrawContext.uiHostVertexStreamZeroStride + ); + } + } +} + +IDirect3DBaseTexture* CxbxConvertXboxSurfaceToHostTexture(XTL::X_D3DBaseTexture* pBaseTexture) +{ + LOG_INIT; + + IDirect3DTexture* pNewHostTexture = nullptr; +#if 0 // TODO : Complete, debug and activate (and then cleanup GetHostBaseTexture) + D3DFORMAT PCFormat = D3DFMT_A8B8G8R8; // TODO : Derive from pBaseTexture + + IDirect3DSurface* pHostSurface = GetHostSurface(pBaseTexture); // TODO : Extend this with a texture channel number too, if surfaces send to SetTexture can be paletized format? + + DWORD dwWidth = GetPixelContainerWidth(pBaseTexture); + DWORD dwHeight = GetPixelContainerHeight(pBaseTexture); + UINT dwMipMapLevels = CxbxGetPixelContainerMipMapLevels(pBaseTexture); + + HRESULT hRet = g_pD3DDevice->CreateTexture(dwWidth, dwHeight, dwMipMapLevels, + /*Usage=*/0, PCFormat, D3DPOOL_SYSTEMMEM, &pNewHostTexture, nullptr); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateTexture (in CxbxConvertXboxSurfaceToHostTexture)"); + if (hRet != D3D_OK) { + CxbxKrnlCleanup("CreateTexture Failed!\n\nError: \nDesc: "/*, + DXGetErrorString(hRet), DXGetErrorDescription(hRet)*/); + } + + IDirect3DSurface* pHostTextureSurface = nullptr; + hRet = pNewHostTexture->GetSurfaceLevel(/*Level=*/0, &pHostTextureSurface); + DEBUG_D3DRESULT(hRet, "pHostBaseTexture->GetSurfaceLevel"); + + if (hRet == D3D_OK) { + hRet = D3DXLoadSurfaceFromSurface(pHostTextureSurface, nullptr, nullptr, pHostSurface, nullptr, nullptr, D3DX_FILTER_NONE, 0x00000000); + DEBUG_D3DRESULT(hRet, "D3DXLoadSurfaceFromSurface"); + pHostTextureSurface->Release(); + } +#endif + return (IDirect3DBaseTexture*)pNewHostTexture; // return it as a base texture +} + +void EmuUpdateActiveTextureStages() +{ + LOG_INIT; + + for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) + { + XTL::X_D3DBaseTexture *pBaseTexture = g_pXbox_SetTexture[i]; + IDirect3DBaseTexture *pHostBaseTexture = nullptr; + bool bNeedRelease = false; + + if (pBaseTexture != xbnullptr) { + DWORD Type = GetXboxCommonResourceType(pBaseTexture); + switch (Type) { + case X_D3DCOMMON_TYPE_TEXTURE: + pHostBaseTexture = GetHostBaseTexture(pBaseTexture, /*D3DUsage=*/0, i); + break; + case X_D3DCOMMON_TYPE_SURFACE: + // Surfaces can be set in the texture stages, instead of textures + LOG_TEST_CASE("ActiveTexture set to a surface (non-texture) resource"); // Test cases : Burnout, Outrun 2006 + // We must wrap the surface before using it as a texture + pHostBaseTexture = CxbxConvertXboxSurfaceToHostTexture(pBaseTexture); + // Release this texture (after SetTexture) when we succeeded in creating it : + bNeedRelease = pHostBaseTexture != nullptr; + break; + default: + LOG_TEST_CASE("ActiveTexture set to an unhandled resource type!"); + break; + } + } + + HRESULT hRet = g_pD3DDevice->SetTexture(i, pHostBaseTexture); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTexture"); + if (bNeedRelease) { + pHostBaseTexture->Release(); + } + } +} + +void CxbxUpdateNativeD3DResources() +{ + // Before we start, make sure our resource cache stays limited in size + PrunePaletizedTexturesCache(); // TODO : Could we move this to Swap instead? + + EmuUpdateActiveTextureStages(); + + // Some titles set Vertex Shader constants directly via pushbuffers rather than through D3D + // We handle that case by updating any constants that have the dirty flag set on the nv2a. + auto nv2a = g_NV2A->GetDeviceState(); + for(int i = 0; i < X_D3DVS_CONSTREG_COUNT; i++) { + // Skip vOffset and vScale constants, we don't want our values to be overwritten by accident + if (i == X_D3DSCM_RESERVED_CONSTANT_OFFSET_CORRECTED || i == X_D3DSCM_RESERVED_CONSTANT_SCALE_CORRECTED) { + continue; + } + + if (nv2a->pgraph.vsh_constants_dirty[i]) { + g_pD3DDevice->SetVertexShaderConstantF(i, (float*)&nv2a->pgraph.vsh_constants[i][0], 1); + nv2a->pgraph.vsh_constants_dirty[i] = false; + } + } + + // NOTE: Order is important here + // Some Texture States depend on RenderState values (Point Sprites) + // And some Pixel Shaders depend on Texture State values (BumpEnvMat, etc) + XboxRenderStates.Apply(); + XboxTextureStates.Apply(); + + // If Pixel Shaders are not disabled, process them + if (!g_DisablePixelShaders) { + DxbxUpdateActivePixelShader(); + } + +/* TODO : Port these : + DxbxUpdateActiveVertexShader(); + DxbxUpdateActiveTextures(); + DxbxUpdateDeferredStates(); // BeginPush sample shows us that this must come *after* texture update! + DxbxUpdateActiveVertexBufferStreams(); + DxbxUpdateActiveRenderTarget(); +*/ +} + +// This function should be called in thight idle-wait loops. +// It's purpose is to lower CPU cost in such a way that the +// caller will still repond quickly, without actually waiting +// or giving up it's time-slice. +// See https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-yieldprocessor +// and https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-pause-intrinsic +inline void CxbxCPUIdleWait() // TODO : Apply wherever applicable +{ + YieldProcessor(); +} + +// This function indicates whether Cxbx can flush host GPU commands. +bool CxbxCanFlushHostGPU() +{ + return (g_pHostQueryWaitForIdle != nullptr); +} + +// Wait until host GPU finished processing it's command queue +bool CxbxFlushHostGPU() +{ + // The following can only work when host GPU queries are available + if (!CxbxCanFlushHostGPU()) { + // If we can't query host GPU, return failure + return false; + } + + // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/queries + // Add an end marker to the command buffer queue. + // This, so that the next GetData will always have at least one + // final query event to flush out, after which GPU will be done. + g_pHostQueryWaitForIdle->Issue(D3DISSUE_END); + + // Empty the command buffer and wait until host GPU is idle. + while (S_FALSE == g_pHostQueryWaitForIdle->GetData(nullptr, 0, D3DGETDATA_FLUSH)) + CxbxCPUIdleWait(); + + // Signal caller that host GPU has been flushed + return true; +} + +// This function mimicks NV2A software callback events. +// Normally, these would be handled by actual push-buffer +// command handling at the point where they where inserted. +// Since our HLE mostly circumvents the NV2A pushbuffer, +// this function has to be called after 'pushing' functions. +void CxbxHandleXboxCallbacks() +{ + // The following can only work when host GPU queries are available + if (g_pHostQueryCallbackEvent != nullptr) { + // Query whether host GPU encountered a callback event already + if (S_FALSE == g_pHostQueryCallbackEvent->GetData(nullptr, 0, 0)) { + // If not, don't handle callbacks + return; + } + } + + // Process inserted callbacks + while (!g_Xbox_CallbackQueue.empty()) { + // Fetch a callback from the FIFO callback queue + s_Xbox_Callback XboxCallback = g_Xbox_CallbackQueue.front(); + g_Xbox_CallbackQueue.pop(); + + // Differentiate between write and read callbacks + if (XboxCallback.Type == XTL::X_D3DCALLBACK_WRITE) { + // Write callbacks should wait until GPU is idle + if (!CxbxFlushHostGPU()) { + // Host GPU can't be flushed. In the old behaviour, we made the callback anyway + // TODO : Should we keep doing that? + } + } else { + assert(XboxCallback.Type == XTL::X_D3DCALLBACK_READ); + // Should we mimick Read callback old behaviour? + if (g_bHack_DisableHostGPUQueries) { + // Note : Previously, we only processed Write, and ignored Read callbacks + continue; + } else { + // New behaviour does place Read callbacks too + } + } + + // Make the callback + XboxCallback.pCallback(XboxCallback.Context); + } +} + +// On Xbox, this function inserts push-buffer commands that +// will trigger the software handler to perform the callback +// when the GPU processes these commands. +// The type X_D3DCALLBACK_WRITE callbacks are prefixed with an +// wait-for-idle command, but otherwise they're identical. +// (Software handlers are triggered on NV2A via NV097_NO_OPERATION) +void CxbxImpl_InsertCallback +( + XTL::X_D3DCALLBACKTYPE Type, + XTL::X_D3DCALLBACK pCallback, + XTL::DWORD Context +) +{ + if (Type > XTL::X_D3DCALLBACK_WRITE) { + LOG_TEST_CASE("Illegal callback type!"); + return; + } + + if (pCallback == xbnullptr) { + LOG_TEST_CASE("pCallback == xbnullptr!"); + return; + } + + // Should we mimick old behaviour? + if (g_bHack_DisableHostGPUQueries) { + // Mimick old behaviour, in which only the final callback event + // was remembered, by emptying the callback queue entirely : + while (!g_Xbox_CallbackQueue.empty()) { + g_Xbox_CallbackQueue.pop(); + } + } + + // Push this callback's arguments into the callback queue : + s_Xbox_Callback XboxCallback = { pCallback, Type, Context }; + g_Xbox_CallbackQueue.push(XboxCallback); // g_Xbox_CallbackQueue.emplace(pCallback, Type, Context); doesn't compile? + + // Does host supports GPU queries? + if (g_pHostQueryCallbackEvent != nullptr) { + // Insert a callback event on host GPU, + // which will be handled by CxbxHandleXboxCallback + g_pHostQueryCallbackEvent->Issue(D3DISSUE_END); + } +} + +VOID __declspec(noinline) D3DDevice_SetPixelShaderCommon(DWORD Handle) +{ + // Cache the active shader handle + g_pXbox_PixelShader = (XTL::X_PixelShader*)Handle; + + // Copy the Pixel Shader data to our RenderState handler + // This mirrors the fact that unpatched SetPixelShader does the same thing! + // This shouldn't be necessary anymore, but shaders still break if we don't do this + if (g_pXbox_PixelShader != nullptr) { + memcpy(XboxRenderStates.GetPixelShaderRenderStatePointer(), g_pXbox_PixelShader->pPSDef, sizeof(XTL::X_D3DPIXELSHADERDEF) - 3 * sizeof(DWORD)); + XboxRenderStates.SetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES, g_pXbox_PixelShader->pPSDef->PSTextureModes); + } +} + +// LTCG specific D3DDevice_SetPixelShader function... +// This uses a custom calling convention where parameter is passed in EAX +// Test-case: Metal Wolf Chaos +// Test-case: Lord of the Rings: The Third Age +VOID WINAPI D3DDevice_SetPixelShader_0_IMPL +( + DWORD Handle +) +{ + LOG_FUNC_ONE_ARG(Handle); + + // Call the Xbox function to make sure D3D structures get set + __asm { + mov eax, Handle + call XB_TRMP(D3DDevice_SetPixelShader_0) + } + + D3DDevice_SetPixelShaderCommon(Handle); +} + +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPixelShader_0)() +{ + __asm { + push eax + call D3DDevice_SetPixelShader_0_IMPL + ret + } +} + +// ****************************************************************** +// * patch: D3DDevice_SetPixelShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPixelShader) +( + DWORD Handle +) +{ + LOG_FUNC_ONE_ARG(Handle); + + // Call the Xbox function to make sure D3D structures get set + XB_TRMP(D3DDevice_SetPixelShader)(Handle); + + D3DDevice_SetPixelShaderCommon(Handle); +} + +// ****************************************************************** +// * patch: D3DDevice_DrawVertices_4 +// LTCG specific D3DDevice_DrawVertices function... +// This uses a custom calling convention where parameter is passed in ECX, EAX and Stack +// Test Case: Conker +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices_4) +( + X_D3DPRIMITIVETYPE PrimitiveType +) +{ + UINT VertexCount; + UINT StartVertex; + + _asm { + mov VertexCount, eax + mov StartVertex, ecx + } + + EMUPATCH(D3DDevice_DrawVertices)(PrimitiveType, StartVertex, VertexCount); +} + +// ****************************************************************** +// * patch: D3DDevice_DrawVertices +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVertices) +( + X_D3DPRIMITIVETYPE PrimitiveType, + UINT StartVertex, + UINT VertexCount +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(PrimitiveType) + LOG_FUNC_ARG(StartVertex) + LOG_FUNC_ARG(VertexCount) + LOG_FUNC_END; + + // Dxbx Note : In DrawVertices and DrawIndexedVertices, PrimitiveType may not be D3DPT_POLYGON + + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { + LOG_TEST_CASE("Invalid VertexCount"); + return; + } + + // TODO : Call unpatched D3DDevice_SetStateVB(0); + + CxbxUpdateNativeD3DResources(); + if (IsValidCurrentShader()) { + CxbxDrawContext DrawContext = {}; + + DrawContext.XboxPrimitiveType = PrimitiveType; + DrawContext.dwVertexCount = VertexCount; + DrawContext.dwStartVertex = StartVertex; + + VertexBufferConverter.Apply(&DrawContext); + if (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST) { + if (StartVertex == 0) { + //LOG_TEST_CASE("X_D3DPT_QUADLIST (StartVertex == 0)"); // disabled, hit too often + // test-case : ?X-Marbles + // test-case XDK Samples : AlphaFog, AntiAlias, BackBufferScale, BeginPush, Cartoon, TrueTypeFont (?maybe PlayField?) + } else { + LOG_TEST_CASE("X_D3DPT_QUADLIST (StartVertex > 0)"); + // https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/1156 + // test-case : All - Star Baseball '03 + // test-case : Army Men Major Malfunction + // test-case : Big Mutha Truckers + // test-case : BLiNX: the time sweeper + // test-case : Blood Wake + // test-case : Call of Duty: Finest Hour + // test-case : Flight academy + // test-case : FIFA World Cup 2002 + // test-case : GENMA ONIMUSHA + // test-case : Halo - Combat Evolved + // test-case : Harry Potter and the Sorcerer's Stone + // test-case : Heroes of the Pacific + // test-case : Hummer Badlands + // test-case : Knights Of The Temple 2 + // test-case : LakeMasters Bass fishing + // test-case : MetalDungeon + // test-case : NFL Fever 2003 Demo - main menu + // test-case : Night Caster 2 + // test-case : Pinball Hall of Fame + // test-case : Robotech : Battlecry + // test-case : SpiderMan 2 + // test-case : Splinter Cell Demo + // test-case : Stubbs the Zombie + // test-case : Tony Hawk's Pro Skater 2X (main menu entries) + // test-case : Worms 3D Special Edition + // test-case : XDK sample Lensflare (4, for 10 flare-out quads that use a linear texture; rendered incorrectly: https://youtu.be/idwlxHl9nAA?t=439) + DrawContext.dwStartVertex = StartVertex; // Breakpoint location for testing. + } + + // Draw quadlists using a single 'quad-to-triangle mapping' index buffer : + // Assure & activate that special index buffer : + CxbxAssureQuadListD3DIndexBuffer(/*NrOfQuadIndices=*/DrawContext.dwVertexCount); + // This API's StartVertex argument is multiplied by vertex stride and added to the start of the vertex buffer; + // BaseVertexIndex offers the same functionality on host : + UINT BaseVertexIndex = DrawContext.dwStartVertex; + // Convert quad vertex count to triangle vertex count : + UINT NumVertices = QuadToTriangleVertexCount(DrawContext.dwVertexCount); + // Convert quad primitive count to triangle primitive count : + UINT primCount = DrawContext.dwHostPrimitiveCount * TRIANGLES_PER_QUAD; + // See https://docs.microsoft.com/en-us/windows/win32/direct3d9/rendering-from-vertex-and-index-buffers + // for an explanation on the function of the BaseVertexIndex, MinVertexIndex, NumVertices and StartIndex arguments. + // Emulate drawing quads by drawing each quad with two indexed triangles : + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitive( + /*PrimitiveType=*/D3DPT_TRIANGLELIST, + BaseVertexIndex, + /*MinVertexIndex=*/0, + NumVertices, + /*startIndex=*/0, + primCount + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitive(X_D3DPT_QUADLIST)"); + + g_dwPrimPerFrame += primCount; + } + else { + // if (StartVertex > 0) LOG_TEST_CASE("StartVertex > 0 (non-quad)"); // Verified test case : XDK Sample (PlayField) + HRESULT hRet = g_pD3DDevice->DrawPrimitive( + EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), + DrawContext.dwStartVertex, + DrawContext.dwHostPrimitiveCount + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawPrimitive"); + + g_dwPrimPerFrame += DrawContext.dwHostPrimitiveCount; + if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { + // Close line-loops using a final single line, drawn from the end to the start vertex + LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Text-cases needed + + assert(DrawContext.dwBaseVertexIndex == 0); // if this fails, it needs to be added to LowIndex and HighIndex : + INDEX16 LowIndex = (INDEX16)DrawContext.dwStartVertex; + INDEX16 HighIndex = (INDEX16)(DrawContext.dwStartVertex + DrawContext.dwHostPrimitiveCount); + // Draw the closing line using a helper function (which will SetIndices) + CxbxDrawIndexedClosingLine(LowIndex, HighIndex); + // NOTE : We don't restore the previously active index buffer + } + } + } + + CxbxHandleXboxCallbacks(); +} + +// ****************************************************************** +// * patch: D3DDevice_DrawVerticesUP +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawVerticesUP) +( + X_D3DPRIMITIVETYPE PrimitiveType, + UINT VertexCount, + CONST PVOID pVertexStreamZeroData, + UINT VertexStreamZeroStride +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(PrimitiveType) + LOG_FUNC_ARG(VertexCount) + LOG_FUNC_ARG(pVertexStreamZeroData) + LOG_FUNC_ARG(VertexStreamZeroStride) + LOG_FUNC_END; + + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { + LOG_TEST_CASE("Invalid VertexCount"); + return; + } + + // TODO : Call unpatched D3DDevice_SetStateUP(); + + CxbxUpdateNativeD3DResources(); + + if (IsValidCurrentShader()) { + CxbxDrawContext DrawContext = {}; + + DrawContext.XboxPrimitiveType = PrimitiveType; + DrawContext.dwVertexCount = VertexCount; + DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; + DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; + + CxbxDrawPrimitiveUP(DrawContext); + } + + CxbxHandleXboxCallbacks(); +} + +// ****************************************************************** +// * patch: D3DDevice_DrawIndexedVertices +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVertices) +( + X_D3DPRIMITIVETYPE PrimitiveType, + UINT VertexCount, + CONST PWORD pIndexData +) +{ + // Test-cases : XDK samples (Cartoon, Gamepad) + // Note : In gamepad.xbe, the gamepad is drawn by D3DDevice_DrawIndexedVertices + // Dxbx Note : In DrawVertices and DrawIndexedVertices, PrimitiveType may not be D3DPT_POLYGON + + LOG_FUNC_BEGIN + LOG_FUNC_ARG(PrimitiveType) + LOG_FUNC_ARG(VertexCount) + LOG_FUNC_ARG(pIndexData) + LOG_FUNC_END; + + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { + LOG_TEST_CASE("Invalid VertexCount"); + return; + } + + // TODO : Call unpatched D3DDevice_SetStateVB(g_Xbox_BaseVertexIndex); + + CxbxUpdateNativeD3DResources(); + + if (IsValidCurrentShader()) { + CxbxDrawContext DrawContext = {}; + + DrawContext.XboxPrimitiveType = PrimitiveType; + DrawContext.dwVertexCount = VertexCount; + DrawContext.dwBaseVertexIndex = g_Xbox_BaseVertexIndex; // Multiplied by vertex stride and added to the vertex buffer start + DrawContext.pXboxIndexData = pIndexData; // Used to derive VerticesInBuffer + + // Test case JSRF draws all geometry through this function (only sparks are drawn via another method) + // using X_D3DPT_TRIANGLELIST and X_D3DPT_TRIANGLESTRIP PrimitiveType + CxbxDrawIndexed(DrawContext); + } + + CxbxHandleXboxCallbacks(); +} + +// ****************************************************************** +// * patch: D3DDevice_DrawIndexedVerticesUP +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_DrawIndexedVerticesUP) +( + X_D3DPRIMITIVETYPE PrimitiveType, + UINT VertexCount, + CONST PVOID pIndexData, + CONST PVOID pVertexStreamZeroData, + UINT VertexStreamZeroStride +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(PrimitiveType) + LOG_FUNC_ARG(VertexCount) + LOG_FUNC_ARG(pIndexData) + LOG_FUNC_ARG(pVertexStreamZeroData) + LOG_FUNC_ARG(VertexStreamZeroStride) + LOG_FUNC_END; + + if (!IsValidXboxVertexCount(PrimitiveType, VertexCount)) { + LOG_TEST_CASE("Invalid VertexCount"); + return; + } + + // TODO : Call unpatched D3DDevice_SetStateUP(); + + CxbxUpdateNativeD3DResources(); + + if (IsValidCurrentShader()) { + CxbxDrawContext DrawContext = {}; + INDEX16* pXboxIndexData = (INDEX16*)pIndexData; + + DrawContext.XboxPrimitiveType = PrimitiveType; + DrawContext.dwVertexCount = VertexCount; + DrawContext.pXboxIndexData = pXboxIndexData; // Used to derive VerticesInBuffer + // Note : D3DDevice_DrawIndexedVerticesUP does NOT use g_Xbox_BaseVertexIndex, so keep DrawContext.dwBaseVertexIndex at 0! + DrawContext.pXboxVertexStreamZeroData = pVertexStreamZeroData; + DrawContext.uiXboxVertexStreamZeroStride = VertexStreamZeroStride; + + // Determine LowIndex and HighIndex *before* VerticesInBuffer gets derived + WalkIndexBuffer(DrawContext.LowIndex, DrawContext.HighIndex, pXboxIndexData, VertexCount); + + VertexBufferConverter.Apply(&DrawContext); + + INDEX16* pHostIndexData; + UINT PrimitiveCount = DrawContext.dwHostPrimitiveCount; + + bool bConvertQuadListToTriangleList = (DrawContext.XboxPrimitiveType == X_D3DPT_QUADLIST); + if (bConvertQuadListToTriangleList) { + LOG_TEST_CASE("X_D3DPT_QUADLIST"); + // Test-case : Buffy: The Vampire Slayer + // Test-case : XDK samples : FastLoad, BackBufferScale, DisplacementMap, Donuts3D, VolumeLight, PersistDisplay, PolynomialTextureMaps, SwapCallback, Tiling, VolumeFog, DebugKeyboard, Gamepad + // Convert draw arguments from quads to triangles : + pHostIndexData = CxbxCreateQuadListToTriangleListIndexData(pXboxIndexData, VertexCount); + PrimitiveCount *= TRIANGLES_PER_QUAD; + // Note, that LowIndex and HighIndex won't change due to this quad-to-triangle conversion, + // so it's less work to WalkIndexBuffer over the input instead of the converted index buffer. + } else { + // LOG_TEST_CASE("DrawIndexedPrimitiveUP"); // Test-case : Burnout, Namco Museum 50th Anniversary + pHostIndexData = pXboxIndexData; + } + + HRESULT hRet = g_pD3DDevice->DrawIndexedPrimitiveUP( + /*PrimitiveType=*/EmuXB2PC_D3DPrimitiveType(DrawContext.XboxPrimitiveType), + /*MinVertexIndex=*/DrawContext.LowIndex, + /*NumVertexIndices=*/(DrawContext.HighIndex - DrawContext.LowIndex) + 1, + PrimitiveCount, + pHostIndexData, + /*IndexDataFormat=*/D3DFMT_INDEX16, + DrawContext.pHostVertexStreamZeroData, + DrawContext.uiHostVertexStreamZeroStride + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawIndexedPrimitiveUP"); + + if (bConvertQuadListToTriangleList) { + CxbxReleaseQuadListToTriangleListIndexData(pHostIndexData); + } + + g_dwPrimPerFrame += PrimitiveCount; + if (DrawContext.XboxPrimitiveType == X_D3DPT_LINELOOP) { + // Close line-loops using a final single line, drawn from the end to the start vertex + LOG_TEST_CASE("X_D3DPT_LINELOOP"); // TODO : Which titles reach this test-case? + // Read the end and start index from the supplied index data + INDEX16 LowIndex = pXboxIndexData[0]; + INDEX16 HighIndex = pXboxIndexData[DrawContext.dwHostPrimitiveCount]; + // If needed, swap so highest index is higher than lowest (duh) + if (HighIndex < LowIndex) { + std::swap(HighIndex, LowIndex); + } + + // Close line-loops using a final single line, drawn from the end to the start vertex : + CxbxDrawIndexedClosingLineUP( + LowIndex, + HighIndex, + DrawContext.pHostVertexStreamZeroData, + DrawContext.uiHostVertexStreamZeroStride + ); + } + } + + CxbxHandleXboxCallbacks(); +} + +// ****************************************************************** +// * patch: D3DDevice_SetLight +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_SetLight) +( + DWORD Index, + CONST X_D3DLIGHT8 *pLight +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Index) + LOG_FUNC_ARG(pLight) + LOG_FUNC_END; + + XB_TRMP(D3DDevice_SetLight)(Index, pLight); + + HRESULT hRet = g_pD3DDevice->SetLight(Index, pLight); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetLight"); + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_SetMaterial +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetMaterial) +( + CONST X_D3DMATERIAL8 *pMaterial +) +{ + LOG_FUNC_ONE_ARG(pMaterial); + + HRESULT hRet = g_pD3DDevice->SetMaterial(pMaterial); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetMaterial"); +} + +// ****************************************************************** +// * patch: D3DDevice_LightEnable +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_LightEnable) +( + DWORD Index, + BOOL bEnable +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Index) + LOG_FUNC_ARG(bEnable) + LOG_FUNC_END; + + XB_TRMP(D3DDevice_LightEnable)(Index, bEnable); + + HRESULT hRet = g_pD3DDevice->LightEnable(Index, bEnable); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->LightEnable"); + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_SetRenderTarget +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetRenderTarget) +( + X_D3DSurface *pRenderTarget, + X_D3DSurface *pNewZStencil +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pRenderTarget) + LOG_FUNC_ARG(pNewZStencil) + LOG_FUNC_END; + + IDirect3DSurface *pHostRenderTarget = nullptr; + IDirect3DSurface *pHostDepthStencil = nullptr; + + XB_TRMP(D3DDevice_SetRenderTarget)(pRenderTarget, pNewZStencil); + + // In Xbox titles, CreateDevice calls SetRenderTarget for the back buffer + // We can use this to determine the Xbox backbuffer surface for later use! + if (g_pXbox_BackBufferSurface == xbnullptr) { + g_pXbox_BackBufferSurface = pRenderTarget; + // TODO : Some titles might render to another backbuffer later on, + // if that happens, we might need to skip the first one or two calls? + } + + if (g_pXbox_DefaultDepthStencilSurface == xbnullptr) { + g_pXbox_DefaultDepthStencilSurface = pNewZStencil; + } + + // The current render target is only replaced if it's passed in here non-null + if (pRenderTarget != xbnullptr) { + g_pXbox_RenderTarget = pRenderTarget; + } + else { + // If non is given, use the current Xbox render target + pRenderTarget = g_pXbox_RenderTarget; + // If there's no Xbox render target yet, fallback to the Xbox back buffer + if (pRenderTarget == xbnullptr) { + LOG_TEST_CASE("SetRenderTarget fallback to backbuffer"); + pRenderTarget = g_pXbox_BackBufferSurface; + } + } + + pHostRenderTarget = GetHostSurface(pRenderTarget, D3DUSAGE_RENDERTARGET); + + // The currenct depth stencil is always replaced by whats passed in here (even a null) + g_pXbox_DepthStencil = pNewZStencil; + pHostDepthStencil = GetHostSurface(g_pXbox_DepthStencil, D3DUSAGE_DEPTHSTENCIL); + + HRESULT hRet; + // Mimick Direct3D 8 SetRenderTarget by only setting render target if non-null + if (pHostRenderTarget) { + hRet = g_pD3DDevice->SetRenderTarget(/*RenderTargetIndex=*/0, pHostRenderTarget); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetRenderTarget"); + if (FAILED(hRet)) { + // If Direct3D 9 SetRenderTarget failed, skip setting depth stencil + return; + } + } + + hRet = g_pD3DDevice->SetDepthStencilSurface(pHostDepthStencil); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetDepthStencilSurface"); + + if (SUCCEEDED(hRet)) { + // Once we're sure the host depth-stencil is activated... + UpdateDepthStencilFlags(pHostDepthStencil); + } + + // Validate that our host render target is still the correct size + DWORD HostRenderTarget_Width, HostRenderTarget_Height; + if (GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) { + DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXbox_RenderTarget); + DWORD XboxRenderTarget_Height = GetPixelContainerHeight(g_pXbox_RenderTarget); + ValidateRenderTargetDimensions(HostRenderTarget_Width, HostRenderTarget_Height, XboxRenderTarget_Width, XboxRenderTarget_Height); + } + + UpdateViewPortOffsetAndScaleConstants(); +} + +// LTCG specific D3DDevice_SetPalette function... +// This uses a custom calling convention where parameter is passed in EAX +// Test-case: Ninja Gaiden +VOID __stdcall XTL::EMUPATCH(D3DDevice_SetPalette_4) +( +) +{ + static uint32_t returnAddr; + +#ifdef _DEBUG_TRACE + __asm add esp, 4 +#endif + + __asm { + pop returnAddr + push eax + call EmuPatch_D3DDevice_SetPalette + mov eax, 0 + push returnAddr + ret + } +} + +// ****************************************************************** +// * patch: D3DDevice_SetPalette +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPalette) +( + DWORD Stage, + X_D3DPalette *pPalette +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Stage) + LOG_FUNC_ARG(pPalette) + LOG_FUNC_END; + + // g_pD3DDevice9->SetPaletteEntries(Stage?, (PALETTEENTRY*)pPalette->Data); + // g_pD3DDevice9->SetCurrentTexturePalette(Stage, Stage); + + if (Stage >= XTL::X_D3DTS_STAGECOUNT) { + LOG_TEST_CASE("Stage out of bounds"); + } else { + // Note : Actual update of paletized textures (X_D3DFMT_P8) happens in EmuUpdateActiveTextureStages! + g_pXbox_Palette_Data[Stage] = GetDataFromXboxResource(pPalette); + g_Xbox_Palette_Size[Stage] = pPalette ? XboxD3DPaletteSizeToBytes(GetXboxPaletteSize(pPalette)) : 0; + } +} + +// LTCG specific D3DDevice_SetFlickerFilter function... +// This uses a custom calling convention where parameter is passed in ESI +// Test-case: Metal Wolf Chaos +VOID __stdcall XTL::EMUPATCH(D3DDevice_SetFlickerFilter_0) +( +) +{ + DWORD Filter; + + __asm { + mov Filter, esi + } + + return EMUPATCH(D3DDevice_SetFlickerFilter)(Filter); +} + +// ****************************************************************** +// * patch: D3DDevice_SetFlickerFilter +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3DDevice_SetFlickerFilter) +( + DWORD Filter +) +{ + LOG_FUNC_ONE_ARG(Filter); + + LOG_IGNORED(); +} + +// ****************************************************************** +// * patch: D3DDevice_SetSoftDisplayFilter +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3DDevice_SetSoftDisplayFilter) +( + BOOL Enable +) +{ + LOG_FUNC_ONE_ARG(Enable); + + LOG_IGNORED(); +} + +// LTCG specific D3DDevice_DeleteVertexShader function... +// This uses a custom calling convention where parameter is passed in EAX +// UNTESTED - Need test-case! +VOID __stdcall XTL::EMUPATCH(D3DDevice_DeleteVertexShader_0) +( +) +{ + DWORD Handle; + + __asm { + mov Handle, eax + } + + LOG_TEST_CASE("Validate this function!"); + return EMUPATCH(D3DDevice_DeleteVertexShader)(Handle); +} + +// ****************************************************************** +// * patch: D3DDevice_DeleteVertexShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_DeleteVertexShader) +( + DWORD Handle +) +{ + LOG_FUNC_ONE_ARG(Handle); + + XB_TRMP(D3DDevice_DeleteVertexShader)(Handle); + + CxbxImpl_DeleteVertexShader(Handle); +} + +// ****************************************************************** +// * patch: D3DDevice_SelectVertexShaderDirect +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SelectVertexShaderDirect) +( + X_VERTEXATTRIBUTEFORMAT *pVAF, + DWORD Address +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pVAF) + LOG_FUNC_ARG(Address) + LOG_FUNC_END; + + CxbxImpl_SelectVertexShaderDirect(pVAF, Address); +} + +// ****************************************************************** +// * patch: D3DDevice_GetShaderConstantMode +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetShaderConstantMode) +( + DWORD *pMode +) +{ + LOG_FUNC_ONE_ARG(pMode); + + if(pMode) + { + *pMode = g_Xbox_VertexShaderConstantMode; + } +} + +// ****************************************************************** +// * patch: D3DDevice_GetVertexShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShader) +( + DWORD *pHandle +) +{ + LOG_FUNC_ONE_ARG(pHandle); + + if(pHandle) + { + (*pHandle) = g_Xbox_VertexShader_Handle; + } +} + +// ****************************************************************** +// * patch: D3DDevice_GetVertexShaderConstant +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderConstant) +( + INT Register, + void *pConstantData, + DWORD ConstantCount +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Register) + LOG_FUNC_ARG(pConstantData) + LOG_FUNC_ARG(ConstantCount) + LOG_FUNC_END; + + // Xbox vertex shader constants range from -96 to 95 + // The host does not support negative, so we adjust to 0..191 + Register += X_D3DSCM_CORRECTION; + + HRESULT hRet = g_pD3DDevice->GetVertexShaderConstantF + ( + Register, + (float*)pConstantData, // TODO : Validate this work correctly under D3D9 + ConstantCount + ); + + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetVertexShaderConstant"); +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderInputDirect +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderInputDirect) +( + X_VERTEXATTRIBUTEFORMAT *pVAF, + UINT StreamCount, + X_STREAMINPUT *pStreamInputs +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pVAF) + LOG_FUNC_ARG(StreamCount) + LOG_FUNC_ARG(pStreamInputs) + LOG_FUNC_END; + + // If pVAF is given, it's copied into a global Xbox VertexBuffer struct and + // D3DDevice_SetVertexShaderInput is called with Handle set to that address, or-ed with 1 (X_D3DFVF_RESERVED0) + // Otherwise, D3DDevice_SetVertexShaderInput is called with Handle 0. + + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_GetVertexShaderInput +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput) +( + DWORD *pHandle, + UINT *pStreamCount, + X_STREAMINPUT *pStreamInputs +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pHandle) + LOG_FUNC_ARG(pStreamCount) + LOG_FUNC_ARG(pStreamInputs) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + return 0; +} + +// ****************************************************************** +// * patch: D3DDevice_SetVertexShaderInput +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderInput) +( + DWORD Handle, + UINT StreamCount, + X_STREAMINPUT *pStreamInputs +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(StreamCount) + LOG_FUNC_ARG(pStreamInputs) + LOG_FUNC_END; + + // When this API is in effect, VertexBuffers as set by Xbox SetStreamSource are disregarded, + // instead, the pStreamInputs[].VertexBuffer streams are used. + + // If Handle is NULL, all VertexShader input state is cleared (after which the VertexBuffers as set by SetStreamSource are used once again). + + // Otherwise, Handle is the address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // The given pStreamInputs are stored in a global array, and the NV2A is programmed to read + // each vertex attribute (as defined in the given VertexShader.VertexAttribute.Slots[]) to read + // the attribute data from the pStreamInputs[slot].VertexBuffer + pStreamInputs[slot].Offset + VertexShader.VertexAttribute.Slots[slot].Offset + + CxbxImpl_SetVertexShaderInput(Handle, StreamCount, pStreamInputs); +} + +// ****************************************************************** +// * patch: D3DDevice_RunVertexStateShader +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_RunVertexStateShader) +( + DWORD Address, + CONST FLOAT *pData +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Address) + LOG_FUNC_ARG(pData) + LOG_FUNC_END; + + // If pData is assigned, pData[0..3] is pushed towards nv2a transform data registers + // then sends the nv2a a command to launch the vertex shader function located at Address + + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_LoadVertexShaderProgram +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShaderProgram) +( + CONST DWORD *pFunction, + DWORD Address + ) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pFunction) + LOG_FUNC_ARG(Address) + LOG_FUNC_END; + + CxbxImpl_LoadVertexShaderProgram(pFunction, Address); +} + +// ****************************************************************** +// * patch: D3DDevice_SetDepthClipPlanes +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_SetDepthClipPlanes) +( + FLOAT Near, + FLOAT Far, + DWORD Flags +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Near) + LOG_FUNC_ARG(Far) + LOG_FUNC_ARG(Flags) + LOG_FUNC_END; + + HRESULT hRet = D3D_OK; + + switch(Flags) // Member of X_D3DSET_DEPTH_CLIP_PLANES_FLAGS enum + { + case X_D3DSDCP_SET_VERTEXPROGRAM_PLANES: + { + // Sets the depth-clipping planes used whenever vertex shader programs are active + // TODO + + // pDevice->fNear = Near + // pDevice->fFar = Far + } + break; + + case X_D3DSDCP_SET_FIXEDFUNCTION_PLANES: + { + // Sets the depth-clipping planes used whenever the fixed-function pipeline is in use. + // TODO + + // pDevice->fNear = Near + // pDevice->fFar = Far + } + break; + + case X_D3DSDCP_USE_DEFAULT_VERTEXPROGRAM_PLANES: + { + // Causes Direct3D to disregard the depth-clipping planes set when using X_D3DSDCP_SET_VERTEXPROGRAM_PLANE. + // Direct3D will resume using its own internally calculated clip planes when vertex shader programs are active. + // TODO + } + break; + + case X_D3DSDCP_USE_DEFAULT_FIXEDFUNCTION_PLANES: + { + // Causes Direct3D to disregard the depth-clipping planes set when using X_D3DSDCP_SET_FIXEDFUNCTION_PLANES. + // Direct3D will resume using its own internally calculated clip planes when the fixed-function pipeline is active. + // TODO + } + break; + + default: + EmuLog(LOG_LEVEL::WARNING, "Unknown SetDepthClipPlanes Flags provided"); + } + + // TODO + + + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_InsertFence +// ****************************************************************** +DWORD WINAPI XTL::EMUPATCH(D3DDevice_InsertFence)() +{ + LOG_FUNC(); + + // TODO: Actually implement this + DWORD dwRet = 0x8000BEEF; + + LOG_UNIMPLEMENTED(); + + return dwRet; +} + +// ****************************************************************** +// * patch: D3DDevice_IsFencePending +// ****************************************************************** +BOOL WINAPI XTL::EMUPATCH(D3DDevice_IsFencePending) +( + DWORD Fence +) +{ + LOG_FUNC_ONE_ARG(Fence); + + // TODO: Implement + LOG_UNIMPLEMENTED(); + + return FALSE; +} + +// ****************************************************************** +// * patch: D3DDevice_BlockOnFence +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_BlockOnFence) +( + DWORD Fence +) +{ + LOG_FUNC_ONE_ARG(Fence); + + // TODO: Implement + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: IDirect3DResource8_BlockUntilNotBusy +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DResource_BlockUntilNotBusy) +( + X_D3DResource *pThis +) +{ + LOG_FUNC_ONE_ARG(pThis); + + // TODO: Implement + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_SetScreenSpaceOffset +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetScreenSpaceOffset) +( + FLOAT x, + FLOAT y +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(x) + LOG_FUNC_ARG(y) + LOG_FUNC_END; + + // No need to log this, it's safe to ignore. + //EmuLog(LOG_LEVEL::WARNING, "EmuD3DDevice_SetScreenSpaceOffset ignored"); +} + +// ****************************************************************** +// * patch: D3DDevice_InsertCallback +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_InsertCallback) +( + X_D3DCALLBACKTYPE Type, + X_D3DCALLBACK pCallback, + DWORD Context +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Type) + LOG_FUNC_ARG(pCallback) + LOG_FUNC_ARG(Context) + LOG_FUNC_END; + + CxbxImpl_InsertCallback(Type, pCallback, Context); + + LOG_INCOMPLETE(); +} + +// ****************************************************************** +// * patch: D3DDevice_DrawRectPatch +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_DrawRectPatch) +( + UINT Handle, + CONST FLOAT *pNumSegs, + CONST D3DRECTPATCH_INFO *pRectPatchInfo +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(pNumSegs) + LOG_FUNC_ARG(pRectPatchInfo) + LOG_FUNC_END; + + CxbxUpdateNativeD3DResources(); + + HRESULT hRet = g_pD3DDevice->DrawRectPatch( Handle, pNumSegs, pRectPatchInfo ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawRectPatch"); + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_DrawTriPatch +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_DrawTriPatch) +( + UINT Handle, + CONST FLOAT *pNumSegs, + CONST D3DTRIPATCH_INFO* pTriPatchInfo +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(pNumSegs) + LOG_FUNC_ARG(pTriPatchInfo) + LOG_FUNC_END; + + CxbxUpdateNativeD3DResources(); + + HRESULT hRet = g_pD3DDevice->DrawTriPatch(Handle, pNumSegs, pTriPatchInfo); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DrawTriPatch"); + + return hRet; +} + +#pragma warning(disable:4244) +// ****************************************************************** +// * patch: D3DDevice_GetProjectionViewportMatrix +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetProjectionViewportMatrix) +( + D3DXMATRIX *pProjectionViewport +) +{ + LOG_FUNC_ONE_ARG(pProjectionViewport); + + // blueshogun96 1/25/10 + // It's been almost 3 years, but I think this is a better + // implementation. Still probably not right, but better + // then before. + + HRESULT hRet; + D3DXMATRIX Out, mtxProjection, mtxViewport; + D3DVIEWPORT Viewport; + + // Get current viewport + hRet = g_pD3DDevice->GetViewport(&Viewport); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetViewport - Unable to get viewport!"); + + // Get current projection matrix + hRet = g_pD3DDevice->GetTransform(D3DTS_PROJECTION, &mtxProjection); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetTransform - Unable to get projection matrix!"); + + // Clear the destination matrix + ::ZeroMemory(&Out, sizeof(D3DMATRIX)); + + // Create the Viewport matrix manually + // Direct3D8 doesn't give me everything I need in a viewport structure + // (one thing I REALLY HATE!) so some constants will have to be used + // instead. + + float ClipWidth = 2.0f; + float ClipHeight = 2.0f; + float ClipX = -1.0f; + float ClipY = 1.0f; + float Width = DWtoF(Viewport.Width); + float Height = DWtoF(Viewport.Height); + + D3DXMatrixIdentity(&mtxViewport); + mtxViewport._11 = Width / ClipWidth; + mtxViewport._22 = -(Height / ClipHeight); + mtxViewport._41 = -(ClipX * mtxViewport._11); + mtxViewport._42 = -(ClipY * mtxViewport._22); + + // Multiply projection and viewport matrix together + Out = mtxProjection * mtxViewport; + + *pProjectionViewport = Out; + +// __asm int 3; +} +#pragma warning(default:4244) + +// ****************************************************************** +// * patch: D3DDevice_SetStateVB (D3D::CDevice::SetStateVB) +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStateVB)( ULONG Unknown1 ) +{ + LOG_FUNC_ONE_ARG(Unknown1); + + // TODO: Anything? +// __asm int 3; + + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_SetStateUP (D3D::CDevice::SetStateUP) +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStateUP)() +{ + LOG_FUNC(); + + LOG_UNIMPLEMENTED(); + + // TODO: Anything? +// __asm int 3; + +} + +// ****************************************************************** +// * patch: D3DDevice_SetStipple +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3DDevice_SetStipple)( DWORD* pPattern ) +{ + LOG_FUNC_ONE_ARG(pPattern); + + // We need an OpenGL port... badly + + LOG_IGNORED(); +} + +// ****************************************************************** +// * patch: D3DDevice_SetSwapCallback +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3DDevice_SetSwapCallback) +( + X_D3DSWAPCALLBACK pCallback +) +{ + LOG_FUNC_ONE_ARG(pCallback); + + g_pXbox_SwapCallback = pCallback; +} + +// ****************************************************************** +// * patch: D3DDevice_PersistDisplay +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_PersistDisplay)() +{ + LOG_FUNC(); + + LOG_INCOMPLETE(); + + // TODO: This function simply saves a copy of the display to a surface and persists it in contiguous memory + // This function, if ever required, can be implemented as the following + // 1. Check for an existing persisted surface via AvGetSavedDataAddress, free it if necessary + // 2. Create an Xbox format surface with the same size and format as active display + // 3. Copy the host framebuffer to the xbox surface, converting format if necessary + // 4. Set the display mode via AvSetDisplayMode to the same format as the persisted surface, + // passing the ->Data pointer of the xbox surface as the framebuffer pointer. + // 5. Use MmPersistContigousMemory to persist the surface data across reboot + // 6. Call AvSetSavedDataAddress, passing the xbox surface data pointer + + // Call the native Xbox function so that AvSetSavedDataAddress is called and the VMManager can know its correct address + if (XB_TRMP(D3DDevice_PersistDisplay)) { + return XB_TRMP(D3DDevice_PersistDisplay)(); + } + return 0; +} + +// ****************************************************************** +// * patch: D3DDevice_PrimeVertexCache +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_PrimeVertexCache) +( + UINT VertexCount, + WORD *pIndexData +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(VertexCount) + LOG_FUNC_ARG(pIndexData) + LOG_FUNC_END; + + // TODO: Implement + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_SetModelView +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetModelView) +( + CONST D3DMATRIX *pModelView, + CONST D3DMATRIX *pInverseModelView, + CONST D3DMATRIX *pComposite +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pModelView) + LOG_FUNC_ARG(pInverseModelView) + LOG_FUNC_ARG(pComposite) + LOG_FUNC_END; + + // TODO: Implement + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_FlushVertexCache +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3DDevice_FlushVertexCache)() +{ + LOG_FUNC(); + + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_GetModelView +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetModelView)(D3DXMATRIX* pModelView) +{ + LOG_FUNC_ONE_ARG(pModelView); + + D3DXMATRIX mtxWorld, mtxView; + + // I hope this is right + g_pD3DDevice->GetTransform( D3DTS_WORLD, &mtxWorld ); + g_pD3DDevice->GetTransform( D3DTS_VIEW, &mtxView ); + + *pModelView = mtxWorld * mtxView; + + return D3D_OK; +} + + +DWORD PushBuffer[64 * 1024 / sizeof(DWORD)]; + +// ****************************************************************** +// * patch: D3D_SetCommonDebugRegisters +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3D_SetCommonDebugRegisters)() +{ + LOG_FUNC(); + + // NOTE: I added this because I was too lazy to deal with emulating certain render + // states that use it. + + LOG_UNIMPLEMENTED(); + +} + +// ****************************************************************** +// * patch: D3DDevice_IsBusy +// ****************************************************************** +BOOL WINAPI XTL::EMUPATCH(D3DDevice_IsBusy)() +{ + LOG_FUNC(); + + // NOTE: This function returns FALSE when the NV2A FIFO is empty/complete, or NV_PGRAPH_STATUS = 0 + // Otherwise, it returns true. + + return FALSE; +} + +// ****************************************************************** +// * patch: D3D_BlockOnTime +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3D_BlockOnTime)( DWORD Unknown1, int Unknown2 ) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Unknown1) + LOG_FUNC_ARG(Unknown2) + LOG_FUNC_END; + + // NOTE: This function is not meant to be emulated. Just use it to find out + // the function that is calling it, and emulate that instead!!! If necessary, + // create an XRef... + + //__asm int 3; + + LOG_UNIMPLEMENTED(); +} + +bool DestroyResource_Common(XTL::X_D3DResource* pResource) +{ + if (pResource == g_pXbox_RenderTarget) { + LOG_TEST_CASE("Skipping Release of active Xbox Render Target"); + return false; + } + + if (pResource == g_pXbox_DepthStencil) { + LOG_TEST_CASE("Skipping Release of active Xbox Depth Stencil"); + return false; + } + + if (pResource == g_pXbox_BackBufferSurface) { + LOG_TEST_CASE("Skipping Release of active Xbox BackBuffer"); + return false; + } + + if (pResource == g_pXbox_DefaultDepthStencilSurface) { + LOG_TEST_CASE("Skipping Release of default Xbox Depth Stencil"); + return false; + } + + for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) { + if (pResource == g_pXbox_SetTexture[i]) { + // This shouldn't happen, since texture resources that get destroyed, + // shouldn't be set to any stage anymore. + LOG_TEST_CASE("Skipping Release of active Xbox Texture"); + return false; + } + } + + // Release the host copy (if it exists!) + FreeHostResource(GetHostResourceKey(pResource)); + + return true; +} + +// ****************************************************************** +// * patch: D3D_DestroyResource +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3D_DestroyResource)(X_D3DResource* pResource) +{ + LOG_FUNC_ONE_ARG(pResource); + + if (DestroyResource_Common(pResource)) { + // Call the Xbox version of DestroyResource + XB_TRMP(D3D_DestroyResource)(pResource); + } +} + +// ****************************************************************** +// * patch: D3D_DestroyResource_LTCG +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3D_DestroyResource__LTCG)() +{ + X_D3DResource* pResource; + __asm { + mov pResource, edi + } + + if (DestroyResource_Common(pResource)) { + // Call the Xbox version of DestroyResource + __asm { + mov edi, pResource + call XB_TRMP(D3D_DestroyResource__LTCG) + } + } +} + + +// ****************************************************************** +// * patch: D3DDevice_SetRenderTargetFast +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetRenderTargetFast) +( + X_D3DSurface *pRenderTarget, + X_D3DSurface *pNewZStencil, + DWORD Flags +) +{ + LOG_FORWARD("D3DDevice_SetRenderTarget"); + + // Redirect to the standard version. + + EMUPATCH(D3DDevice_SetRenderTarget)(pRenderTarget, pNewZStencil); +} + +// ****************************************************************** +// * patch: D3D::LazySetPointParams +// ****************************************************************** +void WINAPI XTL::EMUPATCH(D3D_LazySetPointParams) +( + void* Device +) +{ + LOG_FUNC_ONE_ARG(Device); + + LOG_UNIMPLEMENTED(); +} + +// ****************************************************************** +// * patch: D3DDevice_GetMaterial +// ****************************************************************** +VOID WINAPI XTL::EMUPATCH(D3DDevice_GetMaterial) +( + X_D3DMATERIAL8* pMaterial +) +{ + LOG_FUNC_ONE_ARG(pMaterial); + + HRESULT hRet = D3D_OK; + + if (pMaterial) + { + hRet = g_pD3DDevice->GetMaterial(pMaterial); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetMaterial"); + } + + if(FAILED(hRet)) + { + EmuLog(LOG_LEVEL::WARNING, "We're lying about getting a material!"); + hRet = D3D_OK; + } +} + +// LTCG specific D3DDevice_SetPixelShaderConstant function... +// This uses a custom calling convention where parameter is passed in ECX, EAX +// TODO: Log function is not working due lost parameter in EAX. +// Test-case: Otogi 2, Ninja Gaiden: Black +VOID WINAPI XTL::EMUPATCH(D3DDevice_SetPixelShaderConstant_4) +( + CONST PVOID pConstantData +) +{ + DWORD Register; + DWORD ConstantCount; + + __asm { + mov Register, ecx + mov ConstantCount, eax + } + + //LOG_FUNC_BEGIN + // LOG_FUNC_ARG(Register) + // LOG_FUNC_ARG(pConstantData) + // LOG_FUNC_ARG(ConstantCount) + // LOG_FUNC_END; + EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetPixelShaderConstant_4(Register : %d pConstantData : %08X ConstantCount : %d);", Register, pConstantData, ConstantCount); + + HRESULT hRet = g_pD3DDevice->SetPixelShaderConstantF + ( + Register, + (PixelShaderConstantType*)pConstantData, + ConstantCount + ); + //DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetPixelShaderConstant"); + + if(FAILED(hRet)) + { + EmuLog(LOG_LEVEL::WARNING, "We're lying about setting a pixel shader constant!"); + + hRet = D3D_OK; + } +} diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp.unused-patches b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp.unused-patches index b21c05afd..45f1e5c91 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp.unused-patches +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp.unused-patches @@ -4043,95 +4043,95 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_GetViewportOffsetAndScale) pScale->z = vScale[2]; pScale->w = vScale[3]; } - -// ****************************************************************** -// * patch: D3DDevice_GetVertexShaderDeclaration -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderDeclaration) -( - DWORD Handle, - PVOID pData, - DWORD *pSizeOfData -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(pData) - LOG_FUNC_ARG(pSizeOfData) - LOG_FUNC_END; - - // Handle is always address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) - // If the pData buffer pointer is given, pSizeOfData is the address of it's size (in bytes) - // If pData is null, pSizeOfData is still given (to receive the required data size) - - - // The VertexShader is converted back into the contained program and it's size. - // In any case, *pSizeOfData will be set to the program size. - // If the pData is null, no further action it taken. - // If the pData buffer pointer is given, but the given *pSizeOfData is smaller than the program size, an error is returned. - // Otherwise, the program is unbatched and copied into the pData buffer. - - HRESULT hRet = D3DERR_INVALIDCALL; - - if (pSizeOfData) { - CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(Handle); - if (pCxbxVertexShader) { - DWORD sizeOfData = pCxbxVertexShader->Declaration.XboxDeclarationCount * sizeof(DWORD); - if (*pSizeOfData < sizeOfData || !pData) { - *pSizeOfData = sizeOfData; - hRet = !pData ? D3D_OK : D3DERR_MOREDATA; - } - else { - memcpy(pData, pCxbxVertexShader->Declaration.pXboxDeclarationCopy, pCxbxVertexShader->Declaration.XboxDeclarationCount * sizeof(DWORD)); - hRet = D3D_OK; - } - } - } - - return hRet; -} - -// ****************************************************************** -// * patch: D3DDevice_GetVertexShaderFunction -// ****************************************************************** -HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderFunction) -( - DWORD Handle, - PVOID *pData, - DWORD *pSizeOfData -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(pData) - LOG_FUNC_ARG(pSizeOfData) - LOG_FUNC_END; - - // Handle is always address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) - // If the pData buffer pointer is given, pSizeOfData is the address of it's size (in bytes) - // If pData is null, pSizeOfData is still given (to receive the required data size) - - // The VertexShader is parsed and converted back into the underlying declaration and it's size. - // In any case, *pSizeOfData will be set to the declaration size. - // If the pData is null, no further action it taken. - // If the pData buffer pointer is given, but the given *pSizeOfData is smaller than the declaration size, an error is returned. - // Otherwise, the declaration is copied into the pData buffer. - - HRESULT hRet = D3DERR_INVALIDCALL; - - if(pSizeOfData) { - CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(Handle); - if (pCxbxVertexShader) { - if (*pSizeOfData < pCxbxVertexShader->XboxFunctionSize || !pData) { - *pSizeOfData = pCxbxVertexShader->XboxFunctionSize; - hRet = !pData ? D3D_OK : D3DERR_MOREDATA; - } - else { - memcpy(pData, pCxbxVertexShader->pXboxFunctionCopy, pCxbxVertexShader->XboxFunctionSize); - hRet = D3D_OK; - } - } - } - - return hRet; -} + +// ****************************************************************** +// * patch: D3DDevice_GetVertexShaderDeclaration +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderDeclaration) +( + DWORD Handle, + PVOID pData, + DWORD *pSizeOfData +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(pData) + LOG_FUNC_ARG(pSizeOfData) + LOG_FUNC_END; + + // Handle is always address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // If the pData buffer pointer is given, pSizeOfData is the address of it's size (in bytes) + // If pData is null, pSizeOfData is still given (to receive the required data size) + + + // The VertexShader is converted back into the contained program and it's size. + // In any case, *pSizeOfData will be set to the program size. + // If the pData is null, no further action it taken. + // If the pData buffer pointer is given, but the given *pSizeOfData is smaller than the program size, an error is returned. + // Otherwise, the program is unbatched and copied into the pData buffer. + + HRESULT hRet = D3DERR_INVALIDCALL; + + if (pSizeOfData) { + CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(Handle); + if (pCxbxVertexShader) { + DWORD sizeOfData = pCxbxVertexShader->Declaration.XboxDeclarationCount * sizeof(DWORD); + if (*pSizeOfData < sizeOfData || !pData) { + *pSizeOfData = sizeOfData; + hRet = !pData ? D3D_OK : D3DERR_MOREDATA; + } + else { + memcpy(pData, pCxbxVertexShader->Declaration.pXboxDeclarationCopy, pCxbxVertexShader->Declaration.XboxDeclarationCount * sizeof(DWORD)); + hRet = D3D_OK; + } + } + } + + return hRet; +} + +// ****************************************************************** +// * patch: D3DDevice_GetVertexShaderFunction +// ****************************************************************** +HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderFunction) +( + DWORD Handle, + PVOID *pData, + DWORD *pSizeOfData +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(pData) + LOG_FUNC_ARG(pSizeOfData) + LOG_FUNC_END; + + // Handle is always address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // If the pData buffer pointer is given, pSizeOfData is the address of it's size (in bytes) + // If pData is null, pSizeOfData is still given (to receive the required data size) + + // The VertexShader is parsed and converted back into the underlying declaration and it's size. + // In any case, *pSizeOfData will be set to the declaration size. + // If the pData is null, no further action it taken. + // If the pData buffer pointer is given, but the given *pSizeOfData is smaller than the declaration size, an error is returned. + // Otherwise, the declaration is copied into the pData buffer. + + HRESULT hRet = D3DERR_INVALIDCALL; + + if(pSizeOfData) { + CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(Handle); + if (pCxbxVertexShader) { + if (*pSizeOfData < pCxbxVertexShader->XboxFunctionSize || !pData) { + *pSizeOfData = pCxbxVertexShader->XboxFunctionSize; + hRet = !pData ? D3D_OK : D3DERR_MOREDATA; + } + else { + memcpy(pData, pCxbxVertexShader->pXboxFunctionCopy, pCxbxVertexShader->XboxFunctionSize); + hRet = D3D_OK; + } + } + } + + return hRet; +} diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.h b/src/core/hle/D3D8/Direct3D9/Direct3D9.h index c00cfe3c2..1f12bc71b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.h +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.h @@ -24,10 +24,10 @@ // ****************************************************************** #ifndef DIRECT3D9_H #define DIRECT3D9_H - + #include "core\hle\XAPI\Xapi.h" // For EMUPATCH -#include "core\hle\D3D8\XbD3D8Types.h" - +#include "core\hle\D3D8\XbD3D8Types.h" + #define DIRECTDRAW_VERSION 0x0700 #include @@ -53,8 +53,8 @@ extern uint8_t *ConvertD3DTextureToARGB( uint8_t *pSrc, int *pWidth, int *pHeight, int TextureStage = 0 -); - +); + void CxbxUpdateNativeD3DResources(); // initialize direct3d @@ -62,15 +62,15 @@ extern VOID EmuD3DInit(); // cleanup direct3d extern VOID EmuD3DCleanup(); - -extern IDirect3DDevice *g_pD3DDevice; + +extern IDirect3DDevice *g_pD3DDevice; extern DWORD g_Xbox_VertexShader_Handle; - + extern XTL::X_PixelShader *g_pXbox_PixelShader; extern XTL::X_D3DBaseTexture *g_pXbox_SetTexture[XTL::X_D3DTS_STAGECOUNT]; - + namespace XTL { // ****************************************************************** @@ -2125,7 +2125,7 @@ VOID WINAPI EMUPATCH(D3DDevice_GetMaterial) ( X_D3DMATERIAL8* pMaterial ); - + } // end of namespace XTL #endif diff --git a/src/core/hle/D3D8/Direct3D9/RenderStates.cpp b/src/core/hle/D3D8/Direct3D9/RenderStates.cpp index 27ff00bff..bab491d5f 100644 --- a/src/core/hle/D3D8/Direct3D9/RenderStates.cpp +++ b/src/core/hle/D3D8/Direct3D9/RenderStates.cpp @@ -1,474 +1,474 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2019 Luke Usher -// * -// * All rights reserved -// * -// ****************************************************************** -#define LOG_PREFIX CXBXR_MODULE::D3DST - - -#include "RenderStates.h" -#include "Logging.h" -#include "core/hle/D3D8/Direct3D9/Direct3D9.h" // For g_pD3DDevice -#include "core/hle/D3D8/XbConvert.h" - -bool XboxRenderStateConverter::Init() -{ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2019 Luke Usher +// * +// * All rights reserved +// * +// ****************************************************************** +#define LOG_PREFIX CXBXR_MODULE::D3DST + + +#include "RenderStates.h" +#include "Logging.h" +#include "core/hle/D3D8/Direct3D9/Direct3D9.h" // For g_pD3DDevice +#include "core/hle/D3D8/XbConvert.h" + +bool XboxRenderStateConverter::Init() +{ if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) { D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"]; - } else { - return false; - } - - // At this point, D3D__RenderState points to the first Deferred render state - // Do a little magic to verify that it's correct, then count back to determine the - // start offset of the entire structure - VerifyAndFixDeferredRenderStateOffset(); - - // Now use the verified Deferred offset to derive the D3D__RenderState offset - DeriveRenderStateOffsetFromDeferredRenderStateOffset(); - - // Build a mapping of Cxbx Render State indexes to indexes within the current XDK - BuildRenderStateMappingTable(); - - // Set Initial Values - StoreInitialValues(); - + } else { + return false; + } + + // At this point, D3D__RenderState points to the first Deferred render state + // Do a little magic to verify that it's correct, then count back to determine the + // start offset of the entire structure + VerifyAndFixDeferredRenderStateOffset(); + + // Now use the verified Deferred offset to derive the D3D__RenderState offset + DeriveRenderStateOffsetFromDeferredRenderStateOffset(); + + // Build a mapping of Cxbx Render State indexes to indexes within the current XDK + BuildRenderStateMappingTable(); + + // Set Initial Values + StoreInitialValues(); + return true; -} - -bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInfo) -{ - bool bIsRenderStateAvailable = (aRenderStateInfo.V <= g_LibVersion_D3D8); - if (aRenderStateInfo.R > 0) { // Applies to XTL::X_D3DRS_MULTISAMPLETYPE - // Note : X_D3DRS_MULTISAMPLETYPE seems the only render state that got - // removed (from 4039 onwards), so we check that limitation here as well - bIsRenderStateAvailable &= (g_LibVersion_D3D8 < aRenderStateInfo.R); - } - return bIsRenderStateAvailable; -} - -void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset() -{ - DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"]; - // If we found a valid CullMode offset, verify the symbol location - if (CullModeOffset == 0) { - EmuLog(LOG_LEVEL::WARNING, "D3DRS_CULLMODE could not be found. Please update the XbSymbolDatabase submodule"); - return; - } - - // Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE) - DWORD CullModeIndex = 0; - for (int i = XTL::X_D3DRS_DEFERRED_FIRST; i < XTL::X_D3DRS_CULLMODE; i++) { - auto RenderStateInfo = GetDxbxRenderStateInfo(i); - if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { - CullModeIndex++; - } - } - - // If the offset was incorrect, calculate the correct offset, log it, and fix it - if ((DWORD)(&D3D__RenderState[CullModeIndex]) != CullModeOffset) { - DWORD CorrectOffset = CullModeOffset - (CullModeIndex * sizeof(DWORD)); - EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState returned by XboxSymbolDatabase (0x%08X) was incorrect. Correcting to be 0x%08X.\nPlease file an issue with the XbSymbolDatabase project", D3D__RenderState, CorrectOffset); - D3D__RenderState = (uint32_t*)CorrectOffset; - } -} - -void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset() -{ - // When this function is called. D3D__RenderState actually points to the first deferred render state - // (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find - // the start of D3D__RenderStates. - - // Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE) - int FirstDeferredRenderStateOffset = 0; - for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState < XTL::X_D3DRS_DEFERRED_FIRST; RenderState++) { - // if the current renderstate exists in this XDK version, count it - auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState); - if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { - FirstDeferredRenderStateOffset++; - } - } - - // At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK - // This will be correct as long as our table DxbxRenderStateInfo is correct - // We can get the correct 0 offset by using a negative index - D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset]; -} - -void XboxRenderStateConverter::BuildRenderStateMappingTable() -{ - EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Render State Mapping Table"); - - XboxRenderStateOffsets.fill(-1); - - int XboxIndex = 0; - for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { - auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState); - if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { - XboxRenderStateOffsets[RenderState] = XboxIndex; - EmuLog(LOG_LEVEL::INFO, "%s = %d", RenderStateInfo.S, XboxIndex); - XboxIndex++; - continue; - } - - EmuLog(LOG_LEVEL::INFO, "%s Not Present", RenderStateInfo.S); - } -} - -void XboxRenderStateConverter::SetDirty() -{ - PreviousRenderStateValues.fill(-1); -} - -void* XboxRenderStateConverter::GetPixelShaderRenderStatePointer() -{ - return &D3D__RenderState[XTL::X_D3DRS_PS_FIRST]; -} - -bool XboxRenderStateConverter::XboxRenderStateExists(uint32_t State) -{ - if (XboxRenderStateOffsets[State] >= 0) { - return true; - } - - return false; -} - -bool XboxRenderStateConverter::XboxRenderStateValueChanged(uint32_t State) -{ - if (XboxRenderStateExists(State) && GetXboxRenderState(State) != PreviousRenderStateValues[State]) { - return true; - } - - return false; -} - -void XboxRenderStateConverter::SetXboxRenderState(uint32_t State, uint32_t Value) -{ - if (!XboxRenderStateExists(State)) { - EmuLog(LOG_LEVEL::WARNING, "Attempt to write a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", GetDxbxRenderStateInfo(State).S, g_LibVersion_D3D8); - return; - } - - D3D__RenderState[XboxRenderStateOffsets[State]] = Value; -} - -uint32_t XboxRenderStateConverter::GetXboxRenderState(uint32_t State) -{ - if (!XboxRenderStateExists(State)) { - EmuLog(LOG_LEVEL::WARNING, "Attempt to read a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", GetDxbxRenderStateInfo(State).S, g_LibVersion_D3D8); - return 0; - } - - return D3D__RenderState[XboxRenderStateOffsets[State]]; -} - -void XboxRenderStateConverter::StoreInitialValues() -{ - for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { - // Skip Render States that don't exist within this XDK - if (!XboxRenderStateExists(RenderState)) { - continue; - } - - PreviousRenderStateValues[RenderState] = GetXboxRenderState(RenderState); - } -} - -void XboxRenderStateConverter::SetWireFrameMode(int wireframe) -{ - WireFrameMode = wireframe; - - // Wireframe mode changed, so we must force the Fill Mode renderstate to dirty - // At next call to Apply, the desired WireFrame mode will be set - PreviousRenderStateValues[XTL::X_D3DRS_FILLMODE] = -1; -} - -void XboxRenderStateConverter::Apply() -{ - // Iterate through each RenderState and set the associated host render state - // We start counting at X_D3DRS_SIMPLE_FIRST, to skip the pixel shader renderstates handled elsewhere - for (unsigned int RenderState = XTL::X_D3DRS_SIMPLE_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { - // Skip any renderstate that does not exist in the current XDK, or have not changed since the previous update call - // Also skip PSTextureModes, which is a special case used by Pixel Shaders - if (!XboxRenderStateExists(RenderState) || !XboxRenderStateValueChanged(RenderState) || RenderState == XTL::X_D3DRS_PSTEXTUREMODES) { - continue; - } - - auto Value = GetXboxRenderState(RenderState); - EmuLog(LOG_LEVEL::DEBUG, "XboxRenderStateConverter::Apply(%s, %X)\n", GetDxbxRenderStateInfo(RenderState).S, Value); - - if (RenderState <= XTL::X_D3DRS_SIMPLE_LAST) { - ApplySimpleRenderState(RenderState, Value); - } else if (RenderState <= XTL::X_D3DRS_DEFERRED_LAST) { - ApplyDeferredRenderState(RenderState, Value); - } else if (RenderState <= XTL::X_D3DRS_COMPLEX_LAST) { - ApplyComplexRenderState(RenderState, Value); - } - - PreviousRenderStateValues[RenderState] = Value; - } -} - -void XboxRenderStateConverter::ApplySimpleRenderState(uint32_t State, uint32_t Value) -{ - auto RenderStateInfo = GetDxbxRenderStateInfo(State); - - switch (State) { - case XTL::X_D3DRS_COLORWRITEENABLE: { - DWORD OrigValue = Value; - Value = 0; - - if (OrigValue & (1L << 16)) { - Value |= D3DCOLORWRITEENABLE_RED; - } - - if (OrigValue & (1L << 8)) { - Value |= D3DCOLORWRITEENABLE_GREEN; - } - - if (OrigValue & (1L << 0)) { - Value |= D3DCOLORWRITEENABLE_BLUE; - } - - if (OrigValue & (1L << 24)) { - Value |= D3DCOLORWRITEENABLE_ALPHA; - } - } break; - case XTL::X_D3DRS_SHADEMODE: - Value = EmuXB2PC_D3DSHADEMODE(Value); - break; - case XTL::X_D3DRS_BLENDOP: - Value = EmuXB2PC_D3DBLENDOP(Value); - break; - case XTL::X_D3DRS_SRCBLEND: - case XTL::X_D3DRS_DESTBLEND: - Value = EmuXB2PC_D3DBLEND(Value); - break; - case XTL::X_D3DRS_ZFUNC: - case XTL::X_D3DRS_ALPHAFUNC: - case XTL::X_D3DRS_STENCILFUNC: - Value = EmuXB2PC_D3DCMPFUNC(Value); - break; - case XTL::X_D3DRS_STENCILZFAIL: - case XTL::X_D3DRS_STENCILPASS: - Value = EmuXB2PC_D3DSTENCILOP(Value); - break; - case XTL::X_D3DRS_ALPHATESTENABLE: - if (g_LibVersion_D3D8 == 3925) { - // HACK: Many 3925 have missing polygons when this is true - // Until we find out the true underlying cause, and carry on - // Test Cases: Halo, Silent Hill 2. - LOG_TEST_CASE("Applying 3925 alpha test disable hack"); - Value = false; - } - break; - case XTL::X_D3DRS_ALPHABLENDENABLE: - case XTL::X_D3DRS_BLENDCOLOR: - case XTL::X_D3DRS_ALPHAREF: case XTL::X_D3DRS_ZWRITEENABLE: - case XTL::X_D3DRS_DITHERENABLE: case XTL::X_D3DRS_STENCILREF: - case XTL::X_D3DRS_STENCILMASK: case XTL::X_D3DRS_STENCILWRITEMASK: - // These render states require no conversion, so we simply - // allow SetRenderState to be called with no changes - break; - default: - // Only log missing state if it has a PC counterpart - if (RenderStateInfo.PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "ApplySimpleRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); - } - return; - } - - // Skip RenderStates that don't have a defined PC counterpart - if (RenderStateInfo.PC == 0) { - return; - } - - g_pD3DDevice->SetRenderState((D3DRENDERSTATETYPE)(RenderStateInfo.PC), Value); -} - -void XboxRenderStateConverter::ApplyDeferredRenderState(uint32_t State, uint32_t Value) -{ - auto RenderStateInfo = GetDxbxRenderStateInfo(State); - - // Convert from Xbox Data Formats to PC - switch (State) { - case XTL::X_D3DRS_FOGSTART: - case XTL::X_D3DRS_FOGEND: { - // HACK: If the fog start/fog-end are negative, make them positive - // This fixes Smashing Drive on non-nvidia hardware - // Cause appears to be non-nvidia drivers clamping values < 0 to 0 - // Resulting in the fog formula becoming (0 - d) / 0, which breaks rendering - // This prevents that scenario for screen-space fog, *hopefully* without breaking eye-based fog also - float fogValue = *(float*)& Value; - if (fogValue < 0.0f) { - LOG_TEST_CASE("FOGSTART/FOGEND below 0"); - fogValue = std::abs(fogValue); - Value = *(DWORD*)& fogValue; - } - } break; - case XTL::X_D3DRS_FOGENABLE: - if (g_LibVersion_D3D8 == 3925) { - // HACK: Many 3925 games only show a black screen if fog is enabled - // Initially, this was thought to be bad offsets, but it has been verified to be correct - // Until we find out the true underlying cause, disable fog and carry on - // Test Cases: Halo, Silent Hill 2. - LOG_TEST_CASE("Applying 3925 fog disable hack"); - Value = false; - } - break; - case XTL::X_D3DRS_FOGTABLEMODE: - case XTL::X_D3DRS_FOGDENSITY: - case XTL::X_D3DRS_RANGEFOGENABLE: - case XTL::X_D3DRS_LIGHTING: - case XTL::X_D3DRS_SPECULARENABLE: - case XTL::X_D3DRS_LOCALVIEWER: - case XTL::X_D3DRS_COLORVERTEX: - case XTL::X_D3DRS_SPECULARMATERIALSOURCE: - case XTL::X_D3DRS_DIFFUSEMATERIALSOURCE: - case XTL::X_D3DRS_AMBIENTMATERIALSOURCE: - case XTL::X_D3DRS_EMISSIVEMATERIALSOURCE: - case XTL::X_D3DRS_AMBIENT: - case XTL::X_D3DRS_POINTSIZE: - case XTL::X_D3DRS_POINTSIZE_MIN: - case XTL::X_D3DRS_POINTSPRITEENABLE: - case XTL::X_D3DRS_POINTSCALEENABLE: - case XTL::X_D3DRS_POINTSCALE_A: - case XTL::X_D3DRS_POINTSCALE_B: - case XTL::X_D3DRS_POINTSCALE_C: - case XTL::X_D3DRS_POINTSIZE_MAX: - case XTL::X_D3DRS_PATCHEDGESTYLE: - case XTL::X_D3DRS_PATCHSEGMENTS: - // These render states require no conversion, so we can use them as-is - break; - case XTL::X_D3DRS_BACKSPECULARMATERIALSOURCE: - case XTL::X_D3DRS_BACKDIFFUSEMATERIALSOURCE: - case XTL::X_D3DRS_BACKAMBIENTMATERIALSOURCE: - case XTL::X_D3DRS_BACKEMISSIVEMATERIALSOURCE: - case XTL::X_D3DRS_BACKAMBIENT: - case XTL::X_D3DRS_SWAPFILTER: - // These states are unsupported by the host and are ignored (for now) - return; - case XTL::X_D3DRS_PRESENTATIONINTERVAL: { - // Store this as an override for our frame limiter - // Games can use this to limit certain scenes to a desired target framerate for a specific scene - // If this value is not set, or is set to 0, the default interval passed to CreateDevice is used - extern DWORD g_Xbox_PresentationInterval_Override; - g_Xbox_PresentationInterval_Override = Value; - } return; - case XTL::X_D3DRS_WRAP0: - case XTL::X_D3DRS_WRAP1: - case XTL::X_D3DRS_WRAP2: - case XTL::X_D3DRS_WRAP3: { - DWORD OldValue = Value; - Value = 0; - - Value |= (OldValue & 0x00000010) ? D3DWRAPCOORD_0 : 0; - Value |= (OldValue & 0x00001000) ? D3DWRAPCOORD_1 : 0; - Value |= (OldValue & 0x00100000) ? D3DWRAPCOORD_2 : 0; - Value |= (OldValue & 0x01000000) ? D3DWRAPCOORD_3 : 0; - } break; - default: - // Only log missing state if it has a PC counterpart - if (RenderStateInfo.PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "ApplyDeferredRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); - } - return; - } - - // Skip RenderStates that don't have a defined PC counterpart - if (RenderStateInfo.PC == 0) { - return; - } - - g_pD3DDevice->SetRenderState(RenderStateInfo.PC, Value); -} - -void XboxRenderStateConverter::ApplyComplexRenderState(uint32_t State, uint32_t Value) -{ - auto RenderStateInfo = GetDxbxRenderStateInfo(State); - - switch (State) { - case XTL::X_D3DRS_VERTEXBLEND: - // convert from Xbox direct3d to PC direct3d enumeration - if (Value <= 1) { - Value = Value; - } else if (Value == 3) { - Value = 2; - } else if (Value == 5) { - Value = 3; - } else { - LOG_TEST_CASE("Unsupported D3DVERTEXBLENDFLAGS (%d)"); - return; - } - break; - case XTL::X_D3DRS_FILLMODE: - Value = EmuXB2PC_D3DFILLMODE(Value); - - if (WireFrameMode > 0) { - if (WireFrameMode == 1) { - Value = D3DFILL_WIREFRAME; - } else { - Value = D3DFILL_POINT; - } - } - break; - case XTL::X_D3DRS_CULLMODE: - switch (Value) { - case XTL::X_D3DCULL_NONE: Value = D3DCULL_NONE; break; - case XTL::X_D3DCULL_CW: Value = D3DCULL_CW; break; - case XTL::X_D3DCULL_CCW: Value = D3DCULL_CCW;break; - default: LOG_TEST_CASE("EmuD3DDevice_SetRenderState_CullMode: Unknown Cullmode"); - } - break; - case XTL::X_D3DRS_ZBIAS: { - FLOAT Biased = static_cast(Value) * -0.000005f; - Value = *reinterpret_cast(&Biased); - } break; - // These states require no conversions, so can just be passed through to the host directly - case XTL::X_D3DRS_FOGCOLOR: - case XTL::X_D3DRS_NORMALIZENORMALS: - case XTL::X_D3DRS_ZENABLE: - case XTL::X_D3DRS_STENCILENABLE: - case XTL::X_D3DRS_STENCILFAIL: - case XTL::X_D3DRS_TEXTUREFACTOR: - case XTL::X_D3DRS_EDGEANTIALIAS: - case XTL::X_D3DRS_MULTISAMPLEANTIALIAS: - case XTL::X_D3DRS_MULTISAMPLEMASK: - break; - default: - // Only log missing state if it has a PC counterpart - if (RenderStateInfo.PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "ApplyComplexRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); - } - return; - } - - // Skip RenderStates that don't have a defined PC counterpart - if (RenderStateInfo.PC == 0) { - return; - } - - g_pD3DDevice->SetRenderState(RenderStateInfo.PC, Value); -} +} + +bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInfo) +{ + bool bIsRenderStateAvailable = (aRenderStateInfo.V <= g_LibVersion_D3D8); + if (aRenderStateInfo.R > 0) { // Applies to XTL::X_D3DRS_MULTISAMPLETYPE + // Note : X_D3DRS_MULTISAMPLETYPE seems the only render state that got + // removed (from 4039 onwards), so we check that limitation here as well + bIsRenderStateAvailable &= (g_LibVersion_D3D8 < aRenderStateInfo.R); + } + return bIsRenderStateAvailable; +} + +void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset() +{ + DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"]; + // If we found a valid CullMode offset, verify the symbol location + if (CullModeOffset == 0) { + EmuLog(LOG_LEVEL::WARNING, "D3DRS_CULLMODE could not be found. Please update the XbSymbolDatabase submodule"); + return; + } + + // Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE) + DWORD CullModeIndex = 0; + for (int i = XTL::X_D3DRS_DEFERRED_FIRST; i < XTL::X_D3DRS_CULLMODE; i++) { + auto RenderStateInfo = GetDxbxRenderStateInfo(i); + if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { + CullModeIndex++; + } + } + + // If the offset was incorrect, calculate the correct offset, log it, and fix it + if ((DWORD)(&D3D__RenderState[CullModeIndex]) != CullModeOffset) { + DWORD CorrectOffset = CullModeOffset - (CullModeIndex * sizeof(DWORD)); + EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState returned by XboxSymbolDatabase (0x%08X) was incorrect. Correcting to be 0x%08X.\nPlease file an issue with the XbSymbolDatabase project", D3D__RenderState, CorrectOffset); + D3D__RenderState = (uint32_t*)CorrectOffset; + } +} + +void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset() +{ + // When this function is called. D3D__RenderState actually points to the first deferred render state + // (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find + // the start of D3D__RenderStates. + + // Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE) + int FirstDeferredRenderStateOffset = 0; + for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState < XTL::X_D3DRS_DEFERRED_FIRST; RenderState++) { + // if the current renderstate exists in this XDK version, count it + auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState); + if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { + FirstDeferredRenderStateOffset++; + } + } + + // At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK + // This will be correct as long as our table DxbxRenderStateInfo is correct + // We can get the correct 0 offset by using a negative index + D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset]; +} + +void XboxRenderStateConverter::BuildRenderStateMappingTable() +{ + EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Render State Mapping Table"); + + XboxRenderStateOffsets.fill(-1); + + int XboxIndex = 0; + for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { + auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState); + if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { + XboxRenderStateOffsets[RenderState] = XboxIndex; + EmuLog(LOG_LEVEL::INFO, "%s = %d", RenderStateInfo.S, XboxIndex); + XboxIndex++; + continue; + } + + EmuLog(LOG_LEVEL::INFO, "%s Not Present", RenderStateInfo.S); + } +} + +void XboxRenderStateConverter::SetDirty() +{ + PreviousRenderStateValues.fill(-1); +} + +void* XboxRenderStateConverter::GetPixelShaderRenderStatePointer() +{ + return &D3D__RenderState[XTL::X_D3DRS_PS_FIRST]; +} + +bool XboxRenderStateConverter::XboxRenderStateExists(uint32_t State) +{ + if (XboxRenderStateOffsets[State] >= 0) { + return true; + } + + return false; +} + +bool XboxRenderStateConverter::XboxRenderStateValueChanged(uint32_t State) +{ + if (XboxRenderStateExists(State) && GetXboxRenderState(State) != PreviousRenderStateValues[State]) { + return true; + } + + return false; +} + +void XboxRenderStateConverter::SetXboxRenderState(uint32_t State, uint32_t Value) +{ + if (!XboxRenderStateExists(State)) { + EmuLog(LOG_LEVEL::WARNING, "Attempt to write a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", GetDxbxRenderStateInfo(State).S, g_LibVersion_D3D8); + return; + } + + D3D__RenderState[XboxRenderStateOffsets[State]] = Value; +} + +uint32_t XboxRenderStateConverter::GetXboxRenderState(uint32_t State) +{ + if (!XboxRenderStateExists(State)) { + EmuLog(LOG_LEVEL::WARNING, "Attempt to read a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", GetDxbxRenderStateInfo(State).S, g_LibVersion_D3D8); + return 0; + } + + return D3D__RenderState[XboxRenderStateOffsets[State]]; +} + +void XboxRenderStateConverter::StoreInitialValues() +{ + for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { + // Skip Render States that don't exist within this XDK + if (!XboxRenderStateExists(RenderState)) { + continue; + } + + PreviousRenderStateValues[RenderState] = GetXboxRenderState(RenderState); + } +} + +void XboxRenderStateConverter::SetWireFrameMode(int wireframe) +{ + WireFrameMode = wireframe; + + // Wireframe mode changed, so we must force the Fill Mode renderstate to dirty + // At next call to Apply, the desired WireFrame mode will be set + PreviousRenderStateValues[XTL::X_D3DRS_FILLMODE] = -1; +} + +void XboxRenderStateConverter::Apply() +{ + // Iterate through each RenderState and set the associated host render state + // We start counting at X_D3DRS_SIMPLE_FIRST, to skip the pixel shader renderstates handled elsewhere + for (unsigned int RenderState = XTL::X_D3DRS_SIMPLE_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { + // Skip any renderstate that does not exist in the current XDK, or have not changed since the previous update call + // Also skip PSTextureModes, which is a special case used by Pixel Shaders + if (!XboxRenderStateExists(RenderState) || !XboxRenderStateValueChanged(RenderState) || RenderState == XTL::X_D3DRS_PSTEXTUREMODES) { + continue; + } + + auto Value = GetXboxRenderState(RenderState); + EmuLog(LOG_LEVEL::DEBUG, "XboxRenderStateConverter::Apply(%s, %X)\n", GetDxbxRenderStateInfo(RenderState).S, Value); + + if (RenderState <= XTL::X_D3DRS_SIMPLE_LAST) { + ApplySimpleRenderState(RenderState, Value); + } else if (RenderState <= XTL::X_D3DRS_DEFERRED_LAST) { + ApplyDeferredRenderState(RenderState, Value); + } else if (RenderState <= XTL::X_D3DRS_COMPLEX_LAST) { + ApplyComplexRenderState(RenderState, Value); + } + + PreviousRenderStateValues[RenderState] = Value; + } +} + +void XboxRenderStateConverter::ApplySimpleRenderState(uint32_t State, uint32_t Value) +{ + auto RenderStateInfo = GetDxbxRenderStateInfo(State); + + switch (State) { + case XTL::X_D3DRS_COLORWRITEENABLE: { + DWORD OrigValue = Value; + Value = 0; + + if (OrigValue & (1L << 16)) { + Value |= D3DCOLORWRITEENABLE_RED; + } + + if (OrigValue & (1L << 8)) { + Value |= D3DCOLORWRITEENABLE_GREEN; + } + + if (OrigValue & (1L << 0)) { + Value |= D3DCOLORWRITEENABLE_BLUE; + } + + if (OrigValue & (1L << 24)) { + Value |= D3DCOLORWRITEENABLE_ALPHA; + } + } break; + case XTL::X_D3DRS_SHADEMODE: + Value = EmuXB2PC_D3DSHADEMODE(Value); + break; + case XTL::X_D3DRS_BLENDOP: + Value = EmuXB2PC_D3DBLENDOP(Value); + break; + case XTL::X_D3DRS_SRCBLEND: + case XTL::X_D3DRS_DESTBLEND: + Value = EmuXB2PC_D3DBLEND(Value); + break; + case XTL::X_D3DRS_ZFUNC: + case XTL::X_D3DRS_ALPHAFUNC: + case XTL::X_D3DRS_STENCILFUNC: + Value = EmuXB2PC_D3DCMPFUNC(Value); + break; + case XTL::X_D3DRS_STENCILZFAIL: + case XTL::X_D3DRS_STENCILPASS: + Value = EmuXB2PC_D3DSTENCILOP(Value); + break; + case XTL::X_D3DRS_ALPHATESTENABLE: + if (g_LibVersion_D3D8 == 3925) { + // HACK: Many 3925 have missing polygons when this is true + // Until we find out the true underlying cause, and carry on + // Test Cases: Halo, Silent Hill 2. + LOG_TEST_CASE("Applying 3925 alpha test disable hack"); + Value = false; + } + break; + case XTL::X_D3DRS_ALPHABLENDENABLE: + case XTL::X_D3DRS_BLENDCOLOR: + case XTL::X_D3DRS_ALPHAREF: case XTL::X_D3DRS_ZWRITEENABLE: + case XTL::X_D3DRS_DITHERENABLE: case XTL::X_D3DRS_STENCILREF: + case XTL::X_D3DRS_STENCILMASK: case XTL::X_D3DRS_STENCILWRITEMASK: + // These render states require no conversion, so we simply + // allow SetRenderState to be called with no changes + break; + default: + // Only log missing state if it has a PC counterpart + if (RenderStateInfo.PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "ApplySimpleRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); + } + return; + } + + // Skip RenderStates that don't have a defined PC counterpart + if (RenderStateInfo.PC == 0) { + return; + } + + g_pD3DDevice->SetRenderState((D3DRENDERSTATETYPE)(RenderStateInfo.PC), Value); +} + +void XboxRenderStateConverter::ApplyDeferredRenderState(uint32_t State, uint32_t Value) +{ + auto RenderStateInfo = GetDxbxRenderStateInfo(State); + + // Convert from Xbox Data Formats to PC + switch (State) { + case XTL::X_D3DRS_FOGSTART: + case XTL::X_D3DRS_FOGEND: { + // HACK: If the fog start/fog-end are negative, make them positive + // This fixes Smashing Drive on non-nvidia hardware + // Cause appears to be non-nvidia drivers clamping values < 0 to 0 + // Resulting in the fog formula becoming (0 - d) / 0, which breaks rendering + // This prevents that scenario for screen-space fog, *hopefully* without breaking eye-based fog also + float fogValue = *(float*)& Value; + if (fogValue < 0.0f) { + LOG_TEST_CASE("FOGSTART/FOGEND below 0"); + fogValue = std::abs(fogValue); + Value = *(DWORD*)& fogValue; + } + } break; + case XTL::X_D3DRS_FOGENABLE: + if (g_LibVersion_D3D8 == 3925) { + // HACK: Many 3925 games only show a black screen if fog is enabled + // Initially, this was thought to be bad offsets, but it has been verified to be correct + // Until we find out the true underlying cause, disable fog and carry on + // Test Cases: Halo, Silent Hill 2. + LOG_TEST_CASE("Applying 3925 fog disable hack"); + Value = false; + } + break; + case XTL::X_D3DRS_FOGTABLEMODE: + case XTL::X_D3DRS_FOGDENSITY: + case XTL::X_D3DRS_RANGEFOGENABLE: + case XTL::X_D3DRS_LIGHTING: + case XTL::X_D3DRS_SPECULARENABLE: + case XTL::X_D3DRS_LOCALVIEWER: + case XTL::X_D3DRS_COLORVERTEX: + case XTL::X_D3DRS_SPECULARMATERIALSOURCE: + case XTL::X_D3DRS_DIFFUSEMATERIALSOURCE: + case XTL::X_D3DRS_AMBIENTMATERIALSOURCE: + case XTL::X_D3DRS_EMISSIVEMATERIALSOURCE: + case XTL::X_D3DRS_AMBIENT: + case XTL::X_D3DRS_POINTSIZE: + case XTL::X_D3DRS_POINTSIZE_MIN: + case XTL::X_D3DRS_POINTSPRITEENABLE: + case XTL::X_D3DRS_POINTSCALEENABLE: + case XTL::X_D3DRS_POINTSCALE_A: + case XTL::X_D3DRS_POINTSCALE_B: + case XTL::X_D3DRS_POINTSCALE_C: + case XTL::X_D3DRS_POINTSIZE_MAX: + case XTL::X_D3DRS_PATCHEDGESTYLE: + case XTL::X_D3DRS_PATCHSEGMENTS: + // These render states require no conversion, so we can use them as-is + break; + case XTL::X_D3DRS_BACKSPECULARMATERIALSOURCE: + case XTL::X_D3DRS_BACKDIFFUSEMATERIALSOURCE: + case XTL::X_D3DRS_BACKAMBIENTMATERIALSOURCE: + case XTL::X_D3DRS_BACKEMISSIVEMATERIALSOURCE: + case XTL::X_D3DRS_BACKAMBIENT: + case XTL::X_D3DRS_SWAPFILTER: + // These states are unsupported by the host and are ignored (for now) + return; + case XTL::X_D3DRS_PRESENTATIONINTERVAL: { + // Store this as an override for our frame limiter + // Games can use this to limit certain scenes to a desired target framerate for a specific scene + // If this value is not set, or is set to 0, the default interval passed to CreateDevice is used + extern DWORD g_Xbox_PresentationInterval_Override; + g_Xbox_PresentationInterval_Override = Value; + } return; + case XTL::X_D3DRS_WRAP0: + case XTL::X_D3DRS_WRAP1: + case XTL::X_D3DRS_WRAP2: + case XTL::X_D3DRS_WRAP3: { + DWORD OldValue = Value; + Value = 0; + + Value |= (OldValue & 0x00000010) ? D3DWRAPCOORD_0 : 0; + Value |= (OldValue & 0x00001000) ? D3DWRAPCOORD_1 : 0; + Value |= (OldValue & 0x00100000) ? D3DWRAPCOORD_2 : 0; + Value |= (OldValue & 0x01000000) ? D3DWRAPCOORD_3 : 0; + } break; + default: + // Only log missing state if it has a PC counterpart + if (RenderStateInfo.PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "ApplyDeferredRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); + } + return; + } + + // Skip RenderStates that don't have a defined PC counterpart + if (RenderStateInfo.PC == 0) { + return; + } + + g_pD3DDevice->SetRenderState(RenderStateInfo.PC, Value); +} + +void XboxRenderStateConverter::ApplyComplexRenderState(uint32_t State, uint32_t Value) +{ + auto RenderStateInfo = GetDxbxRenderStateInfo(State); + + switch (State) { + case XTL::X_D3DRS_VERTEXBLEND: + // convert from Xbox direct3d to PC direct3d enumeration + if (Value <= 1) { + Value = Value; + } else if (Value == 3) { + Value = 2; + } else if (Value == 5) { + Value = 3; + } else { + LOG_TEST_CASE("Unsupported D3DVERTEXBLENDFLAGS (%d)"); + return; + } + break; + case XTL::X_D3DRS_FILLMODE: + Value = EmuXB2PC_D3DFILLMODE(Value); + + if (WireFrameMode > 0) { + if (WireFrameMode == 1) { + Value = D3DFILL_WIREFRAME; + } else { + Value = D3DFILL_POINT; + } + } + break; + case XTL::X_D3DRS_CULLMODE: + switch (Value) { + case XTL::X_D3DCULL_NONE: Value = D3DCULL_NONE; break; + case XTL::X_D3DCULL_CW: Value = D3DCULL_CW; break; + case XTL::X_D3DCULL_CCW: Value = D3DCULL_CCW;break; + default: LOG_TEST_CASE("EmuD3DDevice_SetRenderState_CullMode: Unknown Cullmode"); + } + break; + case XTL::X_D3DRS_ZBIAS: { + FLOAT Biased = static_cast(Value) * -0.000005f; + Value = *reinterpret_cast(&Biased); + } break; + // These states require no conversions, so can just be passed through to the host directly + case XTL::X_D3DRS_FOGCOLOR: + case XTL::X_D3DRS_NORMALIZENORMALS: + case XTL::X_D3DRS_ZENABLE: + case XTL::X_D3DRS_STENCILENABLE: + case XTL::X_D3DRS_STENCILFAIL: + case XTL::X_D3DRS_TEXTUREFACTOR: + case XTL::X_D3DRS_EDGEANTIALIAS: + case XTL::X_D3DRS_MULTISAMPLEANTIALIAS: + case XTL::X_D3DRS_MULTISAMPLEMASK: + break; + default: + // Only log missing state if it has a PC counterpart + if (RenderStateInfo.PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "ApplyComplexRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); + } + return; + } + + // Skip RenderStates that don't have a defined PC counterpart + if (RenderStateInfo.PC == 0) { + return; + } + + g_pD3DDevice->SetRenderState(RenderStateInfo.PC, Value); +} diff --git a/src/core/hle/D3D8/Direct3D9/RenderStates.h b/src/core/hle/D3D8/Direct3D9/RenderStates.h index 548772880..287c31f24 100644 --- a/src/core/hle/D3D8/Direct3D9/RenderStates.h +++ b/src/core/hle/D3D8/Direct3D9/RenderStates.h @@ -1,66 +1,66 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2019 Luke Usher -// * -// * All rights reserved -// * -// ****************************************************************** - -#include -#include -#include "EmuShared.h" +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2019 Luke Usher +// * +// * All rights reserved +// * +// ****************************************************************** + +#include +#include +#include "EmuShared.h" #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\support\Emu.h" -#include "core\hle\Intercept.hpp" -#include "core\hle\D3D8\XbD3D8Types.h" - -class XboxRenderStateConverter -{ -public: - bool Init(); - void Apply(); - - void* GetPixelShaderRenderStatePointer(); - - bool XboxRenderStateExists(uint32_t state); - void SetXboxRenderState(uint32_t State, uint32_t Value); - void SetWireFrameMode(int mode); - void SetDirty(); - uint32_t GetXboxRenderState(uint32_t State); -private: - void VerifyAndFixDeferredRenderStateOffset(); - void DeriveRenderStateOffsetFromDeferredRenderStateOffset(); - void StoreInitialValues(); - void BuildRenderStateMappingTable(); - - bool XboxRenderStateValueChanged(uint32_t State); - - void ApplySimpleRenderState(uint32_t State, uint32_t Value); - void ApplyDeferredRenderState(uint32_t State, uint32_t Value); - void ApplyComplexRenderState(uint32_t State, uint32_t Value); - - uint32_t* D3D__RenderState = nullptr; - int WireFrameMode = 0; - // NOTE: RenderStates are 32bit values, but using a 64bit value allows the upper bits to be used as a 'force dirty' flag - std::array PreviousRenderStateValues; - std::array XboxRenderStateOffsets; -}; +#include "core\hle\Intercept.hpp" +#include "core\hle\D3D8\XbD3D8Types.h" + +class XboxRenderStateConverter +{ +public: + bool Init(); + void Apply(); + + void* GetPixelShaderRenderStatePointer(); + + bool XboxRenderStateExists(uint32_t state); + void SetXboxRenderState(uint32_t State, uint32_t Value); + void SetWireFrameMode(int mode); + void SetDirty(); + uint32_t GetXboxRenderState(uint32_t State); +private: + void VerifyAndFixDeferredRenderStateOffset(); + void DeriveRenderStateOffsetFromDeferredRenderStateOffset(); + void StoreInitialValues(); + void BuildRenderStateMappingTable(); + + bool XboxRenderStateValueChanged(uint32_t State); + + void ApplySimpleRenderState(uint32_t State, uint32_t Value); + void ApplyDeferredRenderState(uint32_t State, uint32_t Value); + void ApplyComplexRenderState(uint32_t State, uint32_t Value); + + uint32_t* D3D__RenderState = nullptr; + int WireFrameMode = 0; + // NOTE: RenderStates are 32bit values, but using a 64bit value allows the upper bits to be used as a 'force dirty' flag + std::array PreviousRenderStateValues; + std::array XboxRenderStateOffsets; +}; diff --git a/src/core/hle/D3D8/Direct3D9/TextureStates.cpp b/src/core/hle/D3D8/Direct3D9/TextureStates.cpp index c6b034c3e..205f8020c 100644 --- a/src/core/hle/D3D8/Direct3D9/TextureStates.cpp +++ b/src/core/hle/D3D8/Direct3D9/TextureStates.cpp @@ -1,275 +1,275 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2019 Luke Usher -// * -// * All rights reserved -// * -// ****************************************************************** -#define LOG_PREFIX CXBXR_MODULE::D3DST - -#include "TextureStates.h" -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\support\Emu.h" -#include "Logging.h" -#include "EmuShared.h" -#include "core/hle/Intercept.hpp" -#include "RenderStates.h" -#include "core/hle/D3D8/Direct3D9/Direct3D9.h" // For g_pD3DDevice - -typedef struct { - char* S; // String representation. - bool IsSamplerState; // True if the state maps to a Sampler State instead of Texture Stage - DWORD PC; // PC Index -} TextureStateInfo; - -TextureStateInfo CxbxTextureStateInfo[] = { - { "D3DTSS_ADDRESSU", true, D3DSAMP_ADDRESSU }, - { "D3DTSS_ADDRESSV", true, D3DSAMP_ADDRESSV }, - { "D3DTSS_ADDRESSW", true, D3DSAMP_ADDRESSW }, - { "D3DTSS_MAGFILTER", true, D3DSAMP_MAGFILTER }, - { "D3DTSS_MINFILTER", true, D3DSAMP_MINFILTER }, - { "D3DTSS_MIPFILTER", true, D3DSAMP_MIPFILTER }, - { "D3DTSS_MIPMAPLODBIAS", true, D3DSAMP_MIPMAPLODBIAS }, - { "D3DTSS_MAXMIPLEVEL", true, D3DSAMP_MAXMIPLEVEL }, - { "D3DTSS_MAXANISOTROPY", true, D3DSAMP_MAXANISOTROPY }, - { "D3DTSS_COLORKEYOP", false, 0 }, - { "D3DTSS_COLORSIGN", false, 0 }, - { "D3DTSS_ALPHAKILL", false, 0 }, - { "D3DTSS_COLOROP", false, D3DTSS_COLOROP }, - { "D3DTSS_COLORARG0", false, D3DTSS_COLORARG0 }, - { "D3DTSS_COLORARG1", false, D3DTSS_COLORARG1 }, - { "D3DTSS_COLORARG2", false, D3DTSS_COLORARG2 }, - { "D3DTSS_ALPHAOP", false, D3DTSS_ALPHAOP }, - { "D3DTSS_ALPHAARG0", false, D3DTSS_ALPHAARG0 }, - { "D3DTSS_ALPHAARG1", false, D3DTSS_ALPHAARG1 }, - { "D3DTSS_ALPHAARG2", false, D3DTSS_ALPHAARG2 }, - { "D3DTSS_RESULTARG", false, D3DTSS_RESULTARG }, - { "D3DTSS_TEXTURETRANSFORMFLAGS", false, D3DTSS_TEXTURETRANSFORMFLAGS }, - { "D3DTSS_BUMPENVMAT00", false, D3DTSS_BUMPENVMAT00 }, - { "D3DTSS_BUMPENVMAT01", false, D3DTSS_BUMPENVMAT01 }, - { "D3DTSS_BUMPENVMAT11", false, D3DTSS_BUMPENVMAT11 }, - { "D3DTSS_BUMPENVMAT10", false, D3DTSS_BUMPENVMAT10 }, - { "D3DTSS_BUMPENVLSCALE", false, D3DTSS_BUMPENVLSCALE }, - { "D3DTSS_BUMPENVLOFFSET", false, D3DTSS_BUMPENVLOFFSET }, - { "D3DTSS_TEXCOORDINDEX", false, D3DTSS_TEXCOORDINDEX }, - { "D3DTSS_BORDERCOLOR", true, D3DSAMP_BORDERCOLOR }, - { "D3DTSS_COLORKEYCOLOR", false, 0 }, -}; - -bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState) -{ - // Deferred states start at 0, this menas that D3DDeferredTextureState IS D3D__TextureState - // No further works is required to derive the offset - if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) { - D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"]; - } else { - return false; - } - - // Build a mapping of Cxbx Texture State indexes to indexes within the current XDK - BuildTextureStateMappingTable(); - - // Store a handle to the Xbox Render State manager - // This is used to check for Point Sprites - pXboxRenderStates = pState; - - return true; -} - -void XboxTextureStateConverter::BuildTextureStateMappingTable() -{ - EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Texture State Mapping Table"); - for (int State = XTL::X_D3DTSS_FIRST; State <= XTL::X_D3DTSS_LAST; State++) { - int index = State; - - // On early XDKs, we need to shuffle the values around a little - // TODO: Verify which XDK version this change occurred at - // Values range 0-9 (D3DTSS_COLOROP to D3DTSS_TEXTURETRANSFORMFLAGS) become 12-21 - // Values 10-21 (D3DTSS_ADDRESSU to D3DTSS_ALPHAKILL) become 0-11 - bool bOldOrder = g_LibVersion_D3D8 <= 3948; // Verfied old order in 3944, new order in 4039 - if (bOldOrder) { - if (State <= 9) { - index += 12; - } else if (State <= 21) { - index -= 10; - } - } - - EmuLog(LOG_LEVEL::INFO, "%s = %d", CxbxTextureStateInfo[State].S, index); - XboxTextureStateOffsets[State] = index; - } -} - -DWORD XboxTextureStateConverter::GetHostTextureOpValue(DWORD Value) -{ - bool bOldOrder = g_LibVersion_D3D8 <= 3948; // Verified old order in 3944, new order in 4039 - switch (Value) { - case XTL::X_D3DTOP_DISABLE: return D3DTOP_DISABLE; - case XTL::X_D3DTOP_SELECTARG1: return D3DTOP_SELECTARG1; - case XTL::X_D3DTOP_SELECTARG2: return D3DTOP_SELECTARG2; - case XTL::X_D3DTOP_MODULATE: return D3DTOP_MODULATE; - case XTL::X_D3DTOP_MODULATE2X: return D3DTOP_MODULATE2X; - case XTL::X_D3DTOP_MODULATE4X: return D3DTOP_MODULATE4X; - case XTL::X_D3DTOP_ADD: return D3DTOP_ADD; - case XTL::X_D3DTOP_ADDSIGNED: return D3DTOP_ADDSIGNED; - case XTL::X_D3DTOP_ADDSIGNED2X: return D3DTOP_ADDSIGNED2X; - case XTL::X_D3DTOP_SUBTRACT: return D3DTOP_SUBTRACT; - case XTL::X_D3DTOP_ADDSMOOTH: return D3DTOP_ADDSMOOTH; - case XTL::X_D3DTOP_BLENDDIFFUSEALPHA: return D3DTOP_BLENDDIFFUSEALPHA; - case 0x0D/*XTL::X_D3DTOP_BLENDCURRENTALPHA */: return bOldOrder ? D3DTOP_BLENDTEXTUREALPHA : D3DTOP_BLENDCURRENTALPHA; - case 0x0E/*XTL::X_D3DTOP_BLENDTEXTUREALPHA */: return bOldOrder ? D3DTOP_BLENDFACTORALPHA : D3DTOP_BLENDTEXTUREALPHA; - case 0x0F/*XTL::X_D3DTOP_BLENDFACTORALPHA */: return bOldOrder ? D3DTOP_BLENDTEXTUREALPHAPM : D3DTOP_BLENDFACTORALPHA; - case 0x10/*XTL::X_D3DTOP_BLENDTEXTUREALPHAPM*/: return bOldOrder ? D3DTOP_BLENDCURRENTALPHA : D3DTOP_BLENDTEXTUREALPHAPM; - case XTL::X_D3DTOP_PREMODULATE: return D3DTOP_PREMODULATE; - case XTL::X_D3DTOP_MODULATEALPHA_ADDCOLOR: return D3DTOP_MODULATEALPHA_ADDCOLOR; - case XTL::X_D3DTOP_MODULATECOLOR_ADDALPHA: return D3DTOP_MODULATECOLOR_ADDALPHA; - case XTL::X_D3DTOP_MODULATEINVALPHA_ADDCOLOR: return D3DTOP_MODULATEINVALPHA_ADDCOLOR; - case XTL::X_D3DTOP_MODULATEINVCOLOR_ADDALPHA: return D3DTOP_MODULATEINVCOLOR_ADDALPHA; - case XTL::X_D3DTOP_DOTPRODUCT3: return D3DTOP_DOTPRODUCT3; - case XTL::X_D3DTOP_MULTIPLYADD: return D3DTOP_MULTIPLYADD; - case XTL::X_D3DTOP_LERP: return D3DTOP_LERP; - case XTL::X_D3DTOP_BUMPENVMAP: return D3DTOP_BUMPENVMAP; - case XTL::X_D3DTOP_BUMPENVMAPLUMINANCE: return D3DTOP_BUMPENVMAPLUMINANCE; - } - - EmuLog(LOG_LEVEL::WARNING, "Unsupported D3DTOP Value (%d)", Value); - return D3DTOP_DISABLE; - -} - -void XboxTextureStateConverter::Apply() -{ - // Iterate through all texture states/stages - - // Track if we need to overwrite state 0 with 3 because of Point Sprites - // The Xbox NV2A uses only Stage 3 for point-sprites, so we emulate this - // by mapping Stage 3 to Stage 0, and disabling all stages > 0 - bool pointSpriteOverride = false; - bool pointSpritesEnabled = pXboxRenderStates->GetXboxRenderState(XTL::X_D3DRS_POINTSPRITEENABLE); - if (pointSpritesEnabled) { - pointSpriteOverride = true; - } - - for (int XboxStage = 0; XboxStage < XTL::X_D3DTS_STAGECOUNT; XboxStage++) { - // If point sprites are enabled, we need to overwrite our existing state 0 with State 3 also - DWORD HostStage = (pointSpriteOverride && XboxStage == 3) ? 0 : XboxStage; - - for (int StateIndex = XTL::X_D3DTSS_FIRST; StateIndex <= XTL::X_D3DTSS_LAST; StateIndex++) { - // Read the value of the current stage/state from the Xbox data structure - DWORD Value = D3D__TextureState[(XboxStage * XTL::X_D3DTS_STAGESIZE) + StateIndex]; - - // Convert the index of the current state to an index that we can use - // This handles the case when XDKs have different state values - DWORD State = XboxTextureStateOffsets[StateIndex]; - - switch (State) { - // These types map 1:1 but have some unsupported values - case XTL::X_D3DTSS_ADDRESSU: case XTL::X_D3DTSS_ADDRESSV: case XTL::X_D3DTSS_ADDRESSW: - if (Value == XTL::X_D3DTADDRESS_CLAMPTOEDGE) { - EmuLog(LOG_LEVEL::WARNING, "D3DTADDRESS_CLAMPTOEDGE is unsupported"); - // D3DTADDRESS_BORDER is the closest host match, CLAMPTOEDGE is identical - // Except it has additional restrictions. - Value = D3DTADDRESS_BORDER; - break; - } - break; - case XTL::X_D3DTSS_MAGFILTER: case XTL::X_D3DTSS_MINFILTER: case XTL::X_D3DTSS_MIPFILTER: - if (Value == XTL::X_D3DTEXF_QUINCUNX) { - EmuLog(LOG_LEVEL::WARNING, "D3DTEXF_QUINCUNX is unsupported"); - // Fallback to D3DTEXF_ANISOTROPIC - Value = D3DTEXF_ANISOTROPIC; - break; - } - break; - case XTL::X_D3DTSS_TEXCOORDINDEX: - switch (Value) { - case 0x00040000: - // This value is TCI_OBJECT on Xbox,which is not supported by the host - // In this case, we reset to 0. - EmuLog(LOG_LEVEL::WARNING, "EmuD3DDevice_SetTextureState_TexCoordIndex: D3DTSS_TCI_OBJECT is unsupported", Value); - Value = 0; - break; - case 0x00050000: - // This value is TCI_SPHERE on Xbox, let's map it to D3DTSS_TCI_SPHEREMAP for the host - Value = D3DTSS_TCI_SPHEREMAP; - break; - } - break; - // These types require value remapping for all supported values - case XTL::X_D3DTSS_COLOROP: case XTL::X_D3DTSS_ALPHAOP: - Value = GetHostTextureOpValue(Value); - break; - // These types require no conversion, so we just pass through as-is - case XTL::X_D3DTSS_COLORARG0: case XTL::X_D3DTSS_COLORARG1: case XTL::X_D3DTSS_COLORARG2: - case XTL::X_D3DTSS_ALPHAARG0: case XTL::X_D3DTSS_ALPHAARG1: case XTL::X_D3DTSS_ALPHAARG2: - case XTL::X_D3DTSS_RESULTARG: case XTL::X_D3DTSS_TEXTURETRANSFORMFLAGS: - case XTL::X_D3DTSS_BUMPENVMAT00: case XTL::X_D3DTSS_BUMPENVMAT01: - case XTL::X_D3DTSS_BUMPENVMAT11: case XTL::X_D3DTSS_BUMPENVMAT10: - case XTL::X_D3DTSS_BUMPENVLSCALE: case XTL::X_D3DTSS_BUMPENVLOFFSET: - case XTL::X_D3DTSS_BORDERCOLOR: case XTL::X_D3DTSS_MIPMAPLODBIAS: - case XTL::X_D3DTSS_MAXMIPLEVEL: case XTL::X_D3DTSS_MAXANISOTROPY: - break; - default: - // Only log missing state if it has a PC counterpart - if (CxbxTextureStateInfo[State].PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "XboxTextureStateConverter::Apply(%s, 0x%.08X) is unimplemented!", CxbxTextureStateInfo[State].S, Value); - } - break; - } - - // Skip Texture States that don't have a defined PC counterpart - if (CxbxTextureStateInfo[State].PC == 0) { - continue; - } - - if (CxbxTextureStateInfo[State].IsSamplerState) { - g_pD3DDevice->SetSamplerState(HostStage, (D3DSAMPLERSTATETYPE)CxbxTextureStateInfo[State].PC, Value); - } else { - g_pD3DDevice->SetTextureStageState(HostStage, (D3DTEXTURESTAGESTATETYPE)CxbxTextureStateInfo[State].PC, Value); - } - } - - // Make sure we only do this once - if (pointSpriteOverride && XboxStage == 3) { - pointSpriteOverride = false; - XboxStage--; - } - } - - if (pointSpritesEnabled) { - IDirect3DBaseTexture* pTexture; - - // set the point sprites texture - g_pD3DDevice->GetTexture(3, &pTexture); - g_pD3DDevice->SetTexture(0, pTexture); - - // Avoid a dangling reference that would lead to a memory leak - if (pTexture != nullptr) - pTexture->Release(); - - // disable all other stages - g_pD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); - g_pD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - // no need to actually copy here, since it was handled in the loop above - } -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2019 Luke Usher +// * +// * All rights reserved +// * +// ****************************************************************** +#define LOG_PREFIX CXBXR_MODULE::D3DST + +#include "TextureStates.h" +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\Emu.h" +#include "Logging.h" +#include "EmuShared.h" +#include "core/hle/Intercept.hpp" +#include "RenderStates.h" +#include "core/hle/D3D8/Direct3D9/Direct3D9.h" // For g_pD3DDevice + +typedef struct { + char* S; // String representation. + bool IsSamplerState; // True if the state maps to a Sampler State instead of Texture Stage + DWORD PC; // PC Index +} TextureStateInfo; + +TextureStateInfo CxbxTextureStateInfo[] = { + { "D3DTSS_ADDRESSU", true, D3DSAMP_ADDRESSU }, + { "D3DTSS_ADDRESSV", true, D3DSAMP_ADDRESSV }, + { "D3DTSS_ADDRESSW", true, D3DSAMP_ADDRESSW }, + { "D3DTSS_MAGFILTER", true, D3DSAMP_MAGFILTER }, + { "D3DTSS_MINFILTER", true, D3DSAMP_MINFILTER }, + { "D3DTSS_MIPFILTER", true, D3DSAMP_MIPFILTER }, + { "D3DTSS_MIPMAPLODBIAS", true, D3DSAMP_MIPMAPLODBIAS }, + { "D3DTSS_MAXMIPLEVEL", true, D3DSAMP_MAXMIPLEVEL }, + { "D3DTSS_MAXANISOTROPY", true, D3DSAMP_MAXANISOTROPY }, + { "D3DTSS_COLORKEYOP", false, 0 }, + { "D3DTSS_COLORSIGN", false, 0 }, + { "D3DTSS_ALPHAKILL", false, 0 }, + { "D3DTSS_COLOROP", false, D3DTSS_COLOROP }, + { "D3DTSS_COLORARG0", false, D3DTSS_COLORARG0 }, + { "D3DTSS_COLORARG1", false, D3DTSS_COLORARG1 }, + { "D3DTSS_COLORARG2", false, D3DTSS_COLORARG2 }, + { "D3DTSS_ALPHAOP", false, D3DTSS_ALPHAOP }, + { "D3DTSS_ALPHAARG0", false, D3DTSS_ALPHAARG0 }, + { "D3DTSS_ALPHAARG1", false, D3DTSS_ALPHAARG1 }, + { "D3DTSS_ALPHAARG2", false, D3DTSS_ALPHAARG2 }, + { "D3DTSS_RESULTARG", false, D3DTSS_RESULTARG }, + { "D3DTSS_TEXTURETRANSFORMFLAGS", false, D3DTSS_TEXTURETRANSFORMFLAGS }, + { "D3DTSS_BUMPENVMAT00", false, D3DTSS_BUMPENVMAT00 }, + { "D3DTSS_BUMPENVMAT01", false, D3DTSS_BUMPENVMAT01 }, + { "D3DTSS_BUMPENVMAT11", false, D3DTSS_BUMPENVMAT11 }, + { "D3DTSS_BUMPENVMAT10", false, D3DTSS_BUMPENVMAT10 }, + { "D3DTSS_BUMPENVLSCALE", false, D3DTSS_BUMPENVLSCALE }, + { "D3DTSS_BUMPENVLOFFSET", false, D3DTSS_BUMPENVLOFFSET }, + { "D3DTSS_TEXCOORDINDEX", false, D3DTSS_TEXCOORDINDEX }, + { "D3DTSS_BORDERCOLOR", true, D3DSAMP_BORDERCOLOR }, + { "D3DTSS_COLORKEYCOLOR", false, 0 }, +}; + +bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState) +{ + // Deferred states start at 0, this menas that D3DDeferredTextureState IS D3D__TextureState + // No further works is required to derive the offset + if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) { + D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"]; + } else { + return false; + } + + // Build a mapping of Cxbx Texture State indexes to indexes within the current XDK + BuildTextureStateMappingTable(); + + // Store a handle to the Xbox Render State manager + // This is used to check for Point Sprites + pXboxRenderStates = pState; + + return true; +} + +void XboxTextureStateConverter::BuildTextureStateMappingTable() +{ + EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Texture State Mapping Table"); + for (int State = XTL::X_D3DTSS_FIRST; State <= XTL::X_D3DTSS_LAST; State++) { + int index = State; + + // On early XDKs, we need to shuffle the values around a little + // TODO: Verify which XDK version this change occurred at + // Values range 0-9 (D3DTSS_COLOROP to D3DTSS_TEXTURETRANSFORMFLAGS) become 12-21 + // Values 10-21 (D3DTSS_ADDRESSU to D3DTSS_ALPHAKILL) become 0-11 + bool bOldOrder = g_LibVersion_D3D8 <= 3948; // Verfied old order in 3944, new order in 4039 + if (bOldOrder) { + if (State <= 9) { + index += 12; + } else if (State <= 21) { + index -= 10; + } + } + + EmuLog(LOG_LEVEL::INFO, "%s = %d", CxbxTextureStateInfo[State].S, index); + XboxTextureStateOffsets[State] = index; + } +} + +DWORD XboxTextureStateConverter::GetHostTextureOpValue(DWORD Value) +{ + bool bOldOrder = g_LibVersion_D3D8 <= 3948; // Verified old order in 3944, new order in 4039 + switch (Value) { + case XTL::X_D3DTOP_DISABLE: return D3DTOP_DISABLE; + case XTL::X_D3DTOP_SELECTARG1: return D3DTOP_SELECTARG1; + case XTL::X_D3DTOP_SELECTARG2: return D3DTOP_SELECTARG2; + case XTL::X_D3DTOP_MODULATE: return D3DTOP_MODULATE; + case XTL::X_D3DTOP_MODULATE2X: return D3DTOP_MODULATE2X; + case XTL::X_D3DTOP_MODULATE4X: return D3DTOP_MODULATE4X; + case XTL::X_D3DTOP_ADD: return D3DTOP_ADD; + case XTL::X_D3DTOP_ADDSIGNED: return D3DTOP_ADDSIGNED; + case XTL::X_D3DTOP_ADDSIGNED2X: return D3DTOP_ADDSIGNED2X; + case XTL::X_D3DTOP_SUBTRACT: return D3DTOP_SUBTRACT; + case XTL::X_D3DTOP_ADDSMOOTH: return D3DTOP_ADDSMOOTH; + case XTL::X_D3DTOP_BLENDDIFFUSEALPHA: return D3DTOP_BLENDDIFFUSEALPHA; + case 0x0D/*XTL::X_D3DTOP_BLENDCURRENTALPHA */: return bOldOrder ? D3DTOP_BLENDTEXTUREALPHA : D3DTOP_BLENDCURRENTALPHA; + case 0x0E/*XTL::X_D3DTOP_BLENDTEXTUREALPHA */: return bOldOrder ? D3DTOP_BLENDFACTORALPHA : D3DTOP_BLENDTEXTUREALPHA; + case 0x0F/*XTL::X_D3DTOP_BLENDFACTORALPHA */: return bOldOrder ? D3DTOP_BLENDTEXTUREALPHAPM : D3DTOP_BLENDFACTORALPHA; + case 0x10/*XTL::X_D3DTOP_BLENDTEXTUREALPHAPM*/: return bOldOrder ? D3DTOP_BLENDCURRENTALPHA : D3DTOP_BLENDTEXTUREALPHAPM; + case XTL::X_D3DTOP_PREMODULATE: return D3DTOP_PREMODULATE; + case XTL::X_D3DTOP_MODULATEALPHA_ADDCOLOR: return D3DTOP_MODULATEALPHA_ADDCOLOR; + case XTL::X_D3DTOP_MODULATECOLOR_ADDALPHA: return D3DTOP_MODULATECOLOR_ADDALPHA; + case XTL::X_D3DTOP_MODULATEINVALPHA_ADDCOLOR: return D3DTOP_MODULATEINVALPHA_ADDCOLOR; + case XTL::X_D3DTOP_MODULATEINVCOLOR_ADDALPHA: return D3DTOP_MODULATEINVCOLOR_ADDALPHA; + case XTL::X_D3DTOP_DOTPRODUCT3: return D3DTOP_DOTPRODUCT3; + case XTL::X_D3DTOP_MULTIPLYADD: return D3DTOP_MULTIPLYADD; + case XTL::X_D3DTOP_LERP: return D3DTOP_LERP; + case XTL::X_D3DTOP_BUMPENVMAP: return D3DTOP_BUMPENVMAP; + case XTL::X_D3DTOP_BUMPENVMAPLUMINANCE: return D3DTOP_BUMPENVMAPLUMINANCE; + } + + EmuLog(LOG_LEVEL::WARNING, "Unsupported D3DTOP Value (%d)", Value); + return D3DTOP_DISABLE; + +} + +void XboxTextureStateConverter::Apply() +{ + // Iterate through all texture states/stages + + // Track if we need to overwrite state 0 with 3 because of Point Sprites + // The Xbox NV2A uses only Stage 3 for point-sprites, so we emulate this + // by mapping Stage 3 to Stage 0, and disabling all stages > 0 + bool pointSpriteOverride = false; + bool pointSpritesEnabled = pXboxRenderStates->GetXboxRenderState(XTL::X_D3DRS_POINTSPRITEENABLE); + if (pointSpritesEnabled) { + pointSpriteOverride = true; + } + + for (int XboxStage = 0; XboxStage < XTL::X_D3DTS_STAGECOUNT; XboxStage++) { + // If point sprites are enabled, we need to overwrite our existing state 0 with State 3 also + DWORD HostStage = (pointSpriteOverride && XboxStage == 3) ? 0 : XboxStage; + + for (int StateIndex = XTL::X_D3DTSS_FIRST; StateIndex <= XTL::X_D3DTSS_LAST; StateIndex++) { + // Read the value of the current stage/state from the Xbox data structure + DWORD Value = D3D__TextureState[(XboxStage * XTL::X_D3DTS_STAGESIZE) + StateIndex]; + + // Convert the index of the current state to an index that we can use + // This handles the case when XDKs have different state values + DWORD State = XboxTextureStateOffsets[StateIndex]; + + switch (State) { + // These types map 1:1 but have some unsupported values + case XTL::X_D3DTSS_ADDRESSU: case XTL::X_D3DTSS_ADDRESSV: case XTL::X_D3DTSS_ADDRESSW: + if (Value == XTL::X_D3DTADDRESS_CLAMPTOEDGE) { + EmuLog(LOG_LEVEL::WARNING, "D3DTADDRESS_CLAMPTOEDGE is unsupported"); + // D3DTADDRESS_BORDER is the closest host match, CLAMPTOEDGE is identical + // Except it has additional restrictions. + Value = D3DTADDRESS_BORDER; + break; + } + break; + case XTL::X_D3DTSS_MAGFILTER: case XTL::X_D3DTSS_MINFILTER: case XTL::X_D3DTSS_MIPFILTER: + if (Value == XTL::X_D3DTEXF_QUINCUNX) { + EmuLog(LOG_LEVEL::WARNING, "D3DTEXF_QUINCUNX is unsupported"); + // Fallback to D3DTEXF_ANISOTROPIC + Value = D3DTEXF_ANISOTROPIC; + break; + } + break; + case XTL::X_D3DTSS_TEXCOORDINDEX: + switch (Value) { + case 0x00040000: + // This value is TCI_OBJECT on Xbox,which is not supported by the host + // In this case, we reset to 0. + EmuLog(LOG_LEVEL::WARNING, "EmuD3DDevice_SetTextureState_TexCoordIndex: D3DTSS_TCI_OBJECT is unsupported", Value); + Value = 0; + break; + case 0x00050000: + // This value is TCI_SPHERE on Xbox, let's map it to D3DTSS_TCI_SPHEREMAP for the host + Value = D3DTSS_TCI_SPHEREMAP; + break; + } + break; + // These types require value remapping for all supported values + case XTL::X_D3DTSS_COLOROP: case XTL::X_D3DTSS_ALPHAOP: + Value = GetHostTextureOpValue(Value); + break; + // These types require no conversion, so we just pass through as-is + case XTL::X_D3DTSS_COLORARG0: case XTL::X_D3DTSS_COLORARG1: case XTL::X_D3DTSS_COLORARG2: + case XTL::X_D3DTSS_ALPHAARG0: case XTL::X_D3DTSS_ALPHAARG1: case XTL::X_D3DTSS_ALPHAARG2: + case XTL::X_D3DTSS_RESULTARG: case XTL::X_D3DTSS_TEXTURETRANSFORMFLAGS: + case XTL::X_D3DTSS_BUMPENVMAT00: case XTL::X_D3DTSS_BUMPENVMAT01: + case XTL::X_D3DTSS_BUMPENVMAT11: case XTL::X_D3DTSS_BUMPENVMAT10: + case XTL::X_D3DTSS_BUMPENVLSCALE: case XTL::X_D3DTSS_BUMPENVLOFFSET: + case XTL::X_D3DTSS_BORDERCOLOR: case XTL::X_D3DTSS_MIPMAPLODBIAS: + case XTL::X_D3DTSS_MAXMIPLEVEL: case XTL::X_D3DTSS_MAXANISOTROPY: + break; + default: + // Only log missing state if it has a PC counterpart + if (CxbxTextureStateInfo[State].PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "XboxTextureStateConverter::Apply(%s, 0x%.08X) is unimplemented!", CxbxTextureStateInfo[State].S, Value); + } + break; + } + + // Skip Texture States that don't have a defined PC counterpart + if (CxbxTextureStateInfo[State].PC == 0) { + continue; + } + + if (CxbxTextureStateInfo[State].IsSamplerState) { + g_pD3DDevice->SetSamplerState(HostStage, (D3DSAMPLERSTATETYPE)CxbxTextureStateInfo[State].PC, Value); + } else { + g_pD3DDevice->SetTextureStageState(HostStage, (D3DTEXTURESTAGESTATETYPE)CxbxTextureStateInfo[State].PC, Value); + } + } + + // Make sure we only do this once + if (pointSpriteOverride && XboxStage == 3) { + pointSpriteOverride = false; + XboxStage--; + } + } + + if (pointSpritesEnabled) { + IDirect3DBaseTexture* pTexture; + + // set the point sprites texture + g_pD3DDevice->GetTexture(3, &pTexture); + g_pD3DDevice->SetTexture(0, pTexture); + + // Avoid a dangling reference that would lead to a memory leak + if (pTexture != nullptr) + pTexture->Release(); + + // disable all other stages + g_pD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + g_pD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + // no need to actually copy here, since it was handled in the loop above + } +} diff --git a/src/core/hle/D3D8/Direct3D9/TextureStates.h b/src/core/hle/D3D8/Direct3D9/TextureStates.h index a79444bac..7bda64272 100644 --- a/src/core/hle/D3D8/Direct3D9/TextureStates.h +++ b/src/core/hle/D3D8/Direct3D9/TextureStates.h @@ -1,49 +1,49 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2019 Luke Usher -// * -// * All rights reserved -// * -// ****************************************************************** - -#include -#include -#include "core\hle\D3D8\XbD3D8Types.h" - -#define CXBX_D3DRS_UNSUPPORTED (XTL::X_D3DRS_LAST + 1) - -class XboxRenderStateConverter; - -class XboxTextureStateConverter -{ -public: - bool Init(XboxRenderStateConverter* state); - void Apply(); - -private: - void BuildTextureStateMappingTable(); - DWORD GetHostTextureOpValue(DWORD XboxTextureOp); - - uint32_t* D3D__TextureState = nullptr; - std::array XboxTextureStateOffsets; - XboxRenderStateConverter* pXboxRenderStates; -}; +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2019 Luke Usher +// * +// * All rights reserved +// * +// ****************************************************************** + +#include +#include +#include "core\hle\D3D8\XbD3D8Types.h" + +#define CXBX_D3DRS_UNSUPPORTED (XTL::X_D3DRS_LAST + 1) + +class XboxRenderStateConverter; + +class XboxTextureStateConverter +{ +public: + bool Init(XboxRenderStateConverter* state); + void Apply(); + +private: + void BuildTextureStateMappingTable(); + DWORD GetHostTextureOpValue(DWORD XboxTextureOp); + + uint32_t* D3D__TextureState = nullptr; + std::array XboxTextureStateOffsets; + XboxRenderStateConverter* pXboxRenderStates; +}; diff --git a/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp b/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp index ec2b38bce..c1103bef0 100644 --- a/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp +++ b/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp @@ -5,10 +5,10 @@ #include "Logging.h" #include "util/hasher.h" #include "core/kernel/support/Emu.h" - -VertexShaderSource g_VertexShaderSource = VertexShaderSource(); -// FIXME : This should really be released and created in step with the D3D device lifecycle rather than being a thing on its own -// (And the ResetD3DDevice method should be removed) + +VertexShaderSource g_VertexShaderSource = VertexShaderSource(); +// FIXME : This should really be released and created in step with the D3D device lifecycle rather than being a thing on its own +// (And the ResetD3DDevice method should be removed) ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, ShaderKey key) { // HACK set thread affinity every call to reduce interference with Xbox main thread @@ -44,14 +44,14 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S // Create a new shader // If the shader was already created, just increase its reference count ShaderKey VertexShaderSource::CreateShader(const DWORD* pXboxFunction, DWORD *pXboxFunctionSize) { - IntermediateVertexShader intermediateShader; + IntermediateVertexShader intermediateShader; // Parse into intermediate format EmuParseVshFunction((DWORD*)pXboxFunction, pXboxFunctionSize, &intermediateShader); - - // FIXME ignore shader header when creating key + + // FIXME ignore shader header when creating key ShaderKey key = ComputeHash((void*)pXboxFunction, *pXboxFunctionSize); // Check if we need to create the shader diff --git a/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h b/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h index 1271a6dfa..0ac1204e4 100644 --- a/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h +++ b/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h @@ -41,7 +41,7 @@ private: bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader); }; - -extern VertexShaderSource g_VertexShaderSource; + +extern VertexShaderSource g_VertexShaderSource; #endif diff --git a/src/core/hle/D3D8/XbConvert.cpp b/src/core/hle/D3D8/XbConvert.cpp index 63cd0e37e..679bc332d 100644 --- a/src/core/hle/D3D8/XbConvert.cpp +++ b/src/core/hle/D3D8/XbConvert.cpp @@ -28,8 +28,8 @@ #define LOG_PREFIX CXBXR_MODULE::D3DCVT -#include "common\Settings.hpp" // for g_LibVersion_D3D8 -#include "core\kernel\support\Emu.h" +#include "common\Settings.hpp" // for g_LibVersion_D3D8 +#include "core\kernel\support\Emu.h" #include "XbConvert.h" @@ -698,8 +698,8 @@ static __inline uint32_t Clamp(int32_t val) { #define SIMD_ALIGNED(var) __declspec(align(16)) var #define SIMD_ALIGNED32(var) __declspec(align(64)) var typedef __declspec(align(32)) int16_t lvec16[16]; -typedef __declspec(align(32)) int8_t lvec8[32]; -#elif __GNUC__ +typedef __declspec(align(32)) int8_t lvec8[32]; +#elif __GNUC__ #define SIMD_ALIGNED(var) __attribute__((aligned(16)))var #define SIMD_ALIGNED32(var) __attribute__((aligned(64))) var typedef __attribute__((aligned(32))) int16_t lvec16[16]; @@ -1054,7 +1054,7 @@ D3DFORMAT EmuXB2PC_D3DFormat(XTL::X_D3DFORMAT Format) } XTL::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear) -{ +{ XTL::X_D3DFORMAT result; switch(Format) { @@ -1136,7 +1136,7 @@ XTL::X_D3DFORMAT EmuPC2XB_D3DFormat(D3DFORMAT Format, bool bPreferLinear) } DWORD EmuXB2PC_D3DLock(DWORD Flags) -{ +{ DWORD NewFlags = 0; // Need to convert the flags, TODO: fix the xbox extensions @@ -1189,11 +1189,11 @@ D3DMULTISAMPLE_TYPE EmuXB2PC_D3DMultiSampleFormat(DWORD Type) // lookup table for converting vertex count to primitive count const unsigned g_XboxPrimitiveTypeInfo[11][2] = -{ - // First number is the starting number of vertices the draw requires - // Second number the number of vertices per primitive +{ + // First number is the starting number of vertices the draw requires + // Second number the number of vertices per primitive // Example : Triangle list, has no starting vertices, and uses 3 vertices for each triangle - // Example : Triangle strip, starts with 2 vertices, and adds 1 for each triangle + // Example : Triangle strip, starts with 2 vertices, and adds 1 for each triangle {0, 0}, // NULL {0, 1}, // X_D3DPT_POINTLIST {0, 2}, // X_D3DPT_LINELIST @@ -1337,14 +1337,14 @@ void EmuUnswizzleBox } } } // EmuUnswizzleBox NOPATCH - -// Notes : -// * most renderstates were introduced in the (lowest known) XDK version : 3424 -// * additional renderstates were introduced between 3434 and 4627 -// * we MUST list exact versions for each of those, since their inserts impacts mapping! -// * renderstates were finalized in 4627 (so no change after that version) -// * renderstates after D3DRS_MULTISAMPLEMASK have no host mapping, thus no impact -// * D3DRS_MULTISAMPLETYPE seems the only renderstate that got removed (after 3944, before 4039) + +// Notes : +// * most renderstates were introduced in the (lowest known) XDK version : 3424 +// * additional renderstates were introduced between 3434 and 4627 +// * we MUST list exact versions for each of those, since their inserts impacts mapping! +// * renderstates were finalized in 4627 (so no change after that version) +// * renderstates after D3DRS_MULTISAMPLEMASK have no host mapping, thus no impact +// * D3DRS_MULTISAMPLETYPE seems the only renderstate that got removed (after 3944, before 4039) // * all renderstates marked 3424 are also verified present in 3944 const RenderStateInfo DxbxRenderStateInfo[] = { @@ -1444,40 +1444,40 @@ const RenderStateInfo DxbxRenderStateInfo[] = { { "D3DRS_SIMPLE_UNUSED2" /*= 90*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? { "D3DRS_SIMPLE_UNUSED1" /*= 91*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? // End of "simple" render states, continuing with "deferred" render states : - // Verified as XDK 3911 Deferred RenderStates (3424 yet to do) - { "D3DRS_FOGENABLE" /*= 92*/, 3424, xtBOOL, NV2A_FOG_ENABLE, D3DRS_FOGENABLE }, // TRUE to enable fog blending - { "D3DRS_FOGTABLEMODE" /*= 93*/, 3424, xtD3DFOGMODE, NV2A_FOG_MODE, D3DRS_FOGTABLEMODE }, // D3DFOGMODE - { "D3DRS_FOGSTART" /*= 94*/, 3424, xtFloat, NV2A_FOG_COORD_DIST, D3DRS_FOGSTART }, // float fog start (for both vertex and pixel fog) - { "D3DRS_FOGEND" /*= 95*/, 3424, xtFloat, NV2A_FOG_MODE, D3DRS_FOGEND }, // float fog end - { "D3DRS_FOGDENSITY" /*= 96*/, 3424, xtFloat, NV2A_FOG_EQUATION_CONSTANT, D3DRS_FOGDENSITY }, // float fog density // + NV2A_FOG_EQUATION_LINEAR + NV2A_FOG_EQUATION_QUADRATIC - { "D3DRS_RANGEFOGENABLE" /*= 97*/, 3424, xtBOOL, NV2A_FOG_COORD_DIST, D3DRS_RANGEFOGENABLE }, // TRUE to enable range-based fog - { "D3DRS_WRAP0" /*= 98*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(0), D3DRS_WRAP0 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 1st texture coord. - { "D3DRS_WRAP1" /*= 99*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(1), D3DRS_WRAP1 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 2nd texture coord. - { "D3DRS_WRAP2" /*= 100*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(2), D3DRS_WRAP2 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 3rd texture coord. - { "D3DRS_WRAP3" /*= 101*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(3), D3DRS_WRAP3 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 4th texture coord. - { "D3DRS_LIGHTING" /*= 102*/, 3424, xtBOOL, NV2A_LIGHT_MODEL, D3DRS_LIGHTING }, // TRUE to enable lighting // TODO : Needs push-buffer data conversion - { "D3DRS_SPECULARENABLE" /*= 103*/, 3424, xtBOOL, NV2A_RC_FINAL0, D3DRS_SPECULARENABLE }, // TRUE to enable specular - { "D3DRS_LOCALVIEWER" /*= 104*/, 3424, xtBOOL, 0, D3DRS_LOCALVIEWER }, // TRUE to enable camera-relative specular highlights - { "D3DRS_COLORVERTEX" /*= 105*/, 3424, xtBOOL, 0, D3DRS_COLORVERTEX }, // TRUE to enable per-vertex color - { "D3DRS_BACKSPECULARMATERIALSOURCE" /*= 106*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. - { "D3DRS_BACKDIFFUSEMATERIALSOURCE" /*= 107*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. - { "D3DRS_BACKAMBIENTMATERIALSOURCE" /*= 108*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. - { "D3DRS_BACKEMISSIVEMATERIALSOURCE" /*= 109*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. - { "D3DRS_SPECULARMATERIALSOURCE" /*= 110*/, 3424, xtD3DMCS, NV2A_COLOR_MATERIAL, D3DRS_SPECULARMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE - { "D3DRS_DIFFUSEMATERIALSOURCE" /*= 111*/, 3424, xtD3DMCS, 0, D3DRS_DIFFUSEMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE - { "D3DRS_AMBIENTMATERIALSOURCE" /*= 112*/, 3424, xtD3DMCS, 0, D3DRS_AMBIENTMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE - { "D3DRS_EMISSIVEMATERIALSOURCE" /*= 113*/, 3424, xtD3DMCS, 0, D3DRS_EMISSIVEMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE - { "D3DRS_BACKAMBIENT" /*= 114*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_BACK_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R }, // D3DCOLOR (Xbox extension) // ..NV2A_MATERIAL_FACTOR_BACK_B nsp. Was NV2A_LIGHT_MODEL_BACK_AMBIENT_R - { "D3DRS_AMBIENT" /*= 115*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_FRONT_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R, D3DRS_AMBIENT }, // D3DCOLOR // ..NV2A_LIGHT_MODEL_FRONT_AMBIENT_B + NV2A_MATERIAL_FACTOR_FRONT_R..NV2A_MATERIAL_FACTOR_FRONT_A Was NV2A_LIGHT_MODEL_FRONT_AMBIENT_R - { "D3DRS_POINTSIZE" /*= 116*/, 3424, xtFloat, NV2A_POINT_PARAMETER(0), D3DRS_POINTSIZE }, // float point size - { "D3DRS_POINTSIZE_MIN" /*= 117*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MIN }, // float point size min threshold - { "D3DRS_POINTSPRITEENABLE" /*= 118*/, 3424, xtBOOL, NV2A_POINT_SMOOTH_ENABLE, D3DRS_POINTSPRITEENABLE }, // TRUE to enable point sprites - { "D3DRS_POINTSCALEENABLE" /*= 119*/, 3424, xtBOOL, NV2A_POINT_PARAMETERS_ENABLE, D3DRS_POINTSCALEENABLE }, // TRUE to enable point size scaling - { "D3DRS_POINTSCALE_A" /*= 120*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_A }, // float point attenuation A value - { "D3DRS_POINTSCALE_B" /*= 121*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_B }, // float point attenuation B value - { "D3DRS_POINTSCALE_C" /*= 122*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_C }, // float point attenuation C value - { "D3DRS_POINTSIZE_MAX" /*= 123*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MAX }, // float point size max threshold - { "D3DRS_PATCHEDGESTYLE" /*= 124*/, 3424, xtDWORD, 0, D3DRS_PATCHEDGESTYLE }, // D3DPATCHEDGESTYLE + // Verified as XDK 3911 Deferred RenderStates (3424 yet to do) + { "D3DRS_FOGENABLE" /*= 92*/, 3424, xtBOOL, NV2A_FOG_ENABLE, D3DRS_FOGENABLE }, // TRUE to enable fog blending + { "D3DRS_FOGTABLEMODE" /*= 93*/, 3424, xtD3DFOGMODE, NV2A_FOG_MODE, D3DRS_FOGTABLEMODE }, // D3DFOGMODE + { "D3DRS_FOGSTART" /*= 94*/, 3424, xtFloat, NV2A_FOG_COORD_DIST, D3DRS_FOGSTART }, // float fog start (for both vertex and pixel fog) + { "D3DRS_FOGEND" /*= 95*/, 3424, xtFloat, NV2A_FOG_MODE, D3DRS_FOGEND }, // float fog end + { "D3DRS_FOGDENSITY" /*= 96*/, 3424, xtFloat, NV2A_FOG_EQUATION_CONSTANT, D3DRS_FOGDENSITY }, // float fog density // + NV2A_FOG_EQUATION_LINEAR + NV2A_FOG_EQUATION_QUADRATIC + { "D3DRS_RANGEFOGENABLE" /*= 97*/, 3424, xtBOOL, NV2A_FOG_COORD_DIST, D3DRS_RANGEFOGENABLE }, // TRUE to enable range-based fog + { "D3DRS_WRAP0" /*= 98*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(0), D3DRS_WRAP0 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 1st texture coord. + { "D3DRS_WRAP1" /*= 99*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(1), D3DRS_WRAP1 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 2nd texture coord. + { "D3DRS_WRAP2" /*= 100*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(2), D3DRS_WRAP2 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 3rd texture coord. + { "D3DRS_WRAP3" /*= 101*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(3), D3DRS_WRAP3 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 4th texture coord. + { "D3DRS_LIGHTING" /*= 102*/, 3424, xtBOOL, NV2A_LIGHT_MODEL, D3DRS_LIGHTING }, // TRUE to enable lighting // TODO : Needs push-buffer data conversion + { "D3DRS_SPECULARENABLE" /*= 103*/, 3424, xtBOOL, NV2A_RC_FINAL0, D3DRS_SPECULARENABLE }, // TRUE to enable specular + { "D3DRS_LOCALVIEWER" /*= 104*/, 3424, xtBOOL, 0, D3DRS_LOCALVIEWER }, // TRUE to enable camera-relative specular highlights + { "D3DRS_COLORVERTEX" /*= 105*/, 3424, xtBOOL, 0, D3DRS_COLORVERTEX }, // TRUE to enable per-vertex color + { "D3DRS_BACKSPECULARMATERIALSOURCE" /*= 106*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_BACKDIFFUSEMATERIALSOURCE" /*= 107*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_BACKAMBIENTMATERIALSOURCE" /*= 108*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_BACKEMISSIVEMATERIALSOURCE" /*= 109*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_SPECULARMATERIALSOURCE" /*= 110*/, 3424, xtD3DMCS, NV2A_COLOR_MATERIAL, D3DRS_SPECULARMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_DIFFUSEMATERIALSOURCE" /*= 111*/, 3424, xtD3DMCS, 0, D3DRS_DIFFUSEMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_AMBIENTMATERIALSOURCE" /*= 112*/, 3424, xtD3DMCS, 0, D3DRS_AMBIENTMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_EMISSIVEMATERIALSOURCE" /*= 113*/, 3424, xtD3DMCS, 0, D3DRS_EMISSIVEMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_BACKAMBIENT" /*= 114*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_BACK_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R }, // D3DCOLOR (Xbox extension) // ..NV2A_MATERIAL_FACTOR_BACK_B nsp. Was NV2A_LIGHT_MODEL_BACK_AMBIENT_R + { "D3DRS_AMBIENT" /*= 115*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_FRONT_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R, D3DRS_AMBIENT }, // D3DCOLOR // ..NV2A_LIGHT_MODEL_FRONT_AMBIENT_B + NV2A_MATERIAL_FACTOR_FRONT_R..NV2A_MATERIAL_FACTOR_FRONT_A Was NV2A_LIGHT_MODEL_FRONT_AMBIENT_R + { "D3DRS_POINTSIZE" /*= 116*/, 3424, xtFloat, NV2A_POINT_PARAMETER(0), D3DRS_POINTSIZE }, // float point size + { "D3DRS_POINTSIZE_MIN" /*= 117*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MIN }, // float point size min threshold + { "D3DRS_POINTSPRITEENABLE" /*= 118*/, 3424, xtBOOL, NV2A_POINT_SMOOTH_ENABLE, D3DRS_POINTSPRITEENABLE }, // TRUE to enable point sprites + { "D3DRS_POINTSCALEENABLE" /*= 119*/, 3424, xtBOOL, NV2A_POINT_PARAMETERS_ENABLE, D3DRS_POINTSCALEENABLE }, // TRUE to enable point size scaling + { "D3DRS_POINTSCALE_A" /*= 120*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_A }, // float point attenuation A value + { "D3DRS_POINTSCALE_B" /*= 121*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_B }, // float point attenuation B value + { "D3DRS_POINTSCALE_C" /*= 122*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_C }, // float point attenuation C value + { "D3DRS_POINTSIZE_MAX" /*= 123*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MAX }, // float point size max threshold + { "D3DRS_PATCHEDGESTYLE" /*= 124*/, 3424, xtDWORD, 0, D3DRS_PATCHEDGESTYLE }, // D3DPATCHEDGESTYLE { "D3DRS_PATCHSEGMENTS" /*= 125*/, 3424, xtDWORD, 0 }, // DWORD number of segments per edge when drawing patches, nsp (D3DRS_PATCHSEGMENTS exists in Direct3D 8, but not in 9) // TODO -oDxbx : Is X_D3DRS_SWAPFILTER really a xtD3DMULTISAMPLE_TYPE? { "D3DRS_SWAPFILTER" /*= 126*/, 4039, xtD3DMULTISAMPLE_TYPE, 0, D3DRS_UNSUPPORTED, "D3DTEXF_LINEAR etc. filter to use for Swap" }, // nsp. Verified absent in 3944, present in 4039 TODO : Might be introduced in 4034? @@ -1509,7 +1509,7 @@ const RenderStateInfo DxbxRenderStateInfo[] = { { "D3DRS_EDGEANTIALIAS" /*= 151*/, 3424, xtBOOL, NV2A_LINE_SMOOTH_ENABLE, D3DRS_ANTIALIASEDLINEENABLE }, // Was D3DRS_EDGEANTIALIAS. Dxbx note : No Xbox ext. (according to Direct3D8) ! { "D3DRS_MULTISAMPLEANTIALIAS" /*= 152*/, 3424, xtBOOL, NV2A_MULTISAMPLE_CONTROL, D3DRS_MULTISAMPLEANTIALIAS }, { "D3DRS_MULTISAMPLEMASK" /*= 153*/, 3424, xtDWORD, NV2A_MULTISAMPLE_CONTROL, D3DRS_MULTISAMPLEMASK }, - { "D3DRS_MULTISAMPLETYPE" /*= 154*/, 3424, xtD3DMULTISAMPLE_TYPE, 0, D3DRS_UNSUPPORTED, "aliasses D3DMULTISAMPLE_TYPE, removed from 4039 onward", 4039 }, // Verified present in 3944, removed in 4039 TODO : Might be removed in 4034? + { "D3DRS_MULTISAMPLETYPE" /*= 154*/, 3424, xtD3DMULTISAMPLE_TYPE, 0, D3DRS_UNSUPPORTED, "aliasses D3DMULTISAMPLE_TYPE, removed from 4039 onward", 4039 }, // Verified present in 3944, removed in 4039 TODO : Might be removed in 4034? { "D3DRS_MULTISAMPLEMODE" /*= 155*/, 4039, xtD3DMULTISAMPLEMODE, 0 }, // D3DMULTISAMPLEMODE for the backbuffer. Verified absent in 3944, present in 4039 TODO : Might be introduced in 4034? { "D3DRS_MULTISAMPLERENDERTARGETMODE" /*= 156*/, 4034, xtD3DMULTISAMPLEMODE, NV2A_RT_FORMAT }, // Verified absent in 3944, present in 4039. Presence in 4034 is based on test-case : The Simpsons Road Rage { "D3DRS_SHADOWFUNC" /*= 157*/, 3424, xtD3DCMPFUNC, NV2A_TX_RCOMP }, @@ -1523,11 +1523,11 @@ const RenderStateInfo DxbxRenderStateInfo[] = { { "D3DRS_ROPZREAD" /*= 165*/, 3911, xtBOOL, 0 }, // Verified present in 3944 { "D3DRS_DONOTCULLUNCOMPRESSED" /*= 166*/, 3911, xtBOOL, 0 } // Verified present in 3944 }; - -const RenderStateInfo& GetDxbxRenderStateInfo(int State) -{ - return DxbxRenderStateInfo[State]; -} + +const RenderStateInfo& GetDxbxRenderStateInfo(int State) +{ + return DxbxRenderStateInfo[State]; +} /*Direct3D8 states unused : D3DRS_LINEPATTERN diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index a4fec49f7..0ff875ddf 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -26,11 +26,11 @@ #define XBCONVERT_H #include "core\kernel\init\CxbxKrnl.h" - + #include "core\hle\D3D8\XbD3D8Types.h" -#define VERTICES_PER_DOT 1 -#define VERTICES_PER_LINE 2 +#define VERTICES_PER_DOT 1 +#define VERTICES_PER_LINE 2 #define VERTICES_PER_TRIANGLE 3 #define VERTICES_PER_QUAD 4 #define TRIANGLES_PER_QUAD 2 @@ -95,7 +95,7 @@ else // convert from xbox to pc texture transform state types inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(D3DTRANSFORMSTATETYPE State) -{ +{ // Handle Xbox -> D3D State mapping switch (State) { case 0: return (D3DTRANSFORMSTATETYPE)D3DTS_VIEW; @@ -146,22 +146,22 @@ inline D3DBLENDOP EmuXB2PC_D3DBLENDOP(XTL::X_D3DBLENDOP Value) // convert from xbox to pc blend types inline D3DBLEND EmuXB2PC_D3DBLEND(XTL::X_D3DBLEND Value) -{ +{ switch (Value) { - case 0x000: return D3DBLEND_ZERO; - case 0x001: return D3DBLEND_ONE; - case 0x300: return D3DBLEND_SRCCOLOR; - case 0x301: return D3DBLEND_INVSRCCOLOR; - case 0x302: return D3DBLEND_SRCALPHA; - case 0x303: return D3DBLEND_INVSRCALPHA; - case 0x304: return D3DBLEND_DESTALPHA; - case 0x305: return D3DBLEND_INVDESTALPHA; - case 0x306: return D3DBLEND_DESTCOLOR; - case 0x307: return D3DBLEND_INVDESTCOLOR; - case 0x308: return D3DBLEND_SRCALPHASAT; - case 0x8001:return D3DBLEND_BLENDFACTOR; // Maps Xbox D3DBLEND_CONSTANTCOLOR - case 0x8002:return D3DBLEND_INVBLENDFACTOR; // Maps Xbox D3DBLEND_INVCONSTANTCOLOR - case 0x8003: EmuLogEx(LOG_PREFIX_D3DCVT, LOG_LEVEL::WARNING, "Unsupported Xbox D3DBlend Extension: D3DBLEND_CONSTANTALPHA"); return D3DBLEND_ONE; + case 0x000: return D3DBLEND_ZERO; + case 0x001: return D3DBLEND_ONE; + case 0x300: return D3DBLEND_SRCCOLOR; + case 0x301: return D3DBLEND_INVSRCCOLOR; + case 0x302: return D3DBLEND_SRCALPHA; + case 0x303: return D3DBLEND_INVSRCALPHA; + case 0x304: return D3DBLEND_DESTALPHA; + case 0x305: return D3DBLEND_INVDESTALPHA; + case 0x306: return D3DBLEND_DESTCOLOR; + case 0x307: return D3DBLEND_INVDESTCOLOR; + case 0x308: return D3DBLEND_SRCALPHASAT; + case 0x8001:return D3DBLEND_BLENDFACTOR; // Maps Xbox D3DBLEND_CONSTANTCOLOR + case 0x8002:return D3DBLEND_INVBLENDFACTOR; // Maps Xbox D3DBLEND_INVCONSTANTCOLOR + case 0x8003: EmuLogEx(LOG_PREFIX_D3DCVT, LOG_LEVEL::WARNING, "Unsupported Xbox D3DBlend Extension: D3DBLEND_CONSTANTALPHA"); return D3DBLEND_ONE; case 0x8004: EmuLogEx(LOG_PREFIX_D3DCVT, LOG_LEVEL::WARNING, "Unsupported Xbox D3DBlend Extension: D3DBLEND_INVCONSTANTALPHA"); return D3DBLEND_ONE; } @@ -173,13 +173,13 @@ inline D3DBLEND EmuXB2PC_D3DBLEND(XTL::X_D3DBLEND Value) inline D3DCMPFUNC EmuXB2PC_D3DCMPFUNC(XTL::X_D3DCMPFUNC Value) { switch (Value) { - case 0x200: return D3DCMP_NEVER; - case 0x201: return D3DCMP_LESS; - case 0x202: return D3DCMP_EQUAL; - case 0x203: return D3DCMP_LESSEQUAL; - case 0x204: return D3DCMP_GREATER; - case 0x205: return D3DCMP_NOTEQUAL; - case 0x206: return D3DCMP_GREATEREQUAL; + case 0x200: return D3DCMP_NEVER; + case 0x201: return D3DCMP_LESS; + case 0x202: return D3DCMP_EQUAL; + case 0x203: return D3DCMP_LESSEQUAL; + case 0x204: return D3DCMP_GREATER; + case 0x205: return D3DCMP_NOTEQUAL; + case 0x206: return D3DCMP_GREATEREQUAL; case 0x207: return D3DCMP_ALWAYS; } @@ -191,8 +191,8 @@ inline D3DCMPFUNC EmuXB2PC_D3DCMPFUNC(XTL::X_D3DCMPFUNC Value) inline D3DFILLMODE EmuXB2PC_D3DFILLMODE(XTL::X_D3DFILLMODE Value) { switch (Value) { - case 0x1B00: return D3DFILL_POINT; - case 0x1B01: return D3DFILL_WIREFRAME; + case 0x1B00: return D3DFILL_POINT; + case 0x1B01: return D3DFILL_WIREFRAME; case 0x1B02: return D3DFILL_SOLID; } @@ -204,7 +204,7 @@ inline D3DFILLMODE EmuXB2PC_D3DFILLMODE(XTL::X_D3DFILLMODE Value) inline D3DSHADEMODE EmuXB2PC_D3DSHADEMODE(XTL::X_D3DSHADEMODE Value) { switch (Value) { - case 0x1D00: return D3DSHADE_FLAT; + case 0x1D00: return D3DSHADE_FLAT; case 0x1D01: return D3DSHADE_GOURAUD; } @@ -214,7 +214,7 @@ inline D3DSHADEMODE EmuXB2PC_D3DSHADEMODE(XTL::X_D3DSHADEMODE Value) // convert from xbox to pc stencilop modes inline D3DSTENCILOP EmuXB2PC_D3DSTENCILOP(XTL::X_D3DSTENCILOP Value) -{ +{ switch(Value) { case 0x1e00: return D3DSTENCILOP_KEEP; @@ -236,8 +236,8 @@ inline D3DSTENCILOP EmuXB2PC_D3DSTENCILOP(XTL::X_D3DSTENCILOP Value) extern const UINT g_XboxPrimitiveTypeInfo[XTL::X_D3DPT_POLYGON + 1][2]; inline bool IsValidXboxVertexCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, UINT VertexCount) -{ - assert(XboxPrimitiveType < XTL::X_D3DPT_MAX); +{ + assert(XboxPrimitiveType < XTL::X_D3DPT_MAX); // Are there more vertices than required for setup? if (VertexCount > g_XboxPrimitiveTypeInfo[XboxPrimitiveType][0]) @@ -251,22 +251,22 @@ inline bool IsValidXboxVertexCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, UI // convert from vertex count to primitive count (Xbox) inline unsigned ConvertXboxVertexCountToPrimitiveCount(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType, unsigned VertexCount) { - assert(XboxPrimitiveType < XTL::X_D3DPT_MAX); - + assert(XboxPrimitiveType < XTL::X_D3DPT_MAX); + return (VertexCount - g_XboxPrimitiveTypeInfo[XboxPrimitiveType][0]) / g_XboxPrimitiveTypeInfo[XboxPrimitiveType][1]; } - + // conversion table for xbox->pc primitive types extern const D3DPRIMITIVETYPE g_XboxPrimitiveTypeToHost[]; // convert xbox->pc primitive type inline D3DPRIMITIVETYPE EmuXB2PC_D3DPrimitiveType(XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType) { - if (XboxPrimitiveType >= XTL::X_D3DPT_MAX) { + if (XboxPrimitiveType >= XTL::X_D3DPT_MAX) { LOG_TEST_CASE("XboxPrimitiveType too large"); - return D3DPT_FORCE_DWORD; + return D3DPT_FORCE_DWORD; } - + return g_XboxPrimitiveTypeToHost[XboxPrimitiveType]; } @@ -1810,14 +1810,14 @@ typedef struct _RenderStateInfo { TXBType T = xt_Unknown; // The Xbox data type. Defaults to xt_Unknown. XTL::NV2AMETHOD M; // The related push buffer method. Not always a 1-to-1 mapping. Needs push-buffer interpretation & conversion code. D3DRENDERSTATETYPE PC = (D3DRENDERSTATETYPE)0; // Map XBox to PC render state - char *N; // XDK notes. Defaults to ''. + char *N; // XDK notes. Defaults to ''. WORD R; // The XDK version since which a render state was removed } RenderStateInfo; #define D3DRS_UNSUPPORTED ((D3DRENDERSTATETYPE)0) -extern const RenderStateInfo& GetDxbxRenderStateInfo(int State); +extern const RenderStateInfo& GetDxbxRenderStateInfo(int State); #endif diff --git a/src/core/hle/D3D8/XbD3D8Logging.h b/src/core/hle/D3D8/XbD3D8Logging.h index b8ed88877..0307cd8a9 100644 --- a/src/core/hle/D3D8/XbD3D8Logging.h +++ b/src/core/hle/D3D8/XbD3D8Logging.h @@ -27,17 +27,17 @@ #include "Logging.h" #include "XbD3D8Types.h" - -extern const char* D3DErrorString(HRESULT hResult); // Implemented in Direct3D9.cpp - -#define DEBUG_D3DRESULT(hRet, message) \ - do { \ - LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \ - if (FAILED(hRet)) \ - if(g_bPrintfOn) \ - printf("%s%s : %s D3D error (0x%.08X: %s)\n", _logThreadPrefix.c_str(), _logFuncPrefix.c_str(), message, hRet, D3DErrorString(hRet)); \ - } \ - } while (0) + +extern const char* D3DErrorString(HRESULT hResult); // Implemented in Direct3D9.cpp + +#define DEBUG_D3DRESULT(hRet, message) \ + do { \ + LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \ + if (FAILED(hRet)) \ + if(g_bPrintfOn) \ + printf("%s%s : %s D3D error (0x%.08X: %s)\n", _logThreadPrefix.c_str(), _logFuncPrefix.c_str(), message, hRet, D3DErrorString(hRet)); \ + } \ + } while (0) // Additional types, exclusively for logging (not really enums) : enum D3DVS20CAPS : int; @@ -71,8 +71,8 @@ enum X_D3DCOMMON_TYPE : int; enum X_D3DRESOURCE_COMMON : int; enum X_D3DRESOURCE_FORMAT : int; enum X_D3DRESOURCE_SIZE : int; - -} // end of namespace XTL + +} // end of namespace XTL // // Headers for rendering host D3D enum types : @@ -117,7 +117,7 @@ LOGRENDER_HEADER(D3DPSHADERCAPS2_0) LOGRENDER_HEADER(D3DCAPS) LOGRENDER_HEADER(D3DLOCKED_RECT) LOGRENDER_HEADER(RECT) - + namespace XTL { // @@ -160,4 +160,4 @@ LOGRENDER_HEADER(X_D3DPixelContainer) } // end of namespace XTL -#endif _EMU_D3D8_LOGGING_H +#endif _EMU_D3D8_LOGGING_H diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index 63e3b848a..f616cb17b 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -1,1247 +1,1247 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef XBD3D8TYPES_H -#define XBD3D8TYPES_H - -#undef UNICODE // make sure dxerr.h DXGetErrorString is aliassed to *A, not *W - -// include direct3d 9x headers -#define DIRECT3D_VERSION 0x0900 -#include -//implies #include // for D3DFORMAT, D3DLIGHT9, etc -//implies #include -#include // for D3DXVECTOR4, etc -#include - -#include -//#pragma comment(lib, "dxerr.lib") // See https://blogs.msdn.microsoft.com/chuckw/2012/04/24/wheres-dxerr-lib/ - -// If the above doesn't compile, install the June 2010 DirectX SDK -// from https://www.microsoft.com/en-us/download/details.aspx?id=6812 -// and select the Direct3D 9 include & library path (TODO : how?) - -// We're going to use the approach detailed in : -// https://blogs.msdn.microsoft.com/chuckw/2015/03/23/the-zombie-directx-sdk/ - - -// For transforming code that's written for Direct3D 8 into Direct3D 9, -// See "Converting to Direct3D 9" https://msdn.microsoft.com/en-us/library/windows/desktop/bb204851(v=vs.85).aspx - -// See https://msdn.microsoft.com/en-us/library/windows/desktop/bb204851(v=vs.85).aspx#D3DENUM_NO_WHQL_LEVEL_Changes -#define D3DENUM_NO_WHQL_LEVEL 0 // default in Direct3D 9 - -// Alias all host Direct3D 9 symbols to generic symbols -#define DXGetErrorString DXGetErrorString9A -#define DXGetErrorDescription DXGetErrorDescription9A -#define Direct3DCreate Direct3DCreate9 -#define D3DXAssembleShader D3DXAssembleShader -#define FullScreen_PresentationInterval PresentationInterval // a field in D3DPRESENT_PARAMETERS -#define D3DLockData void -#define PixelShaderConstantType float - -#define D3DADAPTER_IDENTIFIER D3DADAPTER_IDENTIFIER9 -#define D3DCAPS D3DCAPS9 -#define D3DVERTEXELEMENT D3DVERTEXELEMENT9 -#define D3DVIEWPORT D3DVIEWPORT9 - -#define IDirect3D IDirect3D9 -#define IDirect3DDevice IDirect3DDevice9 -#define IDirect3DStateBlock IDirect3DStateBlock9 // unused -#define IDirect3DVertexDeclaration IDirect3DVertexDeclaration9 -#define IDirect3DVertexShader IDirect3DVertexShader9 -#define IDirect3DPixelShader IDirect3DPixelShader9 -#define IDirect3DResource IDirect3DResource9 -#define IDirect3DBaseTexture IDirect3DBaseTexture9 -#define IDirect3DTexture IDirect3DTexture9 -#define IDirect3DVolumeTexture IDirect3DVolumeTexture9 -#define IDirect3DCubeTexture IDirect3DCubeTexture9 -#define IDirect3DVertexBuffer IDirect3DVertexBuffer9 -#define IDirect3DIndexBuffer IDirect3DIndexBuffer9 -#define IDirect3DSurface IDirect3DSurface9 -#define IDirect3DVolume IDirect3DVolume9 -#define IDirect3DSwapChain IDirect3DSwapChain9 -#define IDirect3DQuery IDirect3DQuery9 - -namespace XTL { - -// TODO : Declare these aliasses as Xbox type -typedef D3DLIGHT9 X_D3DLIGHT8; -typedef D3DMATERIAL9 X_D3DMATERIAL8; -typedef D3DVIEWPORT9 X_D3DVIEWPORT8; - -// TODO: fill out these enumeration tables for convienance -typedef D3DSWAPEFFECT X_D3DSWAPEFFECT; -typedef D3DXVECTOR4 X_D3DXVECTOR4; -typedef DWORD X_D3DBLENDOP; -typedef DWORD X_D3DBLEND; -typedef DWORD X_D3DCMPFUNC; -typedef DWORD X_D3DFILLMODE; -typedef DWORD X_D3DMULTISAMPLE_TYPE; -typedef DWORD X_D3DSHADEMODE; -typedef DWORD X_D3DSTENCILOP; -typedef DWORD X_D3DTEXTURESTAGESTATETYPE; - -typedef enum _X_D3DCULL -{ - X_D3DCULL_NONE = 0, - X_D3DCULL_CW = 0x900, - X_D3DCULL_CCW = 0x901, - X_D3DCULL_FORCE_DWORD = 0x7fffffff -} -X_D3DCULL; - -typedef enum _X_D3DFORMAT -{ -/* - Xbox1 D3DFORMAT notes - --------------------- - - The Xbox1 D3DFORMAT type consists of 4 different format categories : - 1. Swizzled (improves data locality, incompatible with native Direct3D) - 2. Compressed (DXT compression, giving 4:1 reduction on 4x4 pixel blocks) - 3. Linear (compatible with native Direct3D) - 4. Depth (Fixed or Floating point, stored Linear or Swizzled) - - Requirements\Format Swizzled Compressed Linear Depth Notes - - Power-of-two required ? YES YES NO NO - Mipmap supported ? YES YES NO YES Linear has MipmapLevels = 1 - CubeMaps supported ? YES YES NO NO Cubemaps have 6 faces - Supports volumes ? YES YES NO NO Volumes have 3 dimensions, Textures have 2 - Can be a rendertarget ? YES YES YES LINEAR Depth buffers can only be rendered to if stored Linear - - Implications : - - CubeMaps must be square - - Volumes cannot be cube mapped and vice versa - - Maximum dimensions : - 2D : 4096 x 4096 (12 mipmap levels) - 3D : 512 x 512 x 512 (9 mipmap levels) - -*/ - - // Xbox D3DFORMAT types : - // See https://wiki.beyondunreal.com/Legacy:Texture_Format - - // Swizzled Formats - - X_D3DFMT_L8 = 0x00, - X_D3DFMT_AL8 = 0x01, - X_D3DFMT_A1R5G5B5 = 0x02, - X_D3DFMT_X1R5G5B5 = 0x03, - X_D3DFMT_A4R4G4B4 = 0x04, - X_D3DFMT_R5G6B5 = 0x05, - X_D3DFMT_A8R8G8B8 = 0x06, - X_D3DFMT_X8R8G8B8 = 0x07, - X_D3DFMT_X8L8V8U8 = 0x07, // Alias - - X_D3DFMT_P8 = 0x0b, // 8-bit Palletized - - X_D3DFMT_A8 = 0x19, - X_D3DFMT_A8L8 = 0x1a, - X_D3DFMT_R6G5B5 = 0x27, - X_D3DFMT_L6V5U5 = 0x27, // Alias - - X_D3DFMT_G8B8 = 0x28, - X_D3DFMT_V8U8 = 0x28, // Alias - - X_D3DFMT_R8B8 = 0x29, - X_D3DFMT_D24S8 = 0x2a, - X_D3DFMT_F24S8 = 0x2b, - X_D3DFMT_D16 = 0x2c, - X_D3DFMT_D16_LOCKABLE = 0x2c, // Alias - - X_D3DFMT_F16 = 0x2d, - X_D3DFMT_L16 = 0x32, - X_D3DFMT_V16U16 = 0x33, - X_D3DFMT_R5G5B5A1 = 0x38, - X_D3DFMT_R4G4B4A4 = 0x39, - X_D3DFMT_A8B8G8R8 = 0x3A, - X_D3DFMT_Q8W8V8U8 = 0x3A, // Alias - - X_D3DFMT_B8G8R8A8 = 0x3B, - X_D3DFMT_R8G8B8A8 = 0x3C, - - // YUV Formats - - X_D3DFMT_YUY2 = 0x24, - X_D3DFMT_UYVY = 0x25, - - // Compressed Formats - - X_D3DFMT_DXT1 = 0x0C, // opaque/one-bit alpha - X_D3DFMT_DXT2 = 0x0E, // Alias for X_D3DFMT_DXT3 - X_D3DFMT_DXT3 = 0x0E, // linear alpha - X_D3DFMT_DXT4 = 0x0F, // Alias for X_D3DFMT_DXT5 - X_D3DFMT_DXT5 = 0x0F, // interpolated alpha - - // Linear Formats - - X_D3DFMT_LIN_A1R5G5B5 = 0x10, - X_D3DFMT_LIN_R5G6B5 = 0x11, - X_D3DFMT_LIN_A8R8G8B8 = 0x12, - X_D3DFMT_LIN_L8 = 0x13, - X_D3DFMT_LIN_R8B8 = 0x16, - X_D3DFMT_LIN_G8B8 = 0x17, - X_D3DFMT_LIN_V8U8 = 0x17, // Alias - - X_D3DFMT_LIN_AL8 = 0x1b, - X_D3DFMT_LIN_X1R5G5B5 = 0x1c, - X_D3DFMT_LIN_A4R4G4B4 = 0x1d, - X_D3DFMT_LIN_X8R8G8B8 = 0x1e, - X_D3DFMT_LIN_X8L8V8U8 = 0x1e, // Alias - - X_D3DFMT_LIN_A8 = 0x1f, - X_D3DFMT_LIN_A8L8 = 0x20, - X_D3DFMT_LIN_D24S8 = 0x2E, - X_D3DFMT_LIN_F24S8 = 0x2f, - X_D3DFMT_LIN_D16 = 0x30, - X_D3DFMT_LIN_F16 = 0x31, - X_D3DFMT_LIN_L16 = 0x35, - X_D3DFMT_LIN_V16U16 = 0x36, - X_D3DFMT_LIN_R6G5B5 = 0x37, - X_D3DFMT_LIN_L6V5U5 = 0x37, // Alias - - X_D3DFMT_LIN_R5G5B5A1 = 0x3D, - X_D3DFMT_LIN_R4G4B4A4 = 0x3e, - X_D3DFMT_LIN_A8B8G8R8 = 0x3f, - X_D3DFMT_LIN_B8G8R8A8 = 0x40, - X_D3DFMT_LIN_R8G8B8A8 = 0x41, - - X_D3DFMT_VERTEXDATA = 0x64, - - X_D3DFMT_INDEX16 = 101/*=D3DFMT_INDEX16*/, // Dxbx addition : Not an Xbox format, used internally - - X_D3DFMT_UNKNOWN = 0xFFFFFFFF - 3, // Unique declaration to make overloads possible -} -X_D3DFORMAT, *PX_D3DFORMAT; - -// Primitives supported by draw-primitive API -typedef enum _X_D3DPRIMITIVETYPE -{ - X_D3DPT_NONE = 0, // Dxbx addition - - X_D3DPT_POINTLIST = 1, - X_D3DPT_LINELIST = 2, - X_D3DPT_LINELOOP = 3, // Xbox only - X_D3DPT_LINESTRIP = 4, - X_D3DPT_TRIANGLELIST = 5, - X_D3DPT_TRIANGLESTRIP = 6, - X_D3DPT_TRIANGLEFAN = 7, - X_D3DPT_QUADLIST = 8, // Xbox only - X_D3DPT_QUADSTRIP = 9, // Xbox only - X_D3DPT_POLYGON = 10, // Xbox only - - X_D3DPT_MAX = 11, - X_D3DPT_INVALID = 0x7fffffff, /* force 32-bit size enum */ -} -X_D3DPRIMITIVETYPE; - -typedef enum _X_D3DRESOURCETYPE -{ - X_D3DRTYPE_NONE = 0, - X_D3DRTYPE_SURFACE = 1, // = D3DRESOURCETYPE.D3DRTYPE_SURFACE - X_D3DRTYPE_VOLUME = 2, // = D3DRESOURCETYPE.D3DRTYPE_VOLUME - X_D3DRTYPE_TEXTURE = 3, // = D3DRESOURCETYPE.D3DRTYPE_TEXTURE - X_D3DRTYPE_VOLUMETEXTURE = 4, // = D3DRESOURCETYPE.D3DRTYPE_VOLUMETEXTURE - X_D3DRTYPE_CUBETEXTURE = 5, // = D3DRESOURCETYPE.D3DRTYPE_CUBETEXTURE - X_D3DRTYPE_VERTEXBUFFER = 6, // = D3DRESOURCETYPE.D3DRTYPE_VERTEXBUFFER - X_D3DRTYPE_INDEXBUFFER = 7, // = D3DRESOURCETYPE.D3DRTYPE_INDEXBUFFER - X_D3DRTYPE_PUSHBUFFER = 8, - X_D3DRTYPE_PALETTE = 9, - X_D3DRTYPE_FIXUP = 10, - - X_D3DRTYPE_FORCE_DWORD = 0x7fffffff -} -X_D3DRESOURCETYPE; - -// D3DUSAGE values (all but the Xbox extensions match the PC versions) : -#define X_D3DUSAGE_RENDERTARGET 0x00000001 -#define X_D3DUSAGE_DEPTHSTENCIL 0x00000002 -// for Vertex/Index buffers -#define X_D3DUSAGE_WRITEONLY 0x00000008 -#define X_D3DUSAGE_POINTS 0x00000040 -#define X_D3DUSAGE_RTPATCHES 0x00000080 -#define X_D3DUSAGE_DYNAMIC 0x00000200 -// for CreateVertexShader -#define X_D3DUSAGE_PERSISTENTDIFFUSE 0x00000400L // Xbox-only -#define X_D3DUSAGE_PERSISTENTSPECULAR 0x00000800L // Xbox-only -#define X_D3DUSAGE_PERSISTENTBACKDIFFUSE 0x00001000L // Xbox-only -#define X_D3DUSAGE_PERSISTENTBACKSPECULAR 0x00002000L // Xbox-only -// for CreateTexture/CreateImageSurface -#define X_D3DUSAGE_BORDERSOURCE_COLOR 0x00000000L // Xbox-only -#define X_D3DUSAGE_BORDERSOURCE_TEXTURE 0x00010000L // Xbox-only - -#define X_D3D_RENDER_MEMORY_ALIGNMENT 64 - -#define X_D3DSURFACE_ALIGNMENT X_D3D_RENDER_MEMORY_ALIGNMENT -#define X_D3DTEXTURE_ALIGNMENT (2 * X_D3D_RENDER_MEMORY_ALIGNMENT) -#define X_D3DTEXTURE_CUBEFACE_ALIGNMENT (2 * X_D3D_RENDER_MEMORY_ALIGNMENT) -#define X_D3DTEXTURE_PITCH_ALIGNMENT X_D3D_RENDER_MEMORY_ALIGNMENT -#define X_D3DTEXTURE_PITCH_MIN X_D3DTEXTURE_PITCH_ALIGNMENT - -typedef enum _X_D3DSET_DEPTH_CLIP_PLANES_FLAGS -{ - X_D3DSDCP_SET_VERTEXPROGRAM_PLANES = 1, - X_D3DSDCP_SET_FIXEDFUNCTION_PLANES = 2, - X_D3DSDCP_USE_DEFAULT_VERTEXPROGRAM_PLANES = 3, - X_D3DSDCP_USE_DEFAULT_FIXEDFUNCTION_PLANES = 4, -} -X_D3DSET_DEPTH_CLIP_PLANES_FLAGS; - -#define X_D3DPRESENTFLAG_LOCKABLE_BACKBUFFER 0x00000001 -#define X_D3DPRESENTFLAG_WIDESCREEN 0x00000010 -#define X_D3DPRESENTFLAG_INTERLACED 0x00000020 -#define X_D3DPRESENTFLAG_PROGRESSIVE 0x00000040 -#define X_D3DPRESENTFLAG_FIELD 0x00000080 -#define X_D3DPRESENTFLAG_10X11PIXELASPECTRATIO 0x00000100 - -typedef struct _X_D3DDISPLAYMODE -{ - UINT Width; - UINT Height; - UINT RefreshRate; - DWORD Flags; - X_D3DFORMAT Format; -} -X_D3DDISPLAYMODE; - -typedef struct _X_D3DVERTEXBUFFER_DESC -{ - X_D3DFORMAT Format; - X_D3DRESOURCETYPE Type; -} -X_D3DVERTEXBUFFER_DESC; - -typedef struct _X_D3DINDEXBUFFER_DESC -{ - X_D3DFORMAT Format; - X_D3DRESOURCETYPE Type; -} -X_D3DINDEXBUFFER_DESC; - -typedef struct _X_D3DSURFACE_DESC -{ - X_D3DFORMAT Format; - X_D3DRESOURCETYPE Type; - DWORD Usage; - UINT Size; - X_D3DMULTISAMPLE_TYPE MultiSampleType; - UINT Width; - UINT Height; -} -X_D3DSURFACE_DESC; - -struct X_D3DSurface; // forward -typedef struct _X_D3DPRESENT_PARAMETERS -{ - UINT BackBufferWidth; - UINT BackBufferHeight; - X_D3DFORMAT BackBufferFormat; - UINT BackBufferCount; - - X_D3DMULTISAMPLE_TYPE MultiSampleType; - - X_D3DSWAPEFFECT SwapEffect; - HWND hDeviceWindow; - BOOL Windowed; - BOOL EnableAutoDepthStencil; - X_D3DFORMAT AutoDepthStencilFormat; - DWORD Flags; - - UINT FullScreen_RefreshRateInHz; - UINT FullScreen_PresentationInterval; - // The Windows DirectX8 variant ends here - // This check guarantees identical layout, compared to Direct3D8._D3DPRESENT_PARAMETERS_: - // assert(offsetof(X_D3DPRESENT_PARAMETERS, BufferSurfaces) == sizeof(_D3DPRESENT_PARAMETERS_)); - X_D3DSurface *BufferSurfaces[3]; - X_D3DSurface *DepthStencilSurface; -} -X_D3DPRESENT_PARAMETERS; - -typedef struct _X_D3DGAMMARAMP -{ - BYTE red[256]; - BYTE green[256]; - BYTE blue[256]; -} -X_D3DGAMMARAMP; - -typedef struct _X_D3DPIXELSHADERDEF // <- blueshogun 10/1/07 -{ - DWORD PSAlphaInputs[8]; // X_D3DRS_PSALPHAINPUTS0..X_D3DRS_PSALPHAINPUTS7 : Alpha inputs for each stage - DWORD PSFinalCombinerInputsABCD; // X_D3DRS_PSFINALCOMBINERINPUTSABCD : Final combiner inputs - DWORD PSFinalCombinerInputsEFG; // X_D3DRS_PSFINALCOMBINERINPUTSEFG : Final combiner inputs (continued) - DWORD PSConstant0[8]; // X_D3DRS_PSCONSTANT0_0..X_D3DRS_PSCONSTANT0_7 : C0 for each stage - DWORD PSConstant1[8]; // X_D3DRS_PSCONSTANT1_0..X_D3DRS_PSCONSTANT1_7 : C1 for each stage - DWORD PSAlphaOutputs[8]; // X_D3DRS_PSALPHAOUTPUTS0..X_D3DRS_PSALPHAOUTPUTS7 : Alpha output for each stage - DWORD PSRGBInputs[8]; // X_D3DRS_PSRGBINPUTS0..X_D3DRS_PSRGBINPUTS7 : RGB inputs for each stage - DWORD PSCompareMode; // X_D3DRS_PSCOMPAREMODE : Compare modes for clipplane texture mode - DWORD PSFinalCombinerConstant0; // X_D3DRS_PSFINALCOMBINERCONSTANT0 : C0 in final combiner - DWORD PSFinalCombinerConstant1; // X_D3DRS_PSFINALCOMBINERCONSTANT1 : C1 in final combiner - DWORD PSRGBOutputs[8]; // X_D3DRS_PSRGBOUTPUTS0..X_D3DRS_PSRGBOUTPUTS7 : Stage 0 RGB outputs - DWORD PSCombinerCount; // X_D3DRS_PSCOMBINERCOUNT : Active combiner count (Stages 0-7) - DWORD PSTextureModes; // X_D3DRS_PSTEXTUREMODES: Texture addressing modes - DWORD PSDotMapping; // X_D3DRS_PSDOTMAPPING : Input mapping for dot product modes - DWORD PSInputTexture; // X_D3DRS_PSINPUTTEXTURE : Texture source for some texture modes - - // These last three DWORDs are used to define how Direct3D8 pixel shader constants map to the constant - // registers in each combiner stage. They are used by the Direct3D run-time software but not by the hardware. - DWORD PSC0Mapping; // Mapping of c0 regs to D3D constants - DWORD PSC1Mapping; // Mapping of c1 regs to D3D constants - DWORD PSFinalCombinerConstants; // Final combiner constant mapping -} -X_D3DPIXELSHADERDEF; - - -typedef struct _X_PixelShader -{ - DWORD RefCount; - DWORD D3DOwned; - X_D3DPIXELSHADERDEF *pPSDef; -} X_PixelShader; - -struct X_D3DResource -{ - DWORD Common; - DWORD Data; - DWORD Lock; -}; - -// D3D resource "common" masks -#define X_D3DCOMMON_REFCOUNT_MASK 0x0000FFFF -#define X_D3DCOMMON_TYPE_MASK 0x00070000 -#define X_D3DCOMMON_TYPE_SHIFT 16 -#define X_D3DCOMMON_TYPE_VERTEXBUFFER 0x00000000 -#define X_D3DCOMMON_TYPE_INDEXBUFFER 0x00010000 -#define X_D3DCOMMON_TYPE_PUSHBUFFER 0x00020000 -#define X_D3DCOMMON_TYPE_PALETTE 0x00030000 -#define X_D3DCOMMON_TYPE_TEXTURE 0x00040000 -#define X_D3DCOMMON_TYPE_SURFACE 0x00050000 // Also covers Volume resources -#define X_D3DCOMMON_TYPE_FIXUP 0x00060000 -#define X_D3DCOMMON_INTREFCOUNT_MASK 0x00780000 -#define X_D3DCOMMON_INTREFCOUNT_SHIFT 19 -#define X_D3DCOMMON_INTREFCOUNT_1 (1 << X_D3DCOMMON_INTREFCOUNT_SHIFT) // Dxbx addition -#define X_D3DCOMMON_VIDEOMEMORY 0x00000000 // Was 0x00800000, but Xbox doesn't have this flag! -#define X_D3DCOMMON_D3DCREATED 0x01000000 -#define X_D3DCOMMON_ISLOCKED 0x02000010 // Surface is currently locked (potential unswizzle candidate) -#define X_D3DCOMMON_UNUSED_MASK 0xFE000000 -#define X_D3DCOMMON_UNUSED_SHIFT 25 - -// d3d palette common -#define X_D3DPALETTE_COMMON_PALETTESIZE_MASK 0xC0000000 -#define X_D3DPALETTE_COMMON_PALETTESIZE_SHIFT 30 - -// special resource lock flags -#define X_D3DRESOURCE_LOCK_FLAG_NOSIZE 0xEFFFFFFF - -// Lock flags -#define X_D3DLOCK_NOFLUSH 0x00000010 // Xbox extension -#define X_D3DLOCK_NOOVERWRITE 0x00000020 -#define X_D3DLOCK_TILED 0x00000040 // Xbox extension -#define X_D3DLOCK_READONLY 0x00000080 - -// Multisample modes -const int X_D3DMULTISAMPLE_NONE = 0x0011; -const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR = 0x1021; -const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX = 0x1121; -const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_HORIZONTAL_LINEAR = 0x2021; -const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_VERTICAL_LINEAR = 0x2012; -const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR = 0x1022; -const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1222; -const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_LINEAR = 0x2022; -const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2222; -const int X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1233; -const int X_D3DMULTISAMPLE_9_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2233; - -// Multisample masks (Cxbx additions) -const int X_D3DMULTISAMPLE_YSCALE_MASK = 0x0003; -const int X_D3DMULTISAMPLE_YSCALE_SHIFT = 0; - -const int X_D3DMULTISAMPLE_XSCALE_MASK = 0x0030; -const int X_D3DMULTISAMPLE_XSCALE_SHIFT = 4; - -const int X_D3DMULTISAMPLE_ALGO_LINEAR = 0x0000; -const int X_D3DMULTISAMPLE_ALGO_QUINCUNX = 0x0100; -const int X_D3DMULTISAMPLE_ALGO_GAUSSIAN = 0x0200; -const int X_D3DMULTISAMPLE_ALGO_MASK = 0x0300; - -const int X_D3DMULTISAMPLE_SAMPLING_NONE = 0x0000; -const int X_D3DMULTISAMPLE_SAMPLING_MULTI = 0x1000; -const int X_D3DMULTISAMPLE_SAMPLING_SUPER = 0x2000; -const int X_D3DMULTISAMPLE_SAMPLING_MASK = 0x3000; - -const int X_D3DMULTISAMPLE_KNOWN_MASK = 0 - | X_D3DMULTISAMPLE_YSCALE_MASK - | X_D3DMULTISAMPLE_XSCALE_MASK - | X_D3DMULTISAMPLE_ALGO_MASK - | X_D3DMULTISAMPLE_SAMPLING_MASK - ; - - -struct X_D3DVertexBuffer : public X_D3DResource -{ - -}; -struct X_D3DIndexBuffer : public X_D3DResource -{ - -}; - -struct X_D3DPushBuffer : public X_D3DResource -{ - ULONG Size; - ULONG AllocationSize; -}; - -struct X_D3DFixup : public X_D3DResource -{ - ULONG Run; - ULONG Next; - ULONG Size; -}; - -struct X_D3DPalette : public X_D3DResource -{ -}; - -typedef enum _X_D3DPALETTESIZE -{ - D3DPALETTE_256 = 0, - D3DPALETTE_128 = 1, - D3DPALETTE_64 = 2, - D3DPALETTE_32 = 3, - D3DPALETTE_MAX = 4, - D3DPALETTE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} -X_D3DPALETTESIZE; - -struct X_D3DPixelContainer : public X_D3DResource -{ - DWORD Format; // Format information about the texture. - DWORD Size; // Size of a non power-of-2 texture, must be zero otherwise -}; - -// pixel container "format" masks -#define X_D3DFORMAT_RESERVED1_MASK 0x00000003 // Must be zero -#define X_D3DFORMAT_DMACHANNEL_MASK 0x00000003 -#define X_D3DFORMAT_DMACHANNEL_A 0x00000001 // DMA channel A - the default for all system memory -#define X_D3DFORMAT_DMACHANNEL_B 0x00000002 // DMA channel B - unused -#define X_D3DFORMAT_CUBEMAP 0x00000004 // Set if the texture if a cube map -#define X_D3DFORMAT_BORDERSOURCE_COLOR 0x00000008 // If set, uses D3DTSS_BORDERCOLOR as a border -#define X_D3DFORMAT_DIMENSION_MASK 0x000000F0 // # of dimensions, must be 2 or 3 -#define X_D3DFORMAT_DIMENSION_SHIFT 4 -#define X_D3DFORMAT_FORMAT_MASK 0x0000FF00 // D3DFORMAT - See X_D3DFMT_* above -#define X_D3DFORMAT_FORMAT_SHIFT 8 -#define X_D3DFORMAT_MIPMAP_MASK 0x000F0000 // # mipmap levels (always 1 for surfaces) -#define X_D3DFORMAT_MIPMAP_SHIFT 16 -#define X_D3DFORMAT_USIZE_MASK 0x00F00000 // Log 2 of the U size of the base texture (only set for swizzled or compressed) -#define X_D3DFORMAT_USIZE_SHIFT 20 -#define X_D3DFORMAT_VSIZE_MASK 0x0F000000 // Log 2 of the V size of the base texture (only set for swizzled or compressed) -#define X_D3DFORMAT_VSIZE_SHIFT 24 -#define X_D3DFORMAT_PSIZE_MASK 0xF0000000 // Log 2 of the P size of the base texture (only set for swizzled or compressed) -#define X_D3DFORMAT_PSIZE_SHIFT 28 - -// pixel container "size" masks -// The layout of the size field, used for non swizzled or compressed textures. -// -// The Size field of a container will be zero if the texture is swizzled or compressed. -// It is guarenteed to be non-zero otherwise because either the height/width will be -// greater than one or the pitch adjust will be nonzero because the minimum texture -// pitch is 8 bytes. -#define X_D3DSIZE_WIDTH_MASK 0x00000FFF // Width of the texture - 1, in texels -//#define X_D3DSIZE_WIDTH_SHIFT 0 -#define X_D3DSIZE_HEIGHT_MASK 0x00FFF000 // Height of the texture - 1, in texels -#define X_D3DSIZE_HEIGHT_SHIFT 12 -#define X_D3DSIZE_PITCH_MASK 0xFF000000 // Pitch / 64 - 1 -#define X_D3DSIZE_PITCH_SHIFT 24 - - -struct X_D3DBaseTexture : public X_D3DPixelContainer -{ - -}; - -struct X_D3DTexture : public X_D3DBaseTexture -{ - -}; - -struct X_D3DVolumeTexture : public X_D3DBaseTexture -{ - -}; - -struct X_D3DCubeTexture : public X_D3DBaseTexture -{ - -}; - -struct X_D3DVolume : public X_D3DPixelContainer -{ - X_D3DBaseTexture *Parent; -}; - -struct X_D3DSurface : public X_D3DPixelContainer -{ - X_D3DBaseTexture *Parent; -}; - -struct X_D3DTILE -{ - DWORD Flags; - PVOID pMemory; - DWORD Size; - DWORD Pitch; - DWORD ZStartTag; - DWORD ZOffset; -}; - -typedef enum _X_D3DCALLBACKTYPE // blueshogun96 10/1/07 -{ - X_D3DCALLBACK_READ = 0, // Fixed PatrickvL 10/7/22 - X_D3DCALLBACK_WRITE = 1 -} -X_D3DCALLBACKTYPE; - -typedef enum _X_D3DFIELDTYPE -{ - X_D3DFIELD_ODD = 1, - X_D3DFIELD_EVEN = 2, - X_D3DFIELD_PROGRESSIVE = 3, - X_D3DFIELD_FORCE_DWORD = 0x7fffffff -} -X_D3DFIELDTYPE; - -typedef struct _X_D3DFIELD_STATUS -{ - X_D3DFIELDTYPE Field; - UINT VBlankCount; -} -X_D3DFIELD_STATUS; - -typedef struct _D3DVBLANKDATA -{ - DWORD VBlank; - DWORD Swap; - DWORD Flags; -} -X_D3DVBLANKDATA; - -typedef struct _D3DSWAPDATA -{ - DWORD Swap; - DWORD SwapVBlank; - DWORD MissedVBlanks; - DWORD TimeUntilSwapVBlank; - DWORD TimeBetweenSwapVBlanks; -} -X_D3DSWAPDATA; - -// D3DVBLANKCALLBACK -typedef void (__cdecl * X_D3DVBLANKCALLBACK)(X_D3DVBLANKDATA *pData); - -// D3DSWAPCALLBACK -typedef void (__cdecl * X_D3DSWAPCALLBACK)(X_D3DSWAPDATA *pData); - -// D3DCALLBACK -typedef void (__cdecl * X_D3DCALLBACK)(DWORD Context); - -// X_D3DRENDERSTATETYPE values -typedef enum _X_D3DRENDERSTATETYPE { - - // Dxbx note : These declarations are from XDK version 5933, the most recent and complete version. - // Older versions are slightly different (some members are missing), so we use a mapping table to - // cater for the differences (see XboxRenderStateConverter::BuildRenderStateMappingTable). This enables to ignore these - // version-differences in the rest of our code (unless it matters somehow); We write like this : - // XboxRenderStates.SetXboxRenderState(X_D3DRENDERSTATETYPE, Value); - // - // And we read like this (do note, that missing elements all point to the same dummy) : - // Result = XboxRenderStates.GetXboxRenderState(X_D3DRENDERSTATETYPE); - - // Dxbx note : The PS* render states map 1-on-1 to the X_D3DPIXELSHADERDEF record, - // SetPixelShader actually pushes the definition into these render state slots. - // See DxbxUpdateActivePixelShader for how this is employed. - - // The set starts out with "pixel-shader" render states (all Xbox extensions) : - X_D3DRS_PSALPHAINPUTS0 = 0, - X_D3DRS_PSALPHAINPUTS1 = 1, - X_D3DRS_PSALPHAINPUTS2 = 2, - X_D3DRS_PSALPHAINPUTS3 = 3, - X_D3DRS_PSALPHAINPUTS4 = 4, - X_D3DRS_PSALPHAINPUTS5 = 5, - X_D3DRS_PSALPHAINPUTS6 = 6, - X_D3DRS_PSALPHAINPUTS7 = 7, - X_D3DRS_PSFINALCOMBINERINPUTSABCD = 8, - X_D3DRS_PSFINALCOMBINERINPUTSEFG = 9, - X_D3DRS_PSCONSTANT0_0 = 10, - X_D3DRS_PSCONSTANT0_1 = 11, - X_D3DRS_PSCONSTANT0_2 = 12, - X_D3DRS_PSCONSTANT0_3 = 13, - X_D3DRS_PSCONSTANT0_4 = 14, - X_D3DRS_PSCONSTANT0_5 = 15, - X_D3DRS_PSCONSTANT0_6 = 16, - X_D3DRS_PSCONSTANT0_7 = 17, - X_D3DRS_PSCONSTANT1_0 = 18, - X_D3DRS_PSCONSTANT1_1 = 19, - X_D3DRS_PSCONSTANT1_2 = 20, - X_D3DRS_PSCONSTANT1_3 = 21, - X_D3DRS_PSCONSTANT1_4 = 22, - X_D3DRS_PSCONSTANT1_5 = 23, - X_D3DRS_PSCONSTANT1_6 = 24, - X_D3DRS_PSCONSTANT1_7 = 25, - X_D3DRS_PSALPHAOUTPUTS0 = 26, - X_D3DRS_PSALPHAOUTPUTS1 = 27, - X_D3DRS_PSALPHAOUTPUTS2 = 28, - X_D3DRS_PSALPHAOUTPUTS3 = 29, - X_D3DRS_PSALPHAOUTPUTS4 = 30, - X_D3DRS_PSALPHAOUTPUTS5 = 31, - X_D3DRS_PSALPHAOUTPUTS6 = 32, - X_D3DRS_PSALPHAOUTPUTS7 = 33, - X_D3DRS_PSRGBINPUTS0 = 34, - X_D3DRS_PSRGBINPUTS1 = 35, - X_D3DRS_PSRGBINPUTS2 = 36, - X_D3DRS_PSRGBINPUTS3 = 37, - X_D3DRS_PSRGBINPUTS4 = 38, - X_D3DRS_PSRGBINPUTS5 = 39, - X_D3DRS_PSRGBINPUTS6 = 40, - X_D3DRS_PSRGBINPUTS7 = 41, - X_D3DRS_PSCOMPAREMODE = 42, - X_D3DRS_PSFINALCOMBINERCONSTANT0 = 43, - X_D3DRS_PSFINALCOMBINERCONSTANT1 = 44, - X_D3DRS_PSRGBOUTPUTS0 = 45, - X_D3DRS_PSRGBOUTPUTS1 = 46, - X_D3DRS_PSRGBOUTPUTS2 = 47, - X_D3DRS_PSRGBOUTPUTS3 = 48, - X_D3DRS_PSRGBOUTPUTS4 = 49, - X_D3DRS_PSRGBOUTPUTS5 = 50, - X_D3DRS_PSRGBOUTPUTS6 = 51, - X_D3DRS_PSRGBOUTPUTS7 = 52, - X_D3DRS_PSCOMBINERCOUNT = 53, - X_D3DRS_PS_RESERVED = 54, // Dxbx note : This takes the slot of X_D3DPIXELSHADERDEF.PSTextureModes, set by D3DDevice_SetRenderState_LogicOp? - X_D3DRS_PSDOTMAPPING = 55, - X_D3DRS_PSINPUTTEXTURE = 56, - // End of "pixel-shader" render states, continuing with "simple" render states : - X_D3DRS_ZFUNC = 57, // D3DCMPFUNC - X_D3DRS_ALPHAFUNC = 58, // D3DCMPFUNC - X_D3DRS_ALPHABLENDENABLE = 59, // TRUE to enable alpha blending - X_D3DRS_ALPHATESTENABLE = 60, // TRUE to enable alpha tests - X_D3DRS_ALPHAREF = 61, // BYTE - X_D3DRS_SRCBLEND = 62, // D3DBLEND - X_D3DRS_DESTBLEND = 63, // D3DBLEND - X_D3DRS_ZWRITEENABLE = 64, // TRUE to enable Z writes - X_D3DRS_DITHERENABLE = 65, // TRUE to enable dithering - X_D3DRS_SHADEMODE = 66, // D3DSHADEMODE - X_D3DRS_COLORWRITEENABLE = 67, // D3DCOLORWRITEENABLE_ALPHA, etc. per-channel write enable - X_D3DRS_STENCILZFAIL = 68, // D3DSTENCILOP to do if stencil test passes and Z test fails - X_D3DRS_STENCILPASS = 69, // D3DSTENCILOP to do if both stencil and Z tests pass - X_D3DRS_STENCILFUNC = 70, // D3DCMPFUNC - X_D3DRS_STENCILREF = 71, // BYTE reference value used in stencil test - X_D3DRS_STENCILMASK = 72, // BYTE mask value used in stencil test - X_D3DRS_STENCILWRITEMASK = 73, // BYTE write mask applied to values written to stencil buffer - X_D3DRS_BLENDOP = 74, // D3DBLENDOP setting - X_D3DRS_BLENDCOLOR = 75, // D3DCOLOR for D3DBLEND_CONSTANTCOLOR - X_D3DRS_SWATHWIDTH = 76, // D3DSWATHWIDTH (Xbox ext.) - X_D3DRS_POLYGONOFFSETZSLOPESCALE = 77, // float Z factor for shadow maps (Xbox ext.) - X_D3DRS_POLYGONOFFSETZOFFSET = 78, // Xbox ext. - X_D3DRS_POINTOFFSETENABLE = 79, // Xbox ext. - X_D3DRS_WIREFRAMEOFFSETENABLE = 80, // Xbox ext. - X_D3DRS_SOLIDOFFSETENABLE = 81, // Xbox ext. - X_D3DRS_DEPTHCLIPCONTROL = 82, // [4432+] Xbox ext. - X_D3DRS_STIPPLEENABLE = 83, // [4627+] Xbox ext. - X_D3DRS_SIMPLE_UNUSED8 = 84, // [4627+] - X_D3DRS_SIMPLE_UNUSED7 = 85, // [4627+] - X_D3DRS_SIMPLE_UNUSED6 = 86, // [4627+] - X_D3DRS_SIMPLE_UNUSED5 = 87, // [4627+] - X_D3DRS_SIMPLE_UNUSED4 = 88, // [4627+] - X_D3DRS_SIMPLE_UNUSED3 = 89, // [4627+] - X_D3DRS_SIMPLE_UNUSED2 = 90, // [4627+] - X_D3DRS_SIMPLE_UNUSED1 = 91, // [4627+] - // End of "simple" render states, continuing with "deferred" render states : - X_D3DRS_FOGENABLE = 92, - X_D3DRS_FOGTABLEMODE = 93, - X_D3DRS_FOGSTART = 94, - X_D3DRS_FOGEND = 95, - X_D3DRS_FOGDENSITY = 96, - X_D3DRS_RANGEFOGENABLE = 97, - X_D3DRS_WRAP0 = 98, - X_D3DRS_WRAP1 = 99, - X_D3DRS_WRAP2 = 100, // Dxbx addition - X_D3DRS_WRAP3 = 101, // Dxbx addition - X_D3DRS_LIGHTING = 102, - X_D3DRS_SPECULARENABLE = 103, - X_D3DRS_LOCALVIEWER = 104, // Dxbx addition - X_D3DRS_COLORVERTEX = 105, - X_D3DRS_BACKSPECULARMATERIALSOURCE = 106, // Xbox ext. nsp. - X_D3DRS_BACKDIFFUSEMATERIALSOURCE = 107, // Xbox ext. nsp. - X_D3DRS_BACKAMBIENTMATERIALSOURCE = 108, // Xbox ext. nsp. - X_D3DRS_BACKEMISSIVEMATERIALSOURCE = 109, // Xbox ext. nsp. - X_D3DRS_SPECULARMATERIALSOURCE = 110, - X_D3DRS_DIFFUSEMATERIALSOURCE = 111, - X_D3DRS_AMBIENTMATERIALSOURCE = 112, - X_D3DRS_EMISSIVEMATERIALSOURCE = 113, - X_D3DRS_BACKAMBIENT = 114, // Xbox ext. nsp. - X_D3DRS_AMBIENT = 115, - X_D3DRS_POINTSIZE = 116, - X_D3DRS_POINTSIZE_MIN = 117, - X_D3DRS_POINTSPRITEENABLE = 118, - X_D3DRS_POINTSCALEENABLE = 119, - X_D3DRS_POINTSCALE_A = 120, - X_D3DRS_POINTSCALE_B = 121, - X_D3DRS_POINTSCALE_C = 122, - X_D3DRS_POINTSIZE_MAX = 123, - X_D3DRS_PATCHEDGESTYLE = 124, // Dxbx addition - X_D3DRS_PATCHSEGMENTS = 125, - X_D3DRS_SWAPFILTER = 126, // [4039+] Xbox ext. nsp. D3DTEXF_LINEAR etc. filter to use for Swap - X_D3DRS_PRESENTATIONINTERVAL = 127, // [4627+] Xbox ext. nsp. TODO : Use 4361? - X_D3DRS_DEFERRED_UNUSED8 = 128, // [4627+] - X_D3DRS_DEFERRED_UNUSED7 = 129, // [4627+] - X_D3DRS_DEFERRED_UNUSED6 = 130, // [4627+] - X_D3DRS_DEFERRED_UNUSED5 = 131, // [4627+] - X_D3DRS_DEFERRED_UNUSED4 = 132, // [4627+] - X_D3DRS_DEFERRED_UNUSED3 = 133, // [4627+] - X_D3DRS_DEFERRED_UNUSED2 = 134, // [4627+] - X_D3DRS_DEFERRED_UNUSED1 = 135, // [4627+] - // End of "deferred" render states, continuing with "complex" render states : - X_D3DRS_PSTEXTUREMODES = 136, // Xbox ext. - X_D3DRS_VERTEXBLEND = 137, - X_D3DRS_FOGCOLOR = 138, - X_D3DRS_FILLMODE = 139, - X_D3DRS_BACKFILLMODE = 140, // Dxbx addition : Xbox ext. nsp. - X_D3DRS_TWOSIDEDLIGHTING = 141, // Dxbx addition : Xbox ext. nsp. - X_D3DRS_NORMALIZENORMALS = 142, - X_D3DRS_ZENABLE = 143, - X_D3DRS_STENCILENABLE = 144, - X_D3DRS_STENCILFAIL = 145, - X_D3DRS_FRONTFACE = 146, // Dxbx addition : Xbox ext. nsp. - X_D3DRS_CULLMODE = 147, - X_D3DRS_TEXTUREFACTOR = 148, - X_D3DRS_ZBIAS = 149, - X_D3DRS_LOGICOP = 150, // Xbox ext. nsp. - X_D3DRS_EDGEANTIALIAS = 151, // Dxbx note : No Xbox ext. (according to Direct3D8) ! - X_D3DRS_MULTISAMPLEANTIALIAS = 152, - X_D3DRS_MULTISAMPLEMASK = 153, - X_D3DRS_MULTISAMPLETYPE = 154, // [-4039] Xbox ext. - // Note : X_D3DRS_MULTISAMPLETYPE seems the only one that got removed, but it does need a slot, so the rest is increased by 1 compared to 5933. - X_D3DRS_MULTISAMPLEMODE = 155, // [4361+] Xbox ext. // D3DMULTISAMPLEMODE for the backbuffer - X_D3DRS_MULTISAMPLERENDERTARGETMODE = 156, // [4039+] Xbox ext. - X_D3DRS_SHADOWFUNC = 157, // D3DCMPFUNC (Xbox extension) - X_D3DRS_LINEWIDTH = 158, // Xbox ext. - X_D3DRS_SAMPLEALPHA = 159, // Xbox ext. - X_D3DRS_DXT1NOISEENABLE = 160, // Xbox ext. - X_D3DRS_YUVENABLE = 161, // [3911+] Xbox ext. - X_D3DRS_OCCLUSIONCULLENABLE = 162, // [3911+] Xbox ext. - X_D3DRS_STENCILCULLENABLE = 163, // [3911+] Xbox ext. - X_D3DRS_ROPZCMPALWAYSREAD = 164, // [3911+] Xbox ext. - X_D3DRS_ROPZREAD = 165, // [3911+] Xbox ext. - X_D3DRS_DONOTCULLUNCOMPRESSED = 166, // [3911+] Xbox ext. - // End of "complex" render states. - X_D3DRS_UNK = 0x7fffffff // deferred render state "unknown" flag -} X_D3DRENDERSTATETYPE; - -// Render state boundaries : - -#define X_D3DRS_PS_FIRST X_D3DRS_PSALPHAINPUTS0 -#define X_D3DRS_PS_LAST X_D3DRS_PSINPUTTEXTURE - -#define X_D3DRS_SIMPLE_FIRST X_D3DRS_ZFUNC -#define X_D3DRS_SIMPLE_LAST X_D3DRS_SIMPLE_UNUSED1 - -#define X_D3DRS_DEFERRED_FIRST X_D3DRS_FOGENABLE -#define X_D3DRS_DEFERRED_LAST X_D3DRS_DEFERRED_UNUSED1 - -#define X_D3DRS_COMPLEX_FIRST X_D3DRS_PSTEXTUREMODES -#define X_D3DRS_COMPLEX_LAST X_D3DRS_DONOTCULLUNCOMPRESSED - -#define X_D3DRS_FIRST X_D3DRS_PS_FIRST -#define X_D3DRS_LAST X_D3DRS_COMPLEX_LAST - -// X_D3DWRAP values : -constexpr DWORD X_D3DWRAP_U = 0x00000010; -constexpr DWORD X_D3DWRAP_V = 0x00001000; -constexpr DWORD X_D3DWRAP_W = 0x00100000; - -// X_D3DTEXTURESTAGESTATETYPE values : -// Dxbx note : See DxbxFromOldVersion_D3DTSS(), as these might need correction for older SDK versions! -// The set starts out with "deferred" texture states : -constexpr DWORD X_D3DTSS_ADDRESSU = 0; -constexpr DWORD X_D3DTSS_ADDRESSV = 1; -constexpr DWORD X_D3DTSS_ADDRESSW = 2; -constexpr DWORD X_D3DTSS_MAGFILTER = 3; -constexpr DWORD X_D3DTSS_MINFILTER = 4; -constexpr DWORD X_D3DTSS_MIPFILTER = 5; -constexpr DWORD X_D3DTSS_MIPMAPLODBIAS = 6; -constexpr DWORD X_D3DTSS_MAXMIPLEVEL = 7; -constexpr DWORD X_D3DTSS_MAXANISOTROPY = 8; -constexpr DWORD X_D3DTSS_COLORKEYOP = 9; // Xbox ext. -constexpr DWORD X_D3DTSS_COLORSIGN = 10; // Xbox ext. -constexpr DWORD X_D3DTSS_ALPHAKILL = 11; // Xbox ext. -constexpr DWORD X_D3DTSS_COLOROP = 12; -constexpr DWORD X_D3DTSS_COLORARG0 = 13; -constexpr DWORD X_D3DTSS_COLORARG1 = 14; -constexpr DWORD X_D3DTSS_COLORARG2 = 15; -constexpr DWORD X_D3DTSS_ALPHAOP = 16; -constexpr DWORD X_D3DTSS_ALPHAARG0 = 17; -constexpr DWORD X_D3DTSS_ALPHAARG1 = 18; -constexpr DWORD X_D3DTSS_ALPHAARG2 = 19; -constexpr DWORD X_D3DTSS_RESULTARG = 20; -constexpr DWORD X_D3DTSS_TEXTURETRANSFORMFLAGS = 21; -// End of "deferred" texture states, continuing with the rest : -constexpr DWORD X_D3DTSS_BUMPENVMAT00 = 22; -constexpr DWORD X_D3DTSS_BUMPENVMAT01 = 23; -constexpr DWORD X_D3DTSS_BUMPENVMAT11 = 24; -constexpr DWORD X_D3DTSS_BUMPENVMAT10 = 25; -constexpr DWORD X_D3DTSS_BUMPENVLSCALE = 26; -constexpr DWORD X_D3DTSS_BUMPENVLOFFSET = 27; -constexpr DWORD X_D3DTSS_TEXCOORDINDEX = 28; -constexpr DWORD X_D3DTSS_BORDERCOLOR = 29; -constexpr DWORD X_D3DTSS_COLORKEYCOLOR = 30; // Xbox ext. -constexpr DWORD X_D3DTSS_UNSUPPORTED = 31; // Note : Somehow, this one comes through D3DDevice_SetTextureStageStateNotInline sometimes -// End of texture states. - -// Texture state boundaries : - -constexpr DWORD X_D3DTSS_DEFERRED_FIRST = X_D3DTSS_ADDRESSU; -constexpr DWORD X_D3DTSS_DEFERRED_LAST = X_D3DTSS_TEXTURETRANSFORMFLAGS; - -constexpr DWORD X_D3DTSS_FIRST = X_D3DTSS_ADDRESSU; -constexpr DWORD X_D3DTSS_LAST = X_D3DTSS_COLORKEYCOLOR; - -constexpr DWORD X_D3DTS_STAGECOUNT = 4; // Dxbx addition -constexpr DWORD X_D3DTS_STAGESIZE = 32; // Dxbx addition - -constexpr DWORD X_PSH_COMBINECOUNT = 8; // Dxbx addition -constexpr DWORD X_PSH_CONSTANTCOUNT = 8; // Dxbx addition - -// X_D3DTEXTUREOP values : -constexpr DWORD X_D3DTOP_DISABLE = 1; -constexpr DWORD X_D3DTOP_SELECTARG1 = 2; -constexpr DWORD X_D3DTOP_SELECTARG2 = 3; -constexpr DWORD X_D3DTOP_MODULATE = 4; -constexpr DWORD X_D3DTOP_MODULATE2X = 5; -constexpr DWORD X_D3DTOP_MODULATE4X = 6; -constexpr DWORD X_D3DTOP_ADD = 7; -constexpr DWORD X_D3DTOP_ADDSIGNED = 8; -constexpr DWORD X_D3DTOP_ADDSIGNED2X = 9; -constexpr DWORD X_D3DTOP_SUBTRACT = 10; -constexpr DWORD X_D3DTOP_ADDSMOOTH = 11; -constexpr DWORD X_D3DTOP_BLENDDIFFUSEALPHA = 12; -constexpr DWORD X_D3DTOP_BLENDCURRENTALPHA = 13; -constexpr DWORD X_D3DTOP_BLENDTEXTUREALPHA = 14; -constexpr DWORD X_D3DTOP_BLENDFACTORALPHA = 15; -constexpr DWORD X_D3DTOP_BLENDTEXTUREALPHAPM = 16; -constexpr DWORD X_D3DTOP_PREMODULATE = 17; -constexpr DWORD X_D3DTOP_MODULATEALPHA_ADDCOLOR = 18; -constexpr DWORD X_D3DTOP_MODULATECOLOR_ADDALPHA = 19; -constexpr DWORD X_D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20; -constexpr DWORD X_D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21; -constexpr DWORD X_D3DTOP_DOTPRODUCT3 = 22; -constexpr DWORD X_D3DTOP_MULTIPLYADD = 23; -constexpr DWORD X_D3DTOP_LERP = 24; -constexpr DWORD X_D3DTOP_BUMPENVMAP = 25; -constexpr DWORD X_D3DTOP_BUMPENVMAPLUMINANCE = 26; - -constexpr DWORD X_D3DTOP_FIRST = X_D3DTOP_DISABLE; -constexpr DWORD X_D3DTOP_LAST = X_D3DTOP_BUMPENVMAPLUMINANCE; - -// X_D3DTEXTUREADDRESS values : -constexpr DWORD X_D3DTADDRESS_WRAP = 1; -constexpr DWORD X_D3DTADDRESS_MIRROR = 2; -constexpr DWORD X_D3DTADDRESS_CLAMP = 3; -constexpr DWORD X_D3DTADDRESS_BORDER = 4; -constexpr DWORD X_D3DTADDRESS_CLAMPTOEDGE = 5; - -// X_D3DTEXTUREFILTERTYPE Values -constexpr DWORD X_D3DTEXF_NONE = 0; -constexpr DWORD X_D3DTEXF_POINT = 1; -constexpr DWORD X_D3DTEXF_LINEAR = 2; -constexpr DWORD X_D3DTEXF_ANISOTROPIC = 3; -constexpr DWORD X_D3DTEXF_QUINCUNX = 4; // Xbox extension -constexpr DWORD X_D3DTEXF_GAUSSIANCUBIC = 5; - -// X_D3DCLEAR values : -constexpr DWORD X_D3DCLEAR_ZBUFFER = 0x00000001; -constexpr DWORD X_D3DCLEAR_STENCIL = 0x00000002; -constexpr DWORD X_D3DCLEAR_TARGET_R = 0x00000010; // Clear target surface R component (Xbox ext.) -constexpr DWORD X_D3DCLEAR_TARGET_G = 0x00000020; // Clear target surface G component (Xbox ext.) -constexpr DWORD X_D3DCLEAR_TARGET_B = 0x00000040; // Clear target surface B component (Xbox ext.) -constexpr DWORD X_D3DCLEAR_TARGET_A = 0x00000080; // Clear target surface A component (Xbox ext.) -constexpr DWORD X_D3DCLEAR_TARGET = X_D3DCLEAR_TARGET_R | X_D3DCLEAR_TARGET_G | X_D3DCLEAR_TARGET_B | X_D3DCLEAR_TARGET_A; - -// X_D3DCOLORWRITEENABLE values : -constexpr DWORD X_D3DCOLORWRITEENABLE_RED = (1 << 16); -constexpr DWORD X_D3DCOLORWRITEENABLE_GREEN = (1 << 8); -constexpr DWORD X_D3DCOLORWRITEENABLE_BLUE = (1 << 0); -constexpr DWORD X_D3DCOLORWRITEENABLE_ALPHA = (1 << 24); -constexpr DWORD X_D3DCOLORWRITEENABLE_ALL = 0x01010101; // Xbox ext. - -// deferred texture stage state "unknown" flag -#define X_D3DTSS_UNK 0x7fffffff - -typedef DWORD X_VERTEXSHADERCONSTANTMODE; - -// Xbox vertex shader constant modes -#define X_D3DSCM_96CONSTANTS 0x00 // Enables constants 0..95 -#define X_D3DSCM_192CONSTANTS 0x01 // Enables constants -96..-1 on top of 0..95 -#define X_D3DSCM_192CONSTANTSANDFIXEDPIPELINE 0x02 // Unsupported? -#define X_D3DSCM_NORESERVEDCONSTANTS 0x10 // Do not reserve constant -38 and -37 - -#define X_D3DSCM_RESERVED_CONSTANT_SCALE -38 // Becomes 58 after correction, contains Scale v -#define X_D3DSCM_RESERVED_CONSTANT_OFFSET -37 // Becomes 59 after correction, contains Offset - -#define X_D3DSCM_CORRECTION 96 // Add 96 to arrive at the range 0..191 (instead of -96..95) -#define X_D3DVS_CONSTREG_COUNT 192 - -// Special Registers, used to pass additional information to the shaders -// TODO co-locate shader workaround constants with shader code -#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE (X_D3DVS_CONSTREG_COUNT) -#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + 16) -#define CXBX_D3DVS_VIEWPORT_SCALE_MIRROR (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE + 4) -#define CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR (CXBX_D3DVS_VIEWPORT_SCALE_MIRROR + 1) - -#define X_D3DSCM_RESERVED_CONSTANT_SCALE_CORRECTED (X_D3DSCM_RESERVED_CONSTANT_SCALE + X_D3DSCM_CORRECTION) -#define X_D3DSCM_RESERVED_CONSTANT_OFFSET_CORRECTED (X_D3DSCM_RESERVED_CONSTANT_OFFSET + X_D3DSCM_CORRECTION) - -// Xbox vertex declaration token bit masks -#define X_D3DVSD_MASK_TESSUV 0x10000000 -#define X_D3DVSD_MASK_SKIP 0x10000000 // Skips (normally) dwords -#define X_D3DVSD_MASK_SKIPBYTES 0x08000000 // Skips bytes (no, really?!) -#define X_D3DVSD_STREAMTESSMASK (1 << 28) - -// Xbox vertex shader types -#define X_VST_NORMAL 1 -#define X_VST_READWRITE 2 -#define X_VST_STATE 3 - -// Xbox vertex shader counts -#define X_VSH_MAX_ATTRIBUTES 16 -#define X_VSH_MAX_STREAMS 16 -#define X_VSH_MAX_INSTRUCTION_COUNT 136 // The maximum Xbox shader instruction count - -// Xbox Vertex Shader versions -#define VERSION_XVS 0x2078 // 'x ' Xbox vertex shader -#define VERSION_XVSS 0x7378 // 'xs' Xbox vertex state shader -#define VERSION_XVSW 0x7778 // 'xw' Xbox vertex read/write shader - -/// nv2a microcode header -typedef struct -{ - uint16_t Version; // See VERSION_XVS* - uint16_t NumInst; -} -X_VSH_SHADER_HEADER; - -#define X_VSH_INSTRUCTION_SIZE 4 -#define X_VSH_INSTRUCTION_SIZE_BYTES (X_VSH_INSTRUCTION_SIZE * sizeof(DWORD)) - -// ****************************************************************** -// * X_VERTEXSHADERINPUT -// ****************************************************************** -typedef struct _X_VERTEXSHADERINPUT -{ - DWORD IndexOfStream; - DWORD Offset; - DWORD Format; - BYTE TesselationType; - BYTE TesselationSource; -} -X_VERTEXSHADERINPUT; - -// ****************************************************************** -// * X_VERTEXATTRIBUTEFORMAT -// ****************************************************************** -typedef struct _X_VERTEXATTRIBUTEFORMAT -{ - // Note : Alignment looks okay even without #pragma pack(1) / #include "AlignPrefix1.h" (and it's closure) - X_VERTEXSHADERINPUT Slots[X_VSH_MAX_ATTRIBUTES]; -} -X_VERTEXATTRIBUTEFORMAT; - -// ****************************************************************** -// * X_STREAMINPUT -// ****************************************************************** -typedef struct _X_STREAMINPUT -{ - X_D3DVertexBuffer *VertexBuffer; - UINT Stride; - UINT Offset; -} X_STREAMINPUT; - -struct X_D3DVertexShader -{ - // Note : Debug XBE's have a 'Vshd' DWORD signature prefixing this! - DWORD RefCount; // Based on the observation this member is set to 1 in D3DDevice_CreateVertexShader and decreased in D3DDevice_DeleteVertexShader - DWORD Flags; - DWORD FunctionSize; // ?Also known as ProgramSize? - DWORD TotalSize; // seems to include both the function and ?constants? - DWORD NumberOfDimensionsPerTexture; // Guesswork, since all 4 bytes (for all 4 textures) are most often set to 0 (or 2 when a texture isn't used) and 1, 3 and 4 also occur (and nothing else) - X_VERTEXATTRIBUTEFORMAT VertexAttribute; - DWORD FunctionData[X_VSH_MAX_INSTRUCTION_COUNT]; // probably the binary function data and ?constants? (data continues futher outside this struct, up to TotalSize DWORD's) -}; - -// vertex shader input registers for fixed function vertex shader - -// Name Register number D3DFVF -const int X_D3DVSDE_POSITION = 0; // Corresponds to D3DFVF_XYZ -const int X_D3DVSDE_BLENDWEIGHT = 1; // Corresponds to D3DFVF_XYZRHW -const int X_D3DVSDE_NORMAL = 2; // Corresponds to D3DFVF_NORMAL -const int X_D3DVSDE_DIFFUSE = 3; // Corresponds to D3DFVF_DIFFUSE -const int X_D3DVSDE_SPECULAR = 4; // Corresponds to D3DFVF_SPECULAR -const int X_D3DVSDE_FOG = 5; // Xbox extension -const int X_D3DVSDE_POINTSIZE = 6; // Dxbx addition -const int X_D3DVSDE_BACKDIFFUSE = 7; // Xbox extension -const int X_D3DVSDE_BACKSPECULAR = 8; // Xbox extension -const int X_D3DVSDE_TEXCOORD0 = 9; // Corresponds to D3DFVF_TEX1 (not D3DFVF_TEX0, which means no textures are present) -const int X_D3DVSDE_TEXCOORD1 = 10; // Corresponds to D3DFVF_TEX2 -const int X_D3DVSDE_TEXCOORD2 = 11; // Corresponds to D3DFVF_TEX3 -const int X_D3DVSDE_TEXCOORD3 = 12; // Corresponds to D3DFVF_TEX4 -const int X_D3DVSDE_VERTEX = 0xFFFFFFFF; // Xbox extension for Begin/End drawing (data is a D3DVSDT_FLOAT4) - -//typedef X_D3DVSDE = X_D3DVSDE_POSITION..High(DWORD)-2; // Unique declaration to make overloads possible; - -// bit declarations for _Type fields -const int X_D3DVSDT_FLOAT1 = 0x12; // 1D float expanded to (value, 0.0, 0.0, 1.0) -const int X_D3DVSDT_FLOAT2 = 0x22; // 2D float expanded to (value, value, 0.0, 1.0) -const int X_D3DVSDT_FLOAT3 = 0x32; // 3D float expanded to (value, value, value, 1.0) In double word format this is ARGB, or in byte ordering it would be B, G, R, A. -const int X_D3DVSDT_FLOAT4 = 0x42; // 4D float -const int X_D3DVSDT_D3DCOLOR = 0x40; // 4D packed unsigned bytes mapped to 0.0 to 1.0 range -//const int X_D3DVSDT_UBYTE4 = 0x05; // 4D unsigned byte Dxbx note : Not supported on Xbox ? -const int X_D3DVSDT_SHORT2 = 0x25; // 2D signed short expanded to (value, value, 0.0, 1.0) -const int X_D3DVSDT_SHORT4 = 0x45; // 4D signed short - -// Xbox only declarations : -const int X_D3DVSDT_NORMSHORT1 = 0x11; // xbox ext. 1D signed, normalized short expanded to (value, 0.0, 0.0, 1.0). Signed, normalized shorts map from -1.0 to 1.0. -const int X_D3DVSDT_NORMSHORT2 = 0x21; // xbox ext. 2D signed, normalized short expanded to (value, value, 0.0, 1.0). Signed, normalized shorts map from -1.0 to 1.0. -const int X_D3DVSDT_NORMSHORT3 = 0x31; // xbox ext. 3D signed, normalized short expanded to (value, value, value, 1.0). Signed, normalized shorts map from -1.0 to 1.0. -const int X_D3DVSDT_NORMSHORT4 = 0x41; // xbox ext. 4D signed, normalized short expanded to (value, value, value, value). Signed, normalized shorts map from -1.0 to 1.0. -const int X_D3DVSDT_NORMPACKED3 = 0x16; // xbox ext. Three signed, normalized components packed in 32-bits. (11,11,10). Each component ranges from -1.0 to 1.0. Expanded to (value, value, value, 1.0). -const int X_D3DVSDT_SHORT1 = 0x15; // xbox ext. 1D signed short expanded to (value, 0., 0., 1). Signed shorts map to the range [-32768, 32767]. -const int X_D3DVSDT_SHORT3 = 0x35; // xbox ext. 3D signed short expanded to (value, value, value, 1). Signed shorts map to the range [-32768, 32767]. -const int X_D3DVSDT_PBYTE1 = 0x14; // xbox ext. 1D packed byte expanded to (value, 0., 0., 1). Packed bytes map to the range [0, 1]. -const int X_D3DVSDT_PBYTE2 = 0x24; // xbox ext. 2D packed byte expanded to (value, value, 0., 1). Packed bytes map to the range [0, 1]. -const int X_D3DVSDT_PBYTE3 = 0x34; // xbox ext. 3D packed byte expanded to (value, value, value, 1). Packed bytes map to the range [0, 1]. -const int X_D3DVSDT_PBYTE4 = 0x44; // xbox ext. 4D packed byte expanded to (value, value, value, value). Packed bytes map to the range [0, 1]. -const int X_D3DVSDT_FLOAT2H = 0x72; // xbox ext. 3D float that expands to (value, value, 0.0, value). Useful for projective texture coordinates. -const int X_D3DVSDT_NONE = 0x02; // xbox ext. nsp - -typedef enum _X_D3DVSD_TOKENTYPE -{ - X_D3DVSD_TOKEN_NOP = 0, // NOP or extension - X_D3DVSD_TOKEN_STREAM, // stream selector - X_D3DVSD_TOKEN_STREAMDATA, // stream data definition (map to vertex input memory) - X_D3DVSD_TOKEN_TESSELLATOR, // vertex input memory from tessellator - X_D3DVSD_TOKEN_CONSTMEM, // constant memory from shader - X_D3DVSD_TOKEN_EXT, // extension - X_D3DVSD_TOKEN_END = 7, // end-of-array (requires all DWORD bits to be 1) - X_D3DVSD_FORCE_DWORD = 0x7fffffff,// force 32-bit size enum -} X_D3DVSD_TOKENTYPE; - -#define X_D3DVSD_TOKENTYPESHIFT 29 -#define X_D3DVSD_TOKENTYPEMASK (7 << X_D3DVSD_TOKENTYPESHIFT) - -#define X_D3DVSD_STREAMNUMBERSHIFT 0 -#define X_D3DVSD_STREAMNUMBERMASK (0xF << X_D3DVSD_STREAMNUMBERSHIFT) - -#define X_D3DVSD_DATALOADTYPESHIFT 28 -#define X_D3DVSD_DATALOADTYPEMASK (0x1 << X_D3DVSD_DATALOADTYPESHIFT) - -#define X_D3DVSD_DATATYPESHIFT 16 -#define X_D3DVSD_DATATYPEMASK (0xFF << X_D3DVSD_DATATYPESHIFT) - -#define X_D3DVSD_SKIPCOUNTSHIFT 16 -#define X_D3DVSD_SKIPCOUNTMASK (0xF << X_D3DVSD_SKIPCOUNTSHIFT) - -#define X_D3DVSD_VERTEXREGSHIFT 0 -#define X_D3DVSD_VERTEXREGMASK (0x1F << X_D3DVSD_VERTEXREGSHIFT) - -#define X_D3DVSD_VERTEXREGINSHIFT 20 -#define X_D3DVSD_VERTEXREGINMASK (0xF << X_D3DVSD_VERTEXREGINSHIFT) - -#define X_D3DVSD_CONSTCOUNTSHIFT 25 -#define X_D3DVSD_CONSTCOUNTMASK (0xF << X_D3DVSD_CONSTCOUNTSHIFT) - -#define X_D3DVSD_CONSTADDRESSSHIFT 0 -#define X_D3DVSD_CONSTADDRESSMASK (0xFF << X_D3DVSD_CONSTADDRESSSHIFT) - -#define X_D3DVSD_CONSTRSSHIFT 16 -#define X_D3DVSD_CONSTRSMASK (0x1FFF << X_D3DVSD_CONSTRSSHIFT) - -#define X_D3DVSD_EXTCOUNTSHIFT 24 -#define X_D3DVSD_EXTCOUNTMASK (0x1F << X_D3DVSD_EXTCOUNTSHIFT) - -#define X_D3DVSD_EXTINFOSHIFT 0 -#define X_D3DVSD_EXTINFOMASK (0xFFFFFF << X_D3DVSD_EXTINFOSHIFT) - -#define X_D3DVSD_MAKETOKENTYPE(Type) ((Type << X_D3DVSD_TOKENTYPESHIFT) & X_D3DVSD_TOKENTYPEMASK) - -#define X_D3DVSD_STREAM(Stream) (X_D3DVSD_MAKETOKENTYPE(X_D3DVSD_TOKEN_STREAM) | (Stream)) -#define X_D3DVSD_REG(Reg, Type) (X_D3DVSD_MAKETOKENTYPE(X_D3DVSD_TOKEN_STREAMDATA) | ((Type) << X_D3DVSD_DATATYPESHIFT) | (Reg)) - -#define X_D3DVSD_NOP() 0x00000000 -#define X_D3DVSD_END() 0xFFFFFFFF - -// FVF Definitions -#define X_D3DFVF_RESERVED0 0x001 -#define X_D3DFVF_XYZ 0x002 -#define X_D3DFVF_XYZRHW 0x004 -#define X_D3DFVF_XYZB1 0x006 -#define X_D3DFVF_XYZB2 0x008 -#define X_D3DFVF_XYZB3 0x00a -#define X_D3DFVF_XYZB4 0x00c -#define X_D3DFVF_POSITION_MASK 0x00E -#define X_D3DFVF_NORMAL 0x010 -#define X_D3DFVF_RESERVED1 0x020 -#define X_D3DFVF_DIFFUSE 0x040 -#define X_D3DFVF_SPECULAR 0x080 -#define X_D3DFVF_TEXCOUNT_MASK 0xf00 -#define X_D3DFVF_TEXCOUNT_SHIFT 8 -#define X_D3DFVF_TEX0 0x000 -#define X_D3DFVF_TEX1 0x100 -#define X_D3DFVF_TEX2 0x200 -#define X_D3DFVF_TEX3 0x300 -#define X_D3DFVF_TEX4 0x400 -#define X_D3DFVF_TEX5 0x500 -#define X_D3DFVF_TEX6 0x600 -#define X_D3DFVF_TEX7 0x700 -#define X_D3DFVF_TEX8 0x800 -#define X_D3DFVF_TEXTUREFORMAT1 0x003 -#define X_D3DFVF_TEXTUREFORMAT2 0x000 -#define X_D3DFVF_TEXTUREFORMAT3 0x001 -#define X_D3DFVF_TEXTUREFORMAT4 0x002 - -#define X_D3DFVF_TEXCOORDSIZE1(Index) (X_D3DFVF_TEXTUREFORMAT1 << (Index * 2 + 16)) -#define X_D3DFVF_TEXCOORDSIZE2(Index) (X_D3DFVF_TEXTUREFORMAT2) -#define X_D3DFVF_TEXCOORDSIZE3(Index) (X_D3DFVF_TEXTUREFORMAT3 << (Index * 2 + 16)) -#define X_D3DFVF_TEXCOORDSIZE4(Index) (X_D3DFVF_TEXTUREFORMAT4 << (Index * 2 + 16)) - -typedef DWORD NV2AMETHOD; - -// -// Below declarations are used by Cxbx, not by the Xbox!!! -// - -} // end of namespace XTL - -#endif +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef XBD3D8TYPES_H +#define XBD3D8TYPES_H + +#undef UNICODE // make sure dxerr.h DXGetErrorString is aliassed to *A, not *W + +// include direct3d 9x headers +#define DIRECT3D_VERSION 0x0900 +#include +//implies #include // for D3DFORMAT, D3DLIGHT9, etc +//implies #include +#include // for D3DXVECTOR4, etc +#include + +#include +//#pragma comment(lib, "dxerr.lib") // See https://blogs.msdn.microsoft.com/chuckw/2012/04/24/wheres-dxerr-lib/ + +// If the above doesn't compile, install the June 2010 DirectX SDK +// from https://www.microsoft.com/en-us/download/details.aspx?id=6812 +// and select the Direct3D 9 include & library path (TODO : how?) + +// We're going to use the approach detailed in : +// https://blogs.msdn.microsoft.com/chuckw/2015/03/23/the-zombie-directx-sdk/ + + +// For transforming code that's written for Direct3D 8 into Direct3D 9, +// See "Converting to Direct3D 9" https://msdn.microsoft.com/en-us/library/windows/desktop/bb204851(v=vs.85).aspx + +// See https://msdn.microsoft.com/en-us/library/windows/desktop/bb204851(v=vs.85).aspx#D3DENUM_NO_WHQL_LEVEL_Changes +#define D3DENUM_NO_WHQL_LEVEL 0 // default in Direct3D 9 + +// Alias all host Direct3D 9 symbols to generic symbols +#define DXGetErrorString DXGetErrorString9A +#define DXGetErrorDescription DXGetErrorDescription9A +#define Direct3DCreate Direct3DCreate9 +#define D3DXAssembleShader D3DXAssembleShader +#define FullScreen_PresentationInterval PresentationInterval // a field in D3DPRESENT_PARAMETERS +#define D3DLockData void +#define PixelShaderConstantType float + +#define D3DADAPTER_IDENTIFIER D3DADAPTER_IDENTIFIER9 +#define D3DCAPS D3DCAPS9 +#define D3DVERTEXELEMENT D3DVERTEXELEMENT9 +#define D3DVIEWPORT D3DVIEWPORT9 + +#define IDirect3D IDirect3D9 +#define IDirect3DDevice IDirect3DDevice9 +#define IDirect3DStateBlock IDirect3DStateBlock9 // unused +#define IDirect3DVertexDeclaration IDirect3DVertexDeclaration9 +#define IDirect3DVertexShader IDirect3DVertexShader9 +#define IDirect3DPixelShader IDirect3DPixelShader9 +#define IDirect3DResource IDirect3DResource9 +#define IDirect3DBaseTexture IDirect3DBaseTexture9 +#define IDirect3DTexture IDirect3DTexture9 +#define IDirect3DVolumeTexture IDirect3DVolumeTexture9 +#define IDirect3DCubeTexture IDirect3DCubeTexture9 +#define IDirect3DVertexBuffer IDirect3DVertexBuffer9 +#define IDirect3DIndexBuffer IDirect3DIndexBuffer9 +#define IDirect3DSurface IDirect3DSurface9 +#define IDirect3DVolume IDirect3DVolume9 +#define IDirect3DSwapChain IDirect3DSwapChain9 +#define IDirect3DQuery IDirect3DQuery9 + +namespace XTL { + +// TODO : Declare these aliasses as Xbox type +typedef D3DLIGHT9 X_D3DLIGHT8; +typedef D3DMATERIAL9 X_D3DMATERIAL8; +typedef D3DVIEWPORT9 X_D3DVIEWPORT8; + +// TODO: fill out these enumeration tables for convienance +typedef D3DSWAPEFFECT X_D3DSWAPEFFECT; +typedef D3DXVECTOR4 X_D3DXVECTOR4; +typedef DWORD X_D3DBLENDOP; +typedef DWORD X_D3DBLEND; +typedef DWORD X_D3DCMPFUNC; +typedef DWORD X_D3DFILLMODE; +typedef DWORD X_D3DMULTISAMPLE_TYPE; +typedef DWORD X_D3DSHADEMODE; +typedef DWORD X_D3DSTENCILOP; +typedef DWORD X_D3DTEXTURESTAGESTATETYPE; + +typedef enum _X_D3DCULL +{ + X_D3DCULL_NONE = 0, + X_D3DCULL_CW = 0x900, + X_D3DCULL_CCW = 0x901, + X_D3DCULL_FORCE_DWORD = 0x7fffffff +} +X_D3DCULL; + +typedef enum _X_D3DFORMAT +{ +/* + Xbox1 D3DFORMAT notes + --------------------- + + The Xbox1 D3DFORMAT type consists of 4 different format categories : + 1. Swizzled (improves data locality, incompatible with native Direct3D) + 2. Compressed (DXT compression, giving 4:1 reduction on 4x4 pixel blocks) + 3. Linear (compatible with native Direct3D) + 4. Depth (Fixed or Floating point, stored Linear or Swizzled) + + Requirements\Format Swizzled Compressed Linear Depth Notes + + Power-of-two required ? YES YES NO NO + Mipmap supported ? YES YES NO YES Linear has MipmapLevels = 1 + CubeMaps supported ? YES YES NO NO Cubemaps have 6 faces + Supports volumes ? YES YES NO NO Volumes have 3 dimensions, Textures have 2 + Can be a rendertarget ? YES YES YES LINEAR Depth buffers can only be rendered to if stored Linear + + Implications : + - CubeMaps must be square + - Volumes cannot be cube mapped and vice versa + + Maximum dimensions : + 2D : 4096 x 4096 (12 mipmap levels) + 3D : 512 x 512 x 512 (9 mipmap levels) + +*/ + + // Xbox D3DFORMAT types : + // See https://wiki.beyondunreal.com/Legacy:Texture_Format + + // Swizzled Formats + + X_D3DFMT_L8 = 0x00, + X_D3DFMT_AL8 = 0x01, + X_D3DFMT_A1R5G5B5 = 0x02, + X_D3DFMT_X1R5G5B5 = 0x03, + X_D3DFMT_A4R4G4B4 = 0x04, + X_D3DFMT_R5G6B5 = 0x05, + X_D3DFMT_A8R8G8B8 = 0x06, + X_D3DFMT_X8R8G8B8 = 0x07, + X_D3DFMT_X8L8V8U8 = 0x07, // Alias + + X_D3DFMT_P8 = 0x0b, // 8-bit Palletized + + X_D3DFMT_A8 = 0x19, + X_D3DFMT_A8L8 = 0x1a, + X_D3DFMT_R6G5B5 = 0x27, + X_D3DFMT_L6V5U5 = 0x27, // Alias + + X_D3DFMT_G8B8 = 0x28, + X_D3DFMT_V8U8 = 0x28, // Alias + + X_D3DFMT_R8B8 = 0x29, + X_D3DFMT_D24S8 = 0x2a, + X_D3DFMT_F24S8 = 0x2b, + X_D3DFMT_D16 = 0x2c, + X_D3DFMT_D16_LOCKABLE = 0x2c, // Alias + + X_D3DFMT_F16 = 0x2d, + X_D3DFMT_L16 = 0x32, + X_D3DFMT_V16U16 = 0x33, + X_D3DFMT_R5G5B5A1 = 0x38, + X_D3DFMT_R4G4B4A4 = 0x39, + X_D3DFMT_A8B8G8R8 = 0x3A, + X_D3DFMT_Q8W8V8U8 = 0x3A, // Alias + + X_D3DFMT_B8G8R8A8 = 0x3B, + X_D3DFMT_R8G8B8A8 = 0x3C, + + // YUV Formats + + X_D3DFMT_YUY2 = 0x24, + X_D3DFMT_UYVY = 0x25, + + // Compressed Formats + + X_D3DFMT_DXT1 = 0x0C, // opaque/one-bit alpha + X_D3DFMT_DXT2 = 0x0E, // Alias for X_D3DFMT_DXT3 + X_D3DFMT_DXT3 = 0x0E, // linear alpha + X_D3DFMT_DXT4 = 0x0F, // Alias for X_D3DFMT_DXT5 + X_D3DFMT_DXT5 = 0x0F, // interpolated alpha + + // Linear Formats + + X_D3DFMT_LIN_A1R5G5B5 = 0x10, + X_D3DFMT_LIN_R5G6B5 = 0x11, + X_D3DFMT_LIN_A8R8G8B8 = 0x12, + X_D3DFMT_LIN_L8 = 0x13, + X_D3DFMT_LIN_R8B8 = 0x16, + X_D3DFMT_LIN_G8B8 = 0x17, + X_D3DFMT_LIN_V8U8 = 0x17, // Alias + + X_D3DFMT_LIN_AL8 = 0x1b, + X_D3DFMT_LIN_X1R5G5B5 = 0x1c, + X_D3DFMT_LIN_A4R4G4B4 = 0x1d, + X_D3DFMT_LIN_X8R8G8B8 = 0x1e, + X_D3DFMT_LIN_X8L8V8U8 = 0x1e, // Alias + + X_D3DFMT_LIN_A8 = 0x1f, + X_D3DFMT_LIN_A8L8 = 0x20, + X_D3DFMT_LIN_D24S8 = 0x2E, + X_D3DFMT_LIN_F24S8 = 0x2f, + X_D3DFMT_LIN_D16 = 0x30, + X_D3DFMT_LIN_F16 = 0x31, + X_D3DFMT_LIN_L16 = 0x35, + X_D3DFMT_LIN_V16U16 = 0x36, + X_D3DFMT_LIN_R6G5B5 = 0x37, + X_D3DFMT_LIN_L6V5U5 = 0x37, // Alias + + X_D3DFMT_LIN_R5G5B5A1 = 0x3D, + X_D3DFMT_LIN_R4G4B4A4 = 0x3e, + X_D3DFMT_LIN_A8B8G8R8 = 0x3f, + X_D3DFMT_LIN_B8G8R8A8 = 0x40, + X_D3DFMT_LIN_R8G8B8A8 = 0x41, + + X_D3DFMT_VERTEXDATA = 0x64, + + X_D3DFMT_INDEX16 = 101/*=D3DFMT_INDEX16*/, // Dxbx addition : Not an Xbox format, used internally + + X_D3DFMT_UNKNOWN = 0xFFFFFFFF - 3, // Unique declaration to make overloads possible +} +X_D3DFORMAT, *PX_D3DFORMAT; + +// Primitives supported by draw-primitive API +typedef enum _X_D3DPRIMITIVETYPE +{ + X_D3DPT_NONE = 0, // Dxbx addition + + X_D3DPT_POINTLIST = 1, + X_D3DPT_LINELIST = 2, + X_D3DPT_LINELOOP = 3, // Xbox only + X_D3DPT_LINESTRIP = 4, + X_D3DPT_TRIANGLELIST = 5, + X_D3DPT_TRIANGLESTRIP = 6, + X_D3DPT_TRIANGLEFAN = 7, + X_D3DPT_QUADLIST = 8, // Xbox only + X_D3DPT_QUADSTRIP = 9, // Xbox only + X_D3DPT_POLYGON = 10, // Xbox only + + X_D3DPT_MAX = 11, + X_D3DPT_INVALID = 0x7fffffff, /* force 32-bit size enum */ +} +X_D3DPRIMITIVETYPE; + +typedef enum _X_D3DRESOURCETYPE +{ + X_D3DRTYPE_NONE = 0, + X_D3DRTYPE_SURFACE = 1, // = D3DRESOURCETYPE.D3DRTYPE_SURFACE + X_D3DRTYPE_VOLUME = 2, // = D3DRESOURCETYPE.D3DRTYPE_VOLUME + X_D3DRTYPE_TEXTURE = 3, // = D3DRESOURCETYPE.D3DRTYPE_TEXTURE + X_D3DRTYPE_VOLUMETEXTURE = 4, // = D3DRESOURCETYPE.D3DRTYPE_VOLUMETEXTURE + X_D3DRTYPE_CUBETEXTURE = 5, // = D3DRESOURCETYPE.D3DRTYPE_CUBETEXTURE + X_D3DRTYPE_VERTEXBUFFER = 6, // = D3DRESOURCETYPE.D3DRTYPE_VERTEXBUFFER + X_D3DRTYPE_INDEXBUFFER = 7, // = D3DRESOURCETYPE.D3DRTYPE_INDEXBUFFER + X_D3DRTYPE_PUSHBUFFER = 8, + X_D3DRTYPE_PALETTE = 9, + X_D3DRTYPE_FIXUP = 10, + + X_D3DRTYPE_FORCE_DWORD = 0x7fffffff +} +X_D3DRESOURCETYPE; + +// D3DUSAGE values (all but the Xbox extensions match the PC versions) : +#define X_D3DUSAGE_RENDERTARGET 0x00000001 +#define X_D3DUSAGE_DEPTHSTENCIL 0x00000002 +// for Vertex/Index buffers +#define X_D3DUSAGE_WRITEONLY 0x00000008 +#define X_D3DUSAGE_POINTS 0x00000040 +#define X_D3DUSAGE_RTPATCHES 0x00000080 +#define X_D3DUSAGE_DYNAMIC 0x00000200 +// for CreateVertexShader +#define X_D3DUSAGE_PERSISTENTDIFFUSE 0x00000400L // Xbox-only +#define X_D3DUSAGE_PERSISTENTSPECULAR 0x00000800L // Xbox-only +#define X_D3DUSAGE_PERSISTENTBACKDIFFUSE 0x00001000L // Xbox-only +#define X_D3DUSAGE_PERSISTENTBACKSPECULAR 0x00002000L // Xbox-only +// for CreateTexture/CreateImageSurface +#define X_D3DUSAGE_BORDERSOURCE_COLOR 0x00000000L // Xbox-only +#define X_D3DUSAGE_BORDERSOURCE_TEXTURE 0x00010000L // Xbox-only + +#define X_D3D_RENDER_MEMORY_ALIGNMENT 64 + +#define X_D3DSURFACE_ALIGNMENT X_D3D_RENDER_MEMORY_ALIGNMENT +#define X_D3DTEXTURE_ALIGNMENT (2 * X_D3D_RENDER_MEMORY_ALIGNMENT) +#define X_D3DTEXTURE_CUBEFACE_ALIGNMENT (2 * X_D3D_RENDER_MEMORY_ALIGNMENT) +#define X_D3DTEXTURE_PITCH_ALIGNMENT X_D3D_RENDER_MEMORY_ALIGNMENT +#define X_D3DTEXTURE_PITCH_MIN X_D3DTEXTURE_PITCH_ALIGNMENT + +typedef enum _X_D3DSET_DEPTH_CLIP_PLANES_FLAGS +{ + X_D3DSDCP_SET_VERTEXPROGRAM_PLANES = 1, + X_D3DSDCP_SET_FIXEDFUNCTION_PLANES = 2, + X_D3DSDCP_USE_DEFAULT_VERTEXPROGRAM_PLANES = 3, + X_D3DSDCP_USE_DEFAULT_FIXEDFUNCTION_PLANES = 4, +} +X_D3DSET_DEPTH_CLIP_PLANES_FLAGS; + +#define X_D3DPRESENTFLAG_LOCKABLE_BACKBUFFER 0x00000001 +#define X_D3DPRESENTFLAG_WIDESCREEN 0x00000010 +#define X_D3DPRESENTFLAG_INTERLACED 0x00000020 +#define X_D3DPRESENTFLAG_PROGRESSIVE 0x00000040 +#define X_D3DPRESENTFLAG_FIELD 0x00000080 +#define X_D3DPRESENTFLAG_10X11PIXELASPECTRATIO 0x00000100 + +typedef struct _X_D3DDISPLAYMODE +{ + UINT Width; + UINT Height; + UINT RefreshRate; + DWORD Flags; + X_D3DFORMAT Format; +} +X_D3DDISPLAYMODE; + +typedef struct _X_D3DVERTEXBUFFER_DESC +{ + X_D3DFORMAT Format; + X_D3DRESOURCETYPE Type; +} +X_D3DVERTEXBUFFER_DESC; + +typedef struct _X_D3DINDEXBUFFER_DESC +{ + X_D3DFORMAT Format; + X_D3DRESOURCETYPE Type; +} +X_D3DINDEXBUFFER_DESC; + +typedef struct _X_D3DSURFACE_DESC +{ + X_D3DFORMAT Format; + X_D3DRESOURCETYPE Type; + DWORD Usage; + UINT Size; + X_D3DMULTISAMPLE_TYPE MultiSampleType; + UINT Width; + UINT Height; +} +X_D3DSURFACE_DESC; + +struct X_D3DSurface; // forward +typedef struct _X_D3DPRESENT_PARAMETERS +{ + UINT BackBufferWidth; + UINT BackBufferHeight; + X_D3DFORMAT BackBufferFormat; + UINT BackBufferCount; + + X_D3DMULTISAMPLE_TYPE MultiSampleType; + + X_D3DSWAPEFFECT SwapEffect; + HWND hDeviceWindow; + BOOL Windowed; + BOOL EnableAutoDepthStencil; + X_D3DFORMAT AutoDepthStencilFormat; + DWORD Flags; + + UINT FullScreen_RefreshRateInHz; + UINT FullScreen_PresentationInterval; + // The Windows DirectX8 variant ends here + // This check guarantees identical layout, compared to Direct3D8._D3DPRESENT_PARAMETERS_: + // assert(offsetof(X_D3DPRESENT_PARAMETERS, BufferSurfaces) == sizeof(_D3DPRESENT_PARAMETERS_)); + X_D3DSurface *BufferSurfaces[3]; + X_D3DSurface *DepthStencilSurface; +} +X_D3DPRESENT_PARAMETERS; + +typedef struct _X_D3DGAMMARAMP +{ + BYTE red[256]; + BYTE green[256]; + BYTE blue[256]; +} +X_D3DGAMMARAMP; + +typedef struct _X_D3DPIXELSHADERDEF // <- blueshogun 10/1/07 +{ + DWORD PSAlphaInputs[8]; // X_D3DRS_PSALPHAINPUTS0..X_D3DRS_PSALPHAINPUTS7 : Alpha inputs for each stage + DWORD PSFinalCombinerInputsABCD; // X_D3DRS_PSFINALCOMBINERINPUTSABCD : Final combiner inputs + DWORD PSFinalCombinerInputsEFG; // X_D3DRS_PSFINALCOMBINERINPUTSEFG : Final combiner inputs (continued) + DWORD PSConstant0[8]; // X_D3DRS_PSCONSTANT0_0..X_D3DRS_PSCONSTANT0_7 : C0 for each stage + DWORD PSConstant1[8]; // X_D3DRS_PSCONSTANT1_0..X_D3DRS_PSCONSTANT1_7 : C1 for each stage + DWORD PSAlphaOutputs[8]; // X_D3DRS_PSALPHAOUTPUTS0..X_D3DRS_PSALPHAOUTPUTS7 : Alpha output for each stage + DWORD PSRGBInputs[8]; // X_D3DRS_PSRGBINPUTS0..X_D3DRS_PSRGBINPUTS7 : RGB inputs for each stage + DWORD PSCompareMode; // X_D3DRS_PSCOMPAREMODE : Compare modes for clipplane texture mode + DWORD PSFinalCombinerConstant0; // X_D3DRS_PSFINALCOMBINERCONSTANT0 : C0 in final combiner + DWORD PSFinalCombinerConstant1; // X_D3DRS_PSFINALCOMBINERCONSTANT1 : C1 in final combiner + DWORD PSRGBOutputs[8]; // X_D3DRS_PSRGBOUTPUTS0..X_D3DRS_PSRGBOUTPUTS7 : Stage 0 RGB outputs + DWORD PSCombinerCount; // X_D3DRS_PSCOMBINERCOUNT : Active combiner count (Stages 0-7) + DWORD PSTextureModes; // X_D3DRS_PSTEXTUREMODES: Texture addressing modes + DWORD PSDotMapping; // X_D3DRS_PSDOTMAPPING : Input mapping for dot product modes + DWORD PSInputTexture; // X_D3DRS_PSINPUTTEXTURE : Texture source for some texture modes + + // These last three DWORDs are used to define how Direct3D8 pixel shader constants map to the constant + // registers in each combiner stage. They are used by the Direct3D run-time software but not by the hardware. + DWORD PSC0Mapping; // Mapping of c0 regs to D3D constants + DWORD PSC1Mapping; // Mapping of c1 regs to D3D constants + DWORD PSFinalCombinerConstants; // Final combiner constant mapping +} +X_D3DPIXELSHADERDEF; + + +typedef struct _X_PixelShader +{ + DWORD RefCount; + DWORD D3DOwned; + X_D3DPIXELSHADERDEF *pPSDef; +} X_PixelShader; + +struct X_D3DResource +{ + DWORD Common; + DWORD Data; + DWORD Lock; +}; + +// D3D resource "common" masks +#define X_D3DCOMMON_REFCOUNT_MASK 0x0000FFFF +#define X_D3DCOMMON_TYPE_MASK 0x00070000 +#define X_D3DCOMMON_TYPE_SHIFT 16 +#define X_D3DCOMMON_TYPE_VERTEXBUFFER 0x00000000 +#define X_D3DCOMMON_TYPE_INDEXBUFFER 0x00010000 +#define X_D3DCOMMON_TYPE_PUSHBUFFER 0x00020000 +#define X_D3DCOMMON_TYPE_PALETTE 0x00030000 +#define X_D3DCOMMON_TYPE_TEXTURE 0x00040000 +#define X_D3DCOMMON_TYPE_SURFACE 0x00050000 // Also covers Volume resources +#define X_D3DCOMMON_TYPE_FIXUP 0x00060000 +#define X_D3DCOMMON_INTREFCOUNT_MASK 0x00780000 +#define X_D3DCOMMON_INTREFCOUNT_SHIFT 19 +#define X_D3DCOMMON_INTREFCOUNT_1 (1 << X_D3DCOMMON_INTREFCOUNT_SHIFT) // Dxbx addition +#define X_D3DCOMMON_VIDEOMEMORY 0x00000000 // Was 0x00800000, but Xbox doesn't have this flag! +#define X_D3DCOMMON_D3DCREATED 0x01000000 +#define X_D3DCOMMON_ISLOCKED 0x02000010 // Surface is currently locked (potential unswizzle candidate) +#define X_D3DCOMMON_UNUSED_MASK 0xFE000000 +#define X_D3DCOMMON_UNUSED_SHIFT 25 + +// d3d palette common +#define X_D3DPALETTE_COMMON_PALETTESIZE_MASK 0xC0000000 +#define X_D3DPALETTE_COMMON_PALETTESIZE_SHIFT 30 + +// special resource lock flags +#define X_D3DRESOURCE_LOCK_FLAG_NOSIZE 0xEFFFFFFF + +// Lock flags +#define X_D3DLOCK_NOFLUSH 0x00000010 // Xbox extension +#define X_D3DLOCK_NOOVERWRITE 0x00000020 +#define X_D3DLOCK_TILED 0x00000040 // Xbox extension +#define X_D3DLOCK_READONLY 0x00000080 + +// Multisample modes +const int X_D3DMULTISAMPLE_NONE = 0x0011; +const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR = 0x1021; +const int X_D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX = 0x1121; +const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_HORIZONTAL_LINEAR = 0x2021; +const int X_D3DMULTISAMPLE_2_SAMPLES_SUPERSAMPLE_VERTICAL_LINEAR = 0x2012; +const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_LINEAR = 0x1022; +const int X_D3DMULTISAMPLE_4_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1222; +const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_LINEAR = 0x2022; +const int X_D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2222; +const int X_D3DMULTISAMPLE_9_SAMPLES_MULTISAMPLE_GAUSSIAN = 0x1233; +const int X_D3DMULTISAMPLE_9_SAMPLES_SUPERSAMPLE_GAUSSIAN = 0x2233; + +// Multisample masks (Cxbx additions) +const int X_D3DMULTISAMPLE_YSCALE_MASK = 0x0003; +const int X_D3DMULTISAMPLE_YSCALE_SHIFT = 0; + +const int X_D3DMULTISAMPLE_XSCALE_MASK = 0x0030; +const int X_D3DMULTISAMPLE_XSCALE_SHIFT = 4; + +const int X_D3DMULTISAMPLE_ALGO_LINEAR = 0x0000; +const int X_D3DMULTISAMPLE_ALGO_QUINCUNX = 0x0100; +const int X_D3DMULTISAMPLE_ALGO_GAUSSIAN = 0x0200; +const int X_D3DMULTISAMPLE_ALGO_MASK = 0x0300; + +const int X_D3DMULTISAMPLE_SAMPLING_NONE = 0x0000; +const int X_D3DMULTISAMPLE_SAMPLING_MULTI = 0x1000; +const int X_D3DMULTISAMPLE_SAMPLING_SUPER = 0x2000; +const int X_D3DMULTISAMPLE_SAMPLING_MASK = 0x3000; + +const int X_D3DMULTISAMPLE_KNOWN_MASK = 0 + | X_D3DMULTISAMPLE_YSCALE_MASK + | X_D3DMULTISAMPLE_XSCALE_MASK + | X_D3DMULTISAMPLE_ALGO_MASK + | X_D3DMULTISAMPLE_SAMPLING_MASK + ; + + +struct X_D3DVertexBuffer : public X_D3DResource +{ + +}; +struct X_D3DIndexBuffer : public X_D3DResource +{ + +}; + +struct X_D3DPushBuffer : public X_D3DResource +{ + ULONG Size; + ULONG AllocationSize; +}; + +struct X_D3DFixup : public X_D3DResource +{ + ULONG Run; + ULONG Next; + ULONG Size; +}; + +struct X_D3DPalette : public X_D3DResource +{ +}; + +typedef enum _X_D3DPALETTESIZE +{ + D3DPALETTE_256 = 0, + D3DPALETTE_128 = 1, + D3DPALETTE_64 = 2, + D3DPALETTE_32 = 3, + D3DPALETTE_MAX = 4, + D3DPALETTE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} +X_D3DPALETTESIZE; + +struct X_D3DPixelContainer : public X_D3DResource +{ + DWORD Format; // Format information about the texture. + DWORD Size; // Size of a non power-of-2 texture, must be zero otherwise +}; + +// pixel container "format" masks +#define X_D3DFORMAT_RESERVED1_MASK 0x00000003 // Must be zero +#define X_D3DFORMAT_DMACHANNEL_MASK 0x00000003 +#define X_D3DFORMAT_DMACHANNEL_A 0x00000001 // DMA channel A - the default for all system memory +#define X_D3DFORMAT_DMACHANNEL_B 0x00000002 // DMA channel B - unused +#define X_D3DFORMAT_CUBEMAP 0x00000004 // Set if the texture if a cube map +#define X_D3DFORMAT_BORDERSOURCE_COLOR 0x00000008 // If set, uses D3DTSS_BORDERCOLOR as a border +#define X_D3DFORMAT_DIMENSION_MASK 0x000000F0 // # of dimensions, must be 2 or 3 +#define X_D3DFORMAT_DIMENSION_SHIFT 4 +#define X_D3DFORMAT_FORMAT_MASK 0x0000FF00 // D3DFORMAT - See X_D3DFMT_* above +#define X_D3DFORMAT_FORMAT_SHIFT 8 +#define X_D3DFORMAT_MIPMAP_MASK 0x000F0000 // # mipmap levels (always 1 for surfaces) +#define X_D3DFORMAT_MIPMAP_SHIFT 16 +#define X_D3DFORMAT_USIZE_MASK 0x00F00000 // Log 2 of the U size of the base texture (only set for swizzled or compressed) +#define X_D3DFORMAT_USIZE_SHIFT 20 +#define X_D3DFORMAT_VSIZE_MASK 0x0F000000 // Log 2 of the V size of the base texture (only set for swizzled or compressed) +#define X_D3DFORMAT_VSIZE_SHIFT 24 +#define X_D3DFORMAT_PSIZE_MASK 0xF0000000 // Log 2 of the P size of the base texture (only set for swizzled or compressed) +#define X_D3DFORMAT_PSIZE_SHIFT 28 + +// pixel container "size" masks +// The layout of the size field, used for non swizzled or compressed textures. +// +// The Size field of a container will be zero if the texture is swizzled or compressed. +// It is guarenteed to be non-zero otherwise because either the height/width will be +// greater than one or the pitch adjust will be nonzero because the minimum texture +// pitch is 8 bytes. +#define X_D3DSIZE_WIDTH_MASK 0x00000FFF // Width of the texture - 1, in texels +//#define X_D3DSIZE_WIDTH_SHIFT 0 +#define X_D3DSIZE_HEIGHT_MASK 0x00FFF000 // Height of the texture - 1, in texels +#define X_D3DSIZE_HEIGHT_SHIFT 12 +#define X_D3DSIZE_PITCH_MASK 0xFF000000 // Pitch / 64 - 1 +#define X_D3DSIZE_PITCH_SHIFT 24 + + +struct X_D3DBaseTexture : public X_D3DPixelContainer +{ + +}; + +struct X_D3DTexture : public X_D3DBaseTexture +{ + +}; + +struct X_D3DVolumeTexture : public X_D3DBaseTexture +{ + +}; + +struct X_D3DCubeTexture : public X_D3DBaseTexture +{ + +}; + +struct X_D3DVolume : public X_D3DPixelContainer +{ + X_D3DBaseTexture *Parent; +}; + +struct X_D3DSurface : public X_D3DPixelContainer +{ + X_D3DBaseTexture *Parent; +}; + +struct X_D3DTILE +{ + DWORD Flags; + PVOID pMemory; + DWORD Size; + DWORD Pitch; + DWORD ZStartTag; + DWORD ZOffset; +}; + +typedef enum _X_D3DCALLBACKTYPE // blueshogun96 10/1/07 +{ + X_D3DCALLBACK_READ = 0, // Fixed PatrickvL 10/7/22 + X_D3DCALLBACK_WRITE = 1 +} +X_D3DCALLBACKTYPE; + +typedef enum _X_D3DFIELDTYPE +{ + X_D3DFIELD_ODD = 1, + X_D3DFIELD_EVEN = 2, + X_D3DFIELD_PROGRESSIVE = 3, + X_D3DFIELD_FORCE_DWORD = 0x7fffffff +} +X_D3DFIELDTYPE; + +typedef struct _X_D3DFIELD_STATUS +{ + X_D3DFIELDTYPE Field; + UINT VBlankCount; +} +X_D3DFIELD_STATUS; + +typedef struct _D3DVBLANKDATA +{ + DWORD VBlank; + DWORD Swap; + DWORD Flags; +} +X_D3DVBLANKDATA; + +typedef struct _D3DSWAPDATA +{ + DWORD Swap; + DWORD SwapVBlank; + DWORD MissedVBlanks; + DWORD TimeUntilSwapVBlank; + DWORD TimeBetweenSwapVBlanks; +} +X_D3DSWAPDATA; + +// D3DVBLANKCALLBACK +typedef void (__cdecl * X_D3DVBLANKCALLBACK)(X_D3DVBLANKDATA *pData); + +// D3DSWAPCALLBACK +typedef void (__cdecl * X_D3DSWAPCALLBACK)(X_D3DSWAPDATA *pData); + +// D3DCALLBACK +typedef void (__cdecl * X_D3DCALLBACK)(DWORD Context); + +// X_D3DRENDERSTATETYPE values +typedef enum _X_D3DRENDERSTATETYPE { + + // Dxbx note : These declarations are from XDK version 5933, the most recent and complete version. + // Older versions are slightly different (some members are missing), so we use a mapping table to + // cater for the differences (see XboxRenderStateConverter::BuildRenderStateMappingTable). This enables to ignore these + // version-differences in the rest of our code (unless it matters somehow); We write like this : + // XboxRenderStates.SetXboxRenderState(X_D3DRENDERSTATETYPE, Value); + // + // And we read like this (do note, that missing elements all point to the same dummy) : + // Result = XboxRenderStates.GetXboxRenderState(X_D3DRENDERSTATETYPE); + + // Dxbx note : The PS* render states map 1-on-1 to the X_D3DPIXELSHADERDEF record, + // SetPixelShader actually pushes the definition into these render state slots. + // See DxbxUpdateActivePixelShader for how this is employed. + + // The set starts out with "pixel-shader" render states (all Xbox extensions) : + X_D3DRS_PSALPHAINPUTS0 = 0, + X_D3DRS_PSALPHAINPUTS1 = 1, + X_D3DRS_PSALPHAINPUTS2 = 2, + X_D3DRS_PSALPHAINPUTS3 = 3, + X_D3DRS_PSALPHAINPUTS4 = 4, + X_D3DRS_PSALPHAINPUTS5 = 5, + X_D3DRS_PSALPHAINPUTS6 = 6, + X_D3DRS_PSALPHAINPUTS7 = 7, + X_D3DRS_PSFINALCOMBINERINPUTSABCD = 8, + X_D3DRS_PSFINALCOMBINERINPUTSEFG = 9, + X_D3DRS_PSCONSTANT0_0 = 10, + X_D3DRS_PSCONSTANT0_1 = 11, + X_D3DRS_PSCONSTANT0_2 = 12, + X_D3DRS_PSCONSTANT0_3 = 13, + X_D3DRS_PSCONSTANT0_4 = 14, + X_D3DRS_PSCONSTANT0_5 = 15, + X_D3DRS_PSCONSTANT0_6 = 16, + X_D3DRS_PSCONSTANT0_7 = 17, + X_D3DRS_PSCONSTANT1_0 = 18, + X_D3DRS_PSCONSTANT1_1 = 19, + X_D3DRS_PSCONSTANT1_2 = 20, + X_D3DRS_PSCONSTANT1_3 = 21, + X_D3DRS_PSCONSTANT1_4 = 22, + X_D3DRS_PSCONSTANT1_5 = 23, + X_D3DRS_PSCONSTANT1_6 = 24, + X_D3DRS_PSCONSTANT1_7 = 25, + X_D3DRS_PSALPHAOUTPUTS0 = 26, + X_D3DRS_PSALPHAOUTPUTS1 = 27, + X_D3DRS_PSALPHAOUTPUTS2 = 28, + X_D3DRS_PSALPHAOUTPUTS3 = 29, + X_D3DRS_PSALPHAOUTPUTS4 = 30, + X_D3DRS_PSALPHAOUTPUTS5 = 31, + X_D3DRS_PSALPHAOUTPUTS6 = 32, + X_D3DRS_PSALPHAOUTPUTS7 = 33, + X_D3DRS_PSRGBINPUTS0 = 34, + X_D3DRS_PSRGBINPUTS1 = 35, + X_D3DRS_PSRGBINPUTS2 = 36, + X_D3DRS_PSRGBINPUTS3 = 37, + X_D3DRS_PSRGBINPUTS4 = 38, + X_D3DRS_PSRGBINPUTS5 = 39, + X_D3DRS_PSRGBINPUTS6 = 40, + X_D3DRS_PSRGBINPUTS7 = 41, + X_D3DRS_PSCOMPAREMODE = 42, + X_D3DRS_PSFINALCOMBINERCONSTANT0 = 43, + X_D3DRS_PSFINALCOMBINERCONSTANT1 = 44, + X_D3DRS_PSRGBOUTPUTS0 = 45, + X_D3DRS_PSRGBOUTPUTS1 = 46, + X_D3DRS_PSRGBOUTPUTS2 = 47, + X_D3DRS_PSRGBOUTPUTS3 = 48, + X_D3DRS_PSRGBOUTPUTS4 = 49, + X_D3DRS_PSRGBOUTPUTS5 = 50, + X_D3DRS_PSRGBOUTPUTS6 = 51, + X_D3DRS_PSRGBOUTPUTS7 = 52, + X_D3DRS_PSCOMBINERCOUNT = 53, + X_D3DRS_PS_RESERVED = 54, // Dxbx note : This takes the slot of X_D3DPIXELSHADERDEF.PSTextureModes, set by D3DDevice_SetRenderState_LogicOp? + X_D3DRS_PSDOTMAPPING = 55, + X_D3DRS_PSINPUTTEXTURE = 56, + // End of "pixel-shader" render states, continuing with "simple" render states : + X_D3DRS_ZFUNC = 57, // D3DCMPFUNC + X_D3DRS_ALPHAFUNC = 58, // D3DCMPFUNC + X_D3DRS_ALPHABLENDENABLE = 59, // TRUE to enable alpha blending + X_D3DRS_ALPHATESTENABLE = 60, // TRUE to enable alpha tests + X_D3DRS_ALPHAREF = 61, // BYTE + X_D3DRS_SRCBLEND = 62, // D3DBLEND + X_D3DRS_DESTBLEND = 63, // D3DBLEND + X_D3DRS_ZWRITEENABLE = 64, // TRUE to enable Z writes + X_D3DRS_DITHERENABLE = 65, // TRUE to enable dithering + X_D3DRS_SHADEMODE = 66, // D3DSHADEMODE + X_D3DRS_COLORWRITEENABLE = 67, // D3DCOLORWRITEENABLE_ALPHA, etc. per-channel write enable + X_D3DRS_STENCILZFAIL = 68, // D3DSTENCILOP to do if stencil test passes and Z test fails + X_D3DRS_STENCILPASS = 69, // D3DSTENCILOP to do if both stencil and Z tests pass + X_D3DRS_STENCILFUNC = 70, // D3DCMPFUNC + X_D3DRS_STENCILREF = 71, // BYTE reference value used in stencil test + X_D3DRS_STENCILMASK = 72, // BYTE mask value used in stencil test + X_D3DRS_STENCILWRITEMASK = 73, // BYTE write mask applied to values written to stencil buffer + X_D3DRS_BLENDOP = 74, // D3DBLENDOP setting + X_D3DRS_BLENDCOLOR = 75, // D3DCOLOR for D3DBLEND_CONSTANTCOLOR + X_D3DRS_SWATHWIDTH = 76, // D3DSWATHWIDTH (Xbox ext.) + X_D3DRS_POLYGONOFFSETZSLOPESCALE = 77, // float Z factor for shadow maps (Xbox ext.) + X_D3DRS_POLYGONOFFSETZOFFSET = 78, // Xbox ext. + X_D3DRS_POINTOFFSETENABLE = 79, // Xbox ext. + X_D3DRS_WIREFRAMEOFFSETENABLE = 80, // Xbox ext. + X_D3DRS_SOLIDOFFSETENABLE = 81, // Xbox ext. + X_D3DRS_DEPTHCLIPCONTROL = 82, // [4432+] Xbox ext. + X_D3DRS_STIPPLEENABLE = 83, // [4627+] Xbox ext. + X_D3DRS_SIMPLE_UNUSED8 = 84, // [4627+] + X_D3DRS_SIMPLE_UNUSED7 = 85, // [4627+] + X_D3DRS_SIMPLE_UNUSED6 = 86, // [4627+] + X_D3DRS_SIMPLE_UNUSED5 = 87, // [4627+] + X_D3DRS_SIMPLE_UNUSED4 = 88, // [4627+] + X_D3DRS_SIMPLE_UNUSED3 = 89, // [4627+] + X_D3DRS_SIMPLE_UNUSED2 = 90, // [4627+] + X_D3DRS_SIMPLE_UNUSED1 = 91, // [4627+] + // End of "simple" render states, continuing with "deferred" render states : + X_D3DRS_FOGENABLE = 92, + X_D3DRS_FOGTABLEMODE = 93, + X_D3DRS_FOGSTART = 94, + X_D3DRS_FOGEND = 95, + X_D3DRS_FOGDENSITY = 96, + X_D3DRS_RANGEFOGENABLE = 97, + X_D3DRS_WRAP0 = 98, + X_D3DRS_WRAP1 = 99, + X_D3DRS_WRAP2 = 100, // Dxbx addition + X_D3DRS_WRAP3 = 101, // Dxbx addition + X_D3DRS_LIGHTING = 102, + X_D3DRS_SPECULARENABLE = 103, + X_D3DRS_LOCALVIEWER = 104, // Dxbx addition + X_D3DRS_COLORVERTEX = 105, + X_D3DRS_BACKSPECULARMATERIALSOURCE = 106, // Xbox ext. nsp. + X_D3DRS_BACKDIFFUSEMATERIALSOURCE = 107, // Xbox ext. nsp. + X_D3DRS_BACKAMBIENTMATERIALSOURCE = 108, // Xbox ext. nsp. + X_D3DRS_BACKEMISSIVEMATERIALSOURCE = 109, // Xbox ext. nsp. + X_D3DRS_SPECULARMATERIALSOURCE = 110, + X_D3DRS_DIFFUSEMATERIALSOURCE = 111, + X_D3DRS_AMBIENTMATERIALSOURCE = 112, + X_D3DRS_EMISSIVEMATERIALSOURCE = 113, + X_D3DRS_BACKAMBIENT = 114, // Xbox ext. nsp. + X_D3DRS_AMBIENT = 115, + X_D3DRS_POINTSIZE = 116, + X_D3DRS_POINTSIZE_MIN = 117, + X_D3DRS_POINTSPRITEENABLE = 118, + X_D3DRS_POINTSCALEENABLE = 119, + X_D3DRS_POINTSCALE_A = 120, + X_D3DRS_POINTSCALE_B = 121, + X_D3DRS_POINTSCALE_C = 122, + X_D3DRS_POINTSIZE_MAX = 123, + X_D3DRS_PATCHEDGESTYLE = 124, // Dxbx addition + X_D3DRS_PATCHSEGMENTS = 125, + X_D3DRS_SWAPFILTER = 126, // [4039+] Xbox ext. nsp. D3DTEXF_LINEAR etc. filter to use for Swap + X_D3DRS_PRESENTATIONINTERVAL = 127, // [4627+] Xbox ext. nsp. TODO : Use 4361? + X_D3DRS_DEFERRED_UNUSED8 = 128, // [4627+] + X_D3DRS_DEFERRED_UNUSED7 = 129, // [4627+] + X_D3DRS_DEFERRED_UNUSED6 = 130, // [4627+] + X_D3DRS_DEFERRED_UNUSED5 = 131, // [4627+] + X_D3DRS_DEFERRED_UNUSED4 = 132, // [4627+] + X_D3DRS_DEFERRED_UNUSED3 = 133, // [4627+] + X_D3DRS_DEFERRED_UNUSED2 = 134, // [4627+] + X_D3DRS_DEFERRED_UNUSED1 = 135, // [4627+] + // End of "deferred" render states, continuing with "complex" render states : + X_D3DRS_PSTEXTUREMODES = 136, // Xbox ext. + X_D3DRS_VERTEXBLEND = 137, + X_D3DRS_FOGCOLOR = 138, + X_D3DRS_FILLMODE = 139, + X_D3DRS_BACKFILLMODE = 140, // Dxbx addition : Xbox ext. nsp. + X_D3DRS_TWOSIDEDLIGHTING = 141, // Dxbx addition : Xbox ext. nsp. + X_D3DRS_NORMALIZENORMALS = 142, + X_D3DRS_ZENABLE = 143, + X_D3DRS_STENCILENABLE = 144, + X_D3DRS_STENCILFAIL = 145, + X_D3DRS_FRONTFACE = 146, // Dxbx addition : Xbox ext. nsp. + X_D3DRS_CULLMODE = 147, + X_D3DRS_TEXTUREFACTOR = 148, + X_D3DRS_ZBIAS = 149, + X_D3DRS_LOGICOP = 150, // Xbox ext. nsp. + X_D3DRS_EDGEANTIALIAS = 151, // Dxbx note : No Xbox ext. (according to Direct3D8) ! + X_D3DRS_MULTISAMPLEANTIALIAS = 152, + X_D3DRS_MULTISAMPLEMASK = 153, + X_D3DRS_MULTISAMPLETYPE = 154, // [-4039] Xbox ext. + // Note : X_D3DRS_MULTISAMPLETYPE seems the only one that got removed, but it does need a slot, so the rest is increased by 1 compared to 5933. + X_D3DRS_MULTISAMPLEMODE = 155, // [4361+] Xbox ext. // D3DMULTISAMPLEMODE for the backbuffer + X_D3DRS_MULTISAMPLERENDERTARGETMODE = 156, // [4039+] Xbox ext. + X_D3DRS_SHADOWFUNC = 157, // D3DCMPFUNC (Xbox extension) + X_D3DRS_LINEWIDTH = 158, // Xbox ext. + X_D3DRS_SAMPLEALPHA = 159, // Xbox ext. + X_D3DRS_DXT1NOISEENABLE = 160, // Xbox ext. + X_D3DRS_YUVENABLE = 161, // [3911+] Xbox ext. + X_D3DRS_OCCLUSIONCULLENABLE = 162, // [3911+] Xbox ext. + X_D3DRS_STENCILCULLENABLE = 163, // [3911+] Xbox ext. + X_D3DRS_ROPZCMPALWAYSREAD = 164, // [3911+] Xbox ext. + X_D3DRS_ROPZREAD = 165, // [3911+] Xbox ext. + X_D3DRS_DONOTCULLUNCOMPRESSED = 166, // [3911+] Xbox ext. + // End of "complex" render states. + X_D3DRS_UNK = 0x7fffffff // deferred render state "unknown" flag +} X_D3DRENDERSTATETYPE; + +// Render state boundaries : + +#define X_D3DRS_PS_FIRST X_D3DRS_PSALPHAINPUTS0 +#define X_D3DRS_PS_LAST X_D3DRS_PSINPUTTEXTURE + +#define X_D3DRS_SIMPLE_FIRST X_D3DRS_ZFUNC +#define X_D3DRS_SIMPLE_LAST X_D3DRS_SIMPLE_UNUSED1 + +#define X_D3DRS_DEFERRED_FIRST X_D3DRS_FOGENABLE +#define X_D3DRS_DEFERRED_LAST X_D3DRS_DEFERRED_UNUSED1 + +#define X_D3DRS_COMPLEX_FIRST X_D3DRS_PSTEXTUREMODES +#define X_D3DRS_COMPLEX_LAST X_D3DRS_DONOTCULLUNCOMPRESSED + +#define X_D3DRS_FIRST X_D3DRS_PS_FIRST +#define X_D3DRS_LAST X_D3DRS_COMPLEX_LAST + +// X_D3DWRAP values : +constexpr DWORD X_D3DWRAP_U = 0x00000010; +constexpr DWORD X_D3DWRAP_V = 0x00001000; +constexpr DWORD X_D3DWRAP_W = 0x00100000; + +// X_D3DTEXTURESTAGESTATETYPE values : +// Dxbx note : See DxbxFromOldVersion_D3DTSS(), as these might need correction for older SDK versions! +// The set starts out with "deferred" texture states : +constexpr DWORD X_D3DTSS_ADDRESSU = 0; +constexpr DWORD X_D3DTSS_ADDRESSV = 1; +constexpr DWORD X_D3DTSS_ADDRESSW = 2; +constexpr DWORD X_D3DTSS_MAGFILTER = 3; +constexpr DWORD X_D3DTSS_MINFILTER = 4; +constexpr DWORD X_D3DTSS_MIPFILTER = 5; +constexpr DWORD X_D3DTSS_MIPMAPLODBIAS = 6; +constexpr DWORD X_D3DTSS_MAXMIPLEVEL = 7; +constexpr DWORD X_D3DTSS_MAXANISOTROPY = 8; +constexpr DWORD X_D3DTSS_COLORKEYOP = 9; // Xbox ext. +constexpr DWORD X_D3DTSS_COLORSIGN = 10; // Xbox ext. +constexpr DWORD X_D3DTSS_ALPHAKILL = 11; // Xbox ext. +constexpr DWORD X_D3DTSS_COLOROP = 12; +constexpr DWORD X_D3DTSS_COLORARG0 = 13; +constexpr DWORD X_D3DTSS_COLORARG1 = 14; +constexpr DWORD X_D3DTSS_COLORARG2 = 15; +constexpr DWORD X_D3DTSS_ALPHAOP = 16; +constexpr DWORD X_D3DTSS_ALPHAARG0 = 17; +constexpr DWORD X_D3DTSS_ALPHAARG1 = 18; +constexpr DWORD X_D3DTSS_ALPHAARG2 = 19; +constexpr DWORD X_D3DTSS_RESULTARG = 20; +constexpr DWORD X_D3DTSS_TEXTURETRANSFORMFLAGS = 21; +// End of "deferred" texture states, continuing with the rest : +constexpr DWORD X_D3DTSS_BUMPENVMAT00 = 22; +constexpr DWORD X_D3DTSS_BUMPENVMAT01 = 23; +constexpr DWORD X_D3DTSS_BUMPENVMAT11 = 24; +constexpr DWORD X_D3DTSS_BUMPENVMAT10 = 25; +constexpr DWORD X_D3DTSS_BUMPENVLSCALE = 26; +constexpr DWORD X_D3DTSS_BUMPENVLOFFSET = 27; +constexpr DWORD X_D3DTSS_TEXCOORDINDEX = 28; +constexpr DWORD X_D3DTSS_BORDERCOLOR = 29; +constexpr DWORD X_D3DTSS_COLORKEYCOLOR = 30; // Xbox ext. +constexpr DWORD X_D3DTSS_UNSUPPORTED = 31; // Note : Somehow, this one comes through D3DDevice_SetTextureStageStateNotInline sometimes +// End of texture states. + +// Texture state boundaries : + +constexpr DWORD X_D3DTSS_DEFERRED_FIRST = X_D3DTSS_ADDRESSU; +constexpr DWORD X_D3DTSS_DEFERRED_LAST = X_D3DTSS_TEXTURETRANSFORMFLAGS; + +constexpr DWORD X_D3DTSS_FIRST = X_D3DTSS_ADDRESSU; +constexpr DWORD X_D3DTSS_LAST = X_D3DTSS_COLORKEYCOLOR; + +constexpr DWORD X_D3DTS_STAGECOUNT = 4; // Dxbx addition +constexpr DWORD X_D3DTS_STAGESIZE = 32; // Dxbx addition + +constexpr DWORD X_PSH_COMBINECOUNT = 8; // Dxbx addition +constexpr DWORD X_PSH_CONSTANTCOUNT = 8; // Dxbx addition + +// X_D3DTEXTUREOP values : +constexpr DWORD X_D3DTOP_DISABLE = 1; +constexpr DWORD X_D3DTOP_SELECTARG1 = 2; +constexpr DWORD X_D3DTOP_SELECTARG2 = 3; +constexpr DWORD X_D3DTOP_MODULATE = 4; +constexpr DWORD X_D3DTOP_MODULATE2X = 5; +constexpr DWORD X_D3DTOP_MODULATE4X = 6; +constexpr DWORD X_D3DTOP_ADD = 7; +constexpr DWORD X_D3DTOP_ADDSIGNED = 8; +constexpr DWORD X_D3DTOP_ADDSIGNED2X = 9; +constexpr DWORD X_D3DTOP_SUBTRACT = 10; +constexpr DWORD X_D3DTOP_ADDSMOOTH = 11; +constexpr DWORD X_D3DTOP_BLENDDIFFUSEALPHA = 12; +constexpr DWORD X_D3DTOP_BLENDCURRENTALPHA = 13; +constexpr DWORD X_D3DTOP_BLENDTEXTUREALPHA = 14; +constexpr DWORD X_D3DTOP_BLENDFACTORALPHA = 15; +constexpr DWORD X_D3DTOP_BLENDTEXTUREALPHAPM = 16; +constexpr DWORD X_D3DTOP_PREMODULATE = 17; +constexpr DWORD X_D3DTOP_MODULATEALPHA_ADDCOLOR = 18; +constexpr DWORD X_D3DTOP_MODULATECOLOR_ADDALPHA = 19; +constexpr DWORD X_D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20; +constexpr DWORD X_D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21; +constexpr DWORD X_D3DTOP_DOTPRODUCT3 = 22; +constexpr DWORD X_D3DTOP_MULTIPLYADD = 23; +constexpr DWORD X_D3DTOP_LERP = 24; +constexpr DWORD X_D3DTOP_BUMPENVMAP = 25; +constexpr DWORD X_D3DTOP_BUMPENVMAPLUMINANCE = 26; + +constexpr DWORD X_D3DTOP_FIRST = X_D3DTOP_DISABLE; +constexpr DWORD X_D3DTOP_LAST = X_D3DTOP_BUMPENVMAPLUMINANCE; + +// X_D3DTEXTUREADDRESS values : +constexpr DWORD X_D3DTADDRESS_WRAP = 1; +constexpr DWORD X_D3DTADDRESS_MIRROR = 2; +constexpr DWORD X_D3DTADDRESS_CLAMP = 3; +constexpr DWORD X_D3DTADDRESS_BORDER = 4; +constexpr DWORD X_D3DTADDRESS_CLAMPTOEDGE = 5; + +// X_D3DTEXTUREFILTERTYPE Values +constexpr DWORD X_D3DTEXF_NONE = 0; +constexpr DWORD X_D3DTEXF_POINT = 1; +constexpr DWORD X_D3DTEXF_LINEAR = 2; +constexpr DWORD X_D3DTEXF_ANISOTROPIC = 3; +constexpr DWORD X_D3DTEXF_QUINCUNX = 4; // Xbox extension +constexpr DWORD X_D3DTEXF_GAUSSIANCUBIC = 5; + +// X_D3DCLEAR values : +constexpr DWORD X_D3DCLEAR_ZBUFFER = 0x00000001; +constexpr DWORD X_D3DCLEAR_STENCIL = 0x00000002; +constexpr DWORD X_D3DCLEAR_TARGET_R = 0x00000010; // Clear target surface R component (Xbox ext.) +constexpr DWORD X_D3DCLEAR_TARGET_G = 0x00000020; // Clear target surface G component (Xbox ext.) +constexpr DWORD X_D3DCLEAR_TARGET_B = 0x00000040; // Clear target surface B component (Xbox ext.) +constexpr DWORD X_D3DCLEAR_TARGET_A = 0x00000080; // Clear target surface A component (Xbox ext.) +constexpr DWORD X_D3DCLEAR_TARGET = X_D3DCLEAR_TARGET_R | X_D3DCLEAR_TARGET_G | X_D3DCLEAR_TARGET_B | X_D3DCLEAR_TARGET_A; + +// X_D3DCOLORWRITEENABLE values : +constexpr DWORD X_D3DCOLORWRITEENABLE_RED = (1 << 16); +constexpr DWORD X_D3DCOLORWRITEENABLE_GREEN = (1 << 8); +constexpr DWORD X_D3DCOLORWRITEENABLE_BLUE = (1 << 0); +constexpr DWORD X_D3DCOLORWRITEENABLE_ALPHA = (1 << 24); +constexpr DWORD X_D3DCOLORWRITEENABLE_ALL = 0x01010101; // Xbox ext. + +// deferred texture stage state "unknown" flag +#define X_D3DTSS_UNK 0x7fffffff + +typedef DWORD X_VERTEXSHADERCONSTANTMODE; + +// Xbox vertex shader constant modes +#define X_D3DSCM_96CONSTANTS 0x00 // Enables constants 0..95 +#define X_D3DSCM_192CONSTANTS 0x01 // Enables constants -96..-1 on top of 0..95 +#define X_D3DSCM_192CONSTANTSANDFIXEDPIPELINE 0x02 // Unsupported? +#define X_D3DSCM_NORESERVEDCONSTANTS 0x10 // Do not reserve constant -38 and -37 + +#define X_D3DSCM_RESERVED_CONSTANT_SCALE -38 // Becomes 58 after correction, contains Scale v +#define X_D3DSCM_RESERVED_CONSTANT_OFFSET -37 // Becomes 59 after correction, contains Offset + +#define X_D3DSCM_CORRECTION 96 // Add 96 to arrive at the range 0..191 (instead of -96..95) +#define X_D3DVS_CONSTREG_COUNT 192 + +// Special Registers, used to pass additional information to the shaders +// TODO co-locate shader workaround constants with shader code +#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE (X_D3DVS_CONSTREG_COUNT) +#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + 16) +#define CXBX_D3DVS_VIEWPORT_SCALE_MIRROR (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE + 4) +#define CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR (CXBX_D3DVS_VIEWPORT_SCALE_MIRROR + 1) + +#define X_D3DSCM_RESERVED_CONSTANT_SCALE_CORRECTED (X_D3DSCM_RESERVED_CONSTANT_SCALE + X_D3DSCM_CORRECTION) +#define X_D3DSCM_RESERVED_CONSTANT_OFFSET_CORRECTED (X_D3DSCM_RESERVED_CONSTANT_OFFSET + X_D3DSCM_CORRECTION) + +// Xbox vertex declaration token bit masks +#define X_D3DVSD_MASK_TESSUV 0x10000000 +#define X_D3DVSD_MASK_SKIP 0x10000000 // Skips (normally) dwords +#define X_D3DVSD_MASK_SKIPBYTES 0x08000000 // Skips bytes (no, really?!) +#define X_D3DVSD_STREAMTESSMASK (1 << 28) + +// Xbox vertex shader types +#define X_VST_NORMAL 1 +#define X_VST_READWRITE 2 +#define X_VST_STATE 3 + +// Xbox vertex shader counts +#define X_VSH_MAX_ATTRIBUTES 16 +#define X_VSH_MAX_STREAMS 16 +#define X_VSH_MAX_INSTRUCTION_COUNT 136 // The maximum Xbox shader instruction count + +// Xbox Vertex Shader versions +#define VERSION_XVS 0x2078 // 'x ' Xbox vertex shader +#define VERSION_XVSS 0x7378 // 'xs' Xbox vertex state shader +#define VERSION_XVSW 0x7778 // 'xw' Xbox vertex read/write shader + +/// nv2a microcode header +typedef struct +{ + uint16_t Version; // See VERSION_XVS* + uint16_t NumInst; +} +X_VSH_SHADER_HEADER; + +#define X_VSH_INSTRUCTION_SIZE 4 +#define X_VSH_INSTRUCTION_SIZE_BYTES (X_VSH_INSTRUCTION_SIZE * sizeof(DWORD)) + +// ****************************************************************** +// * X_VERTEXSHADERINPUT +// ****************************************************************** +typedef struct _X_VERTEXSHADERINPUT +{ + DWORD IndexOfStream; + DWORD Offset; + DWORD Format; + BYTE TesselationType; + BYTE TesselationSource; +} +X_VERTEXSHADERINPUT; + +// ****************************************************************** +// * X_VERTEXATTRIBUTEFORMAT +// ****************************************************************** +typedef struct _X_VERTEXATTRIBUTEFORMAT +{ + // Note : Alignment looks okay even without #pragma pack(1) / #include "AlignPrefix1.h" (and it's closure) + X_VERTEXSHADERINPUT Slots[X_VSH_MAX_ATTRIBUTES]; +} +X_VERTEXATTRIBUTEFORMAT; + +// ****************************************************************** +// * X_STREAMINPUT +// ****************************************************************** +typedef struct _X_STREAMINPUT +{ + X_D3DVertexBuffer *VertexBuffer; + UINT Stride; + UINT Offset; +} X_STREAMINPUT; + +struct X_D3DVertexShader +{ + // Note : Debug XBE's have a 'Vshd' DWORD signature prefixing this! + DWORD RefCount; // Based on the observation this member is set to 1 in D3DDevice_CreateVertexShader and decreased in D3DDevice_DeleteVertexShader + DWORD Flags; + DWORD FunctionSize; // ?Also known as ProgramSize? + DWORD TotalSize; // seems to include both the function and ?constants? + DWORD NumberOfDimensionsPerTexture; // Guesswork, since all 4 bytes (for all 4 textures) are most often set to 0 (or 2 when a texture isn't used) and 1, 3 and 4 also occur (and nothing else) + X_VERTEXATTRIBUTEFORMAT VertexAttribute; + DWORD FunctionData[X_VSH_MAX_INSTRUCTION_COUNT]; // probably the binary function data and ?constants? (data continues futher outside this struct, up to TotalSize DWORD's) +}; + +// vertex shader input registers for fixed function vertex shader + +// Name Register number D3DFVF +const int X_D3DVSDE_POSITION = 0; // Corresponds to D3DFVF_XYZ +const int X_D3DVSDE_BLENDWEIGHT = 1; // Corresponds to D3DFVF_XYZRHW +const int X_D3DVSDE_NORMAL = 2; // Corresponds to D3DFVF_NORMAL +const int X_D3DVSDE_DIFFUSE = 3; // Corresponds to D3DFVF_DIFFUSE +const int X_D3DVSDE_SPECULAR = 4; // Corresponds to D3DFVF_SPECULAR +const int X_D3DVSDE_FOG = 5; // Xbox extension +const int X_D3DVSDE_POINTSIZE = 6; // Dxbx addition +const int X_D3DVSDE_BACKDIFFUSE = 7; // Xbox extension +const int X_D3DVSDE_BACKSPECULAR = 8; // Xbox extension +const int X_D3DVSDE_TEXCOORD0 = 9; // Corresponds to D3DFVF_TEX1 (not D3DFVF_TEX0, which means no textures are present) +const int X_D3DVSDE_TEXCOORD1 = 10; // Corresponds to D3DFVF_TEX2 +const int X_D3DVSDE_TEXCOORD2 = 11; // Corresponds to D3DFVF_TEX3 +const int X_D3DVSDE_TEXCOORD3 = 12; // Corresponds to D3DFVF_TEX4 +const int X_D3DVSDE_VERTEX = 0xFFFFFFFF; // Xbox extension for Begin/End drawing (data is a D3DVSDT_FLOAT4) + +//typedef X_D3DVSDE = X_D3DVSDE_POSITION..High(DWORD)-2; // Unique declaration to make overloads possible; + +// bit declarations for _Type fields +const int X_D3DVSDT_FLOAT1 = 0x12; // 1D float expanded to (value, 0.0, 0.0, 1.0) +const int X_D3DVSDT_FLOAT2 = 0x22; // 2D float expanded to (value, value, 0.0, 1.0) +const int X_D3DVSDT_FLOAT3 = 0x32; // 3D float expanded to (value, value, value, 1.0) In double word format this is ARGB, or in byte ordering it would be B, G, R, A. +const int X_D3DVSDT_FLOAT4 = 0x42; // 4D float +const int X_D3DVSDT_D3DCOLOR = 0x40; // 4D packed unsigned bytes mapped to 0.0 to 1.0 range +//const int X_D3DVSDT_UBYTE4 = 0x05; // 4D unsigned byte Dxbx note : Not supported on Xbox ? +const int X_D3DVSDT_SHORT2 = 0x25; // 2D signed short expanded to (value, value, 0.0, 1.0) +const int X_D3DVSDT_SHORT4 = 0x45; // 4D signed short + +// Xbox only declarations : +const int X_D3DVSDT_NORMSHORT1 = 0x11; // xbox ext. 1D signed, normalized short expanded to (value, 0.0, 0.0, 1.0). Signed, normalized shorts map from -1.0 to 1.0. +const int X_D3DVSDT_NORMSHORT2 = 0x21; // xbox ext. 2D signed, normalized short expanded to (value, value, 0.0, 1.0). Signed, normalized shorts map from -1.0 to 1.0. +const int X_D3DVSDT_NORMSHORT3 = 0x31; // xbox ext. 3D signed, normalized short expanded to (value, value, value, 1.0). Signed, normalized shorts map from -1.0 to 1.0. +const int X_D3DVSDT_NORMSHORT4 = 0x41; // xbox ext. 4D signed, normalized short expanded to (value, value, value, value). Signed, normalized shorts map from -1.0 to 1.0. +const int X_D3DVSDT_NORMPACKED3 = 0x16; // xbox ext. Three signed, normalized components packed in 32-bits. (11,11,10). Each component ranges from -1.0 to 1.0. Expanded to (value, value, value, 1.0). +const int X_D3DVSDT_SHORT1 = 0x15; // xbox ext. 1D signed short expanded to (value, 0., 0., 1). Signed shorts map to the range [-32768, 32767]. +const int X_D3DVSDT_SHORT3 = 0x35; // xbox ext. 3D signed short expanded to (value, value, value, 1). Signed shorts map to the range [-32768, 32767]. +const int X_D3DVSDT_PBYTE1 = 0x14; // xbox ext. 1D packed byte expanded to (value, 0., 0., 1). Packed bytes map to the range [0, 1]. +const int X_D3DVSDT_PBYTE2 = 0x24; // xbox ext. 2D packed byte expanded to (value, value, 0., 1). Packed bytes map to the range [0, 1]. +const int X_D3DVSDT_PBYTE3 = 0x34; // xbox ext. 3D packed byte expanded to (value, value, value, 1). Packed bytes map to the range [0, 1]. +const int X_D3DVSDT_PBYTE4 = 0x44; // xbox ext. 4D packed byte expanded to (value, value, value, value). Packed bytes map to the range [0, 1]. +const int X_D3DVSDT_FLOAT2H = 0x72; // xbox ext. 3D float that expands to (value, value, 0.0, value). Useful for projective texture coordinates. +const int X_D3DVSDT_NONE = 0x02; // xbox ext. nsp + +typedef enum _X_D3DVSD_TOKENTYPE +{ + X_D3DVSD_TOKEN_NOP = 0, // NOP or extension + X_D3DVSD_TOKEN_STREAM, // stream selector + X_D3DVSD_TOKEN_STREAMDATA, // stream data definition (map to vertex input memory) + X_D3DVSD_TOKEN_TESSELLATOR, // vertex input memory from tessellator + X_D3DVSD_TOKEN_CONSTMEM, // constant memory from shader + X_D3DVSD_TOKEN_EXT, // extension + X_D3DVSD_TOKEN_END = 7, // end-of-array (requires all DWORD bits to be 1) + X_D3DVSD_FORCE_DWORD = 0x7fffffff,// force 32-bit size enum +} X_D3DVSD_TOKENTYPE; + +#define X_D3DVSD_TOKENTYPESHIFT 29 +#define X_D3DVSD_TOKENTYPEMASK (7 << X_D3DVSD_TOKENTYPESHIFT) + +#define X_D3DVSD_STREAMNUMBERSHIFT 0 +#define X_D3DVSD_STREAMNUMBERMASK (0xF << X_D3DVSD_STREAMNUMBERSHIFT) + +#define X_D3DVSD_DATALOADTYPESHIFT 28 +#define X_D3DVSD_DATALOADTYPEMASK (0x1 << X_D3DVSD_DATALOADTYPESHIFT) + +#define X_D3DVSD_DATATYPESHIFT 16 +#define X_D3DVSD_DATATYPEMASK (0xFF << X_D3DVSD_DATATYPESHIFT) + +#define X_D3DVSD_SKIPCOUNTSHIFT 16 +#define X_D3DVSD_SKIPCOUNTMASK (0xF << X_D3DVSD_SKIPCOUNTSHIFT) + +#define X_D3DVSD_VERTEXREGSHIFT 0 +#define X_D3DVSD_VERTEXREGMASK (0x1F << X_D3DVSD_VERTEXREGSHIFT) + +#define X_D3DVSD_VERTEXREGINSHIFT 20 +#define X_D3DVSD_VERTEXREGINMASK (0xF << X_D3DVSD_VERTEXREGINSHIFT) + +#define X_D3DVSD_CONSTCOUNTSHIFT 25 +#define X_D3DVSD_CONSTCOUNTMASK (0xF << X_D3DVSD_CONSTCOUNTSHIFT) + +#define X_D3DVSD_CONSTADDRESSSHIFT 0 +#define X_D3DVSD_CONSTADDRESSMASK (0xFF << X_D3DVSD_CONSTADDRESSSHIFT) + +#define X_D3DVSD_CONSTRSSHIFT 16 +#define X_D3DVSD_CONSTRSMASK (0x1FFF << X_D3DVSD_CONSTRSSHIFT) + +#define X_D3DVSD_EXTCOUNTSHIFT 24 +#define X_D3DVSD_EXTCOUNTMASK (0x1F << X_D3DVSD_EXTCOUNTSHIFT) + +#define X_D3DVSD_EXTINFOSHIFT 0 +#define X_D3DVSD_EXTINFOMASK (0xFFFFFF << X_D3DVSD_EXTINFOSHIFT) + +#define X_D3DVSD_MAKETOKENTYPE(Type) ((Type << X_D3DVSD_TOKENTYPESHIFT) & X_D3DVSD_TOKENTYPEMASK) + +#define X_D3DVSD_STREAM(Stream) (X_D3DVSD_MAKETOKENTYPE(X_D3DVSD_TOKEN_STREAM) | (Stream)) +#define X_D3DVSD_REG(Reg, Type) (X_D3DVSD_MAKETOKENTYPE(X_D3DVSD_TOKEN_STREAMDATA) | ((Type) << X_D3DVSD_DATATYPESHIFT) | (Reg)) + +#define X_D3DVSD_NOP() 0x00000000 +#define X_D3DVSD_END() 0xFFFFFFFF + +// FVF Definitions +#define X_D3DFVF_RESERVED0 0x001 +#define X_D3DFVF_XYZ 0x002 +#define X_D3DFVF_XYZRHW 0x004 +#define X_D3DFVF_XYZB1 0x006 +#define X_D3DFVF_XYZB2 0x008 +#define X_D3DFVF_XYZB3 0x00a +#define X_D3DFVF_XYZB4 0x00c +#define X_D3DFVF_POSITION_MASK 0x00E +#define X_D3DFVF_NORMAL 0x010 +#define X_D3DFVF_RESERVED1 0x020 +#define X_D3DFVF_DIFFUSE 0x040 +#define X_D3DFVF_SPECULAR 0x080 +#define X_D3DFVF_TEXCOUNT_MASK 0xf00 +#define X_D3DFVF_TEXCOUNT_SHIFT 8 +#define X_D3DFVF_TEX0 0x000 +#define X_D3DFVF_TEX1 0x100 +#define X_D3DFVF_TEX2 0x200 +#define X_D3DFVF_TEX3 0x300 +#define X_D3DFVF_TEX4 0x400 +#define X_D3DFVF_TEX5 0x500 +#define X_D3DFVF_TEX6 0x600 +#define X_D3DFVF_TEX7 0x700 +#define X_D3DFVF_TEX8 0x800 +#define X_D3DFVF_TEXTUREFORMAT1 0x003 +#define X_D3DFVF_TEXTUREFORMAT2 0x000 +#define X_D3DFVF_TEXTUREFORMAT3 0x001 +#define X_D3DFVF_TEXTUREFORMAT4 0x002 + +#define X_D3DFVF_TEXCOORDSIZE1(Index) (X_D3DFVF_TEXTUREFORMAT1 << (Index * 2 + 16)) +#define X_D3DFVF_TEXCOORDSIZE2(Index) (X_D3DFVF_TEXTUREFORMAT2) +#define X_D3DFVF_TEXCOORDSIZE3(Index) (X_D3DFVF_TEXTUREFORMAT3 << (Index * 2 + 16)) +#define X_D3DFVF_TEXCOORDSIZE4(Index) (X_D3DFVF_TEXTUREFORMAT4 << (Index * 2 + 16)) + +typedef DWORD NV2AMETHOD; + +// +// Below declarations are used by Cxbx, not by the Xbox!!! +// + +} // end of namespace XTL + +#endif diff --git a/src/core/hle/D3D8/XbPixelShader.cpp b/src/core/hle/D3D8/XbPixelShader.cpp index 20130b0ae..feba9eaf4 100644 --- a/src/core/hle/D3D8/XbPixelShader.cpp +++ b/src/core/hle/D3D8/XbPixelShader.cpp @@ -1,8199 +1,8199 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 kingofc -// * -// * All rights reserved -// * -// ****************************************************************** - -/* - - This is a parser for the Xbox Pixel Shader Format. - - With the help of this parser it is possible to generate - Direct3D pixel shader assembly code. - - TODO: - - fix BumpDemo - (after second recompilation the shader does not work, - can also be something in CxbxKrnl because it looks like no - textures are set. Check cubemap loading from resourcesd!!!) - => seems to work now, the problem is that I don't know - how it must look on a real xbox - - - add reference counting constants which were added as c variables - if they are compiled away (optimization of the command, etc.) - decrement the reference count and when it reaches 0 remove - the constant (to save the num of vars) - - - add _sat feature - * Support as instruction modifier, - if necessary as mov_sat x, y - - - When porting to DirectX 9, expand this to pixel shader model 2.0 or up - - Alternatively, translate to HLSL and let D3DXCompileShader/D3DCompile figure it out -*/ - -#define LOG_PREFIX CXBXR_MODULE::PXSH - -#include "core\kernel\support\Emu.h" -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice, g_pXbox_PixelShader -#include "core\hle\D3D8\XbPixelShader.h" -#include "core\hle\D3D8\XbD3D8Logging.h" // For D3DErrorString() - -#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup() - -#include // assert() -#include -#include - -#include "Direct3D9\RenderStates.h" -extern XboxRenderStateConverter XboxRenderStates; - -#define DbgPshPrintf \ - LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \ - if(g_bPrintfOn) printf - - -/*---------------------------------------------------------------------------*/ -/* Texture configuration - The following members of the D3DPixelShaderDef */ -/* structure define the addressing modes of each of the four texture stages:*/ -/* PSTextureModes */ -/* PSDotMapping */ -/* PSInputTexture */ -/* PSCompareMode */ -/*---------------------------------------------------------------------------*/ - -// ========================================================================================================= -// PSTextureModes -// --------.--------.--------.---xxxxx stage0 -// --------.--------.------xx.xxx----- stage1 -// --------.--------.-xxxxx--.-------- stage2 -// --------.----xxxx.x-------.-------- stage3 - -#define PS_TEXTUREMODES(t0,t1,t2,t3) (((t3)<<15)|((t2)<<10)|((t1)<<5)|(t0)) - -/* -Texture modes: -NONE :stage inactive -PROJECT2D :argb = texture(s/q, t/q) -PROJECT3D :argb = texture(s/q, t/q, r/q) -CUBEMAP :argb = cubemap(s,t,r) -PASSTHRU :argb = s,t,r,q -CLIPPLANE :pixel not drawn if s,t,r, or q < 0. PSCompareMode affects comparison -BUMPENVMAP :argb=texture(s+mat00*src.r+mat01*src.g, - t+mat10*src.r+mat11*src.g) - mat00 set via D3DTSS_BUMPENVMAT00, etc. -BUMPENVMAP_LUM :argb=texture(s+mat00*src.r+mat01*src.g, - t+mat10*src.r+mat11*src.g); - rgb *= (lum_scale*src.b + lum_bias); (a is not affected) - lum_scale set by D3DTSS_BUMPENVLSCALE - lum_bias set by D3DTSS_BUMPENVLOFFSET - mat00 set via D3DTSS_BUMPENVMAT00, etc. -BRDF :argb = texture(eyeSigma, lightSigma, dPhi) - eyeSigma = Sigma of eye vector in spherical coordinates - lightSigma = Sigma of light vector in spherical coordinates - dPhi = Phi of eye - Phi of light -DOT_ST :argb = texture(, (s,t,r).(src.r,src.g,src.b)) -DOT_ZW :frag depth = (/((s,t,r).(src.r,src.g,src.b)) -DOT_RFLCT_DIFF :n = (,(s,t,r).(src.r,src.g,src.b),) - argb = cubemap(n) -DOT_RFLCT_SPEC :n = (,,(s,t,r).(src.r,src.g,src.b)) - r = 2*n*(n.e)/(n.n) - e where e is eye vector built from q coord of each stage - argb = cubemap(r) -DOT_STR_3D :argb=texture((,,(s,t,r).(src.r,src.g,src.b))) -DOT_STR_CUBE :argb=cubemap((,,(s,t,r).(src.r,src.g,src.b))) -DEPENDENT_AR :argb = texture(src.a, src.r) -DEPENDENT_GB :argb = texture(src.g, src.b) -DOTPRODUCT :argb = (s,t,r).(src.r,src.g,src.b) -DOT_RFLCT_SPEC_CONST :n = (,,(s,t,r).(src.r,src.g,src.b)) - r = 2*n*(n.e)/(n.n) - e where e is eye vector set via SetEyeVector() - argb = cubemap(r) -*/ - -enum PS_TEXTUREMODES -{ // valid in stage 0 1 2 3 - PS_TEXTUREMODES_NONE= 0x00L, // * * * * - PS_TEXTUREMODES_PROJECT2D= 0x01L, // * * * * - PS_TEXTUREMODES_PROJECT3D= 0x02L, // * * * * - PS_TEXTUREMODES_CUBEMAP= 0x03L, // * * * * - PS_TEXTUREMODES_PASSTHRU= 0x04L, // * * * * - PS_TEXTUREMODES_CLIPPLANE= 0x05L, // * * * * - PS_TEXTUREMODES_BUMPENVMAP= 0x06L, // - * * * - PS_TEXTUREMODES_BUMPENVMAP_LUM= 0x07L, // - * * * - PS_TEXTUREMODES_BRDF= 0x08L, // - - * * - PS_TEXTUREMODES_DOT_ST= 0x09L, // - - * * - PS_TEXTUREMODES_DOT_ZW= 0x0aL, // - - * * - PS_TEXTUREMODES_DOT_RFLCT_DIFF= 0x0bL, // - - * - - PS_TEXTUREMODES_DOT_RFLCT_SPEC= 0x0cL, // - - - * - PS_TEXTUREMODES_DOT_STR_3D= 0x0dL, // - - - * - PS_TEXTUREMODES_DOT_STR_CUBE= 0x0eL, // - - - * - PS_TEXTUREMODES_DPNDNT_AR= 0x0fL, // - * * * - PS_TEXTUREMODES_DPNDNT_GB= 0x10L, // - * * * - PS_TEXTUREMODES_DOTPRODUCT= 0x11L, // - * * - - PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST= 0x12L, // - - - * - // 0x13-0x1f reserved -}; - -// ========================================================================================================= -// PSDotMapping -// --------.--------.--------.-----xxx // stage1 -// --------.--------.--------.-xxx---- // stage2 -// --------.--------.-----xxx.-------- // stage3 - -#define PS_DOTMAPPING(t0,t1,t2,t3) (((t3)<<8)|((t2)<<4)|(t1)) - -// Mappings: -// ZERO_TO_ONE :rgb->(r,g,b): 0x0=>0.0, 0xff=>1.0 -// MINUS1_TO_1_D3D :rgb->(r,g,b): 0x0=>-128/127, 0x01=>-1.0, 0x80=>0.0, 0xff=>1.0 -// MINUS1_TO_1_GL :rgb->(r,g,b): 0x80=>-1.0, 0x0=>0.0, 0x7f=>1.0 -// MINUS1_TO_1 :rgb->(r,g,b): 0x80=>-128/127, 0x81=>-1.0, 0x0=>0.0, 0x7f=>1.0 -// HILO_1 :HL->(H,L,1.0): 0x0000=>0.0, 0xffff=>1.0 -// HILO_HEMISPHERE :HL->(H,L,sqrt(1-H*H-L*L)): 0x8001=>-1.0, 0x0=>0.0, 0x7fff=>1.0, 0x8000=>-32768/32767 - -enum PS_DOTMAPPING -{ // valid in stage 0 1 2 3 - PS_DOTMAPPING_ZERO_TO_ONE= 0x00L, // - * * * - PS_DOTMAPPING_MINUS1_TO_1_D3D= 0x01L, // - * * * - PS_DOTMAPPING_MINUS1_TO_1_GL= 0x02L, // - * * * - PS_DOTMAPPING_MINUS1_TO_1= 0x03L, // - * * * - PS_DOTMAPPING_HILO_1= 0x04L, // - * * * - // ? 0x05L ? - // ? 0x06L ? - PS_DOTMAPPING_HILO_HEMISPHERE= 0x07L, // - * * * -}; - -// ========================================================================================================= -// PSCompareMode -// --------.--------.--------.----xxxx // stage0 -// --------.--------.--------.xxxx---- // stage1 -// --------.--------.----xxxx.-------- // stage2 -// --------.--------.xxxx----.-------- // stage3 - -#define PS_COMPAREMODE(t0,t1,t2,t3) (((t3)<<12)|((t2)<<8)|((t1)<<4)|(t0)) - -enum PS_COMPAREMODE -{ - PS_COMPAREMODE_S_LT= 0x00L, - PS_COMPAREMODE_S_GE= 0x01L, - - PS_COMPAREMODE_T_LT= 0x00L, - PS_COMPAREMODE_T_GE= 0x02L, - - PS_COMPAREMODE_R_LT= 0x00L, - PS_COMPAREMODE_R_GE= 0x04L, - - PS_COMPAREMODE_Q_LT= 0x00L, - PS_COMPAREMODE_Q_GE= 0x08L, -}; - -// ========================================================================================================= -// PSInputTexture -// --------.-------x.--------.-------- // stage2 -// --------.--xx----.--------.-------- // stage3 -// -// Selects the other texture to use as an input in the following texture modes: -// DOT_ST, DOT_STR_3D, DOT_STR_CUBE, DOT_ZW, DOT_RFLCT_SPEC, -// DOT_RFLCT_DIFF, DPNDNT_AR, DPNDNT_GB, BUMPENVMAP, -// BUMPENVMAP_LUM, DOT_PRODUCT - -#define PS_INPUTTEXTURE(t0,t1,t2,t3) (((t3)<<20)|((t2)<<16)) - - -/*---------------------------------------------------------------------------------*/ -/* Color combiners - The following members of the D3DPixelShaderDef structure */ -/* define the state for the eight stages of color combiners: */ -/* PSCombinerCount - Number of stages */ -/* PSAlphaInputs[8] - Inputs for alpha portion of each stage */ -/* PSRGBInputs[8] - Inputs for RGB portion of each stage */ -/* PSConstant0[8] - Constant 0 for each stage */ -/* PSConstant1[8] - Constant 1 for each stage */ -/* PSFinalCombinerConstant0 - Constant 0 for final combiner */ -/* PSFinalCombinerConstant1 - Constant 1 for final combiner */ -/* PSAlphaOutputs[8] - Outputs for alpha portion of each stage */ -/* PSRGBOutputs[8] - Outputs for RGB portion of each stage */ -/*---------------------------------------------------------------------------------*/ - - -// ========================================================================================================= -// PSCombinerCount -// --------.--------.--------.----xxxx // number of combiners (1-8) -// --------.--------.-------x.-------- // mux bit (0= LSB, 1= MSB) -// --------.--------.---x----.-------- // separate C0 -// --------.-------x.--------.-------- // separate C1 - -#define PS_COMBINERCOUNT(count, flags) (((flags)<<8)|(count)) -// count is 1-8, flags contains one or more values from PS_COMBINERCOUNTFLAGS - -enum PS_COMBINERCOUNTFLAGS -{ - PS_COMBINERCOUNT_MUX_LSB= 0x0000L, // mux on r0.a lsb - PS_COMBINERCOUNT_MUX_MSB= 0x0001L, // mux on r0.a msb - - PS_COMBINERCOUNT_SAME_C0= 0x0000L, // c0 same in each stage - PS_COMBINERCOUNT_UNIQUE_C0= 0x0010L, // c0 unique in each stage - - PS_COMBINERCOUNT_SAME_C1= 0x0000L, // c1 same in each stage - PS_COMBINERCOUNT_UNIQUE_C1= 0x0100L // c1 unique in each stage -}; - -// ========================================================================================================= -// PSRGBInputs[0-7] -// PSAlphaInputs[0-7] -// PSFinalCombinerInputsABCD -// PSFinalCombinerInputsEFG -// --------.--------.--------.----xxxx // D register -// --------.--------.--------.---x---- // D channel (0= RGB/BLUE, 1= ALPHA) -// --------.--------.--------.xxx----- // D input mapping -// --------.--------.----xxxx.-------- // C register -// --------.--------.---x----.-------- // C channel (0= RGB/BLUE, 1= ALPHA) -// --------.--------.xxx-----.-------- // C input mapping -// --------.----xxxx.--------.-------- // B register -// --------.---x----.--------.-------- // B channel (0= RGB/BLUE, 1= ALPHA) -// --------.xxx-----.--------.-------- // B input mapping -// ----xxxx.--------.--------.-------- // A register -// ---x----.--------.--------.-------- // A channel (0= RGB/BLUE, 1= ALPHA) -// xxx-----.--------.--------.-------- // A input mapping - -// examples: -// -// shader.PSRGBInputs[3]= PS_COMBINERINPUTS( -// PS_REGISTER_T0 | PS_INPUTMAPPING_EXPAND_NORMAL | PS_CHANNEL_RGB, -// PS_REGISTER_C0 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_ALPHA, -// PS_REGISTER_ZERO, -// PS_REGISTER_ZERO); -// -// shader.PSFinalCombinerInputsABCD= PS_COMBINERINPUTS( -// PS_REGISTER_T0 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_ALPHA, -// PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL | PS_CHANNEL_RGB, -// PS_REGISTER_EFPROD | PS_INPUTMAPPING_UNSIGNED_INVERT | PS_CHANNEL_RGB, -// PS_REGISTER_ZERO); -// -// PS_FINALCOMBINERSETTING is set in 4th field of PSFinalCombinerInputsEFG with PS_COMBINERINPUTS -// example: -// -// shader.PSFinalCombinerInputsEFG= PS_COMBINERINPUTS( -// PS_REGISTER_R0 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_RGB, -// PS_REGISTER_R1 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_RGB, -// PS_REGISTER_R1 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_BLUE, -// PS_FINALCOMBINERSETTING_CLAMP_SUM | PS_FINALCOMBINERSETTING_COMPLEMENT_R0); - -#define PS_COMBINERINPUTS(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) -// For PSFinalCombinerInputsEFG, -// a,b,c contain a value from PS_REGISTER, PS_CHANNEL, and PS_INPUTMAPPING for input E,F, and G -// d contains values from PS_FINALCOMBINERSETTING -// For all other inputs, -// a,b,c,d each contain a value from PS_REGISTER, PS_CHANNEL, and PS_INPUTMAPPING - -// The input can have the following mappings applied : -// -// PS_INPUTMAPPING_UNSIGNED_IDENTITY : y = max(0,x) = 1*max(0,x) + 0.0 -// PS_INPUTMAPPING_UNSIGNED_INVERT : y = 1 - max(0,x) = -1*max(0,x) + 1.0 -// PS_INPUTMAPPING_EXPAND_NORMAL : y = 2*max(0,x) - 1 = 2*max(0,x) - 1.0 -// PS_INPUTMAPPING_EXPAND_NEGATE : y = 1 - 2*max(0,x) = -2*max(0,x) + 1.0 -// PS_INPUTMAPPING_HALFBIAS_NORMAL : y = max(0,x) - 1/2 = 1*max(0,x) - 0.5 -// PS_INPUTMAPPING_HALFBIAS_NEGATE : y = 1/2 - max(0,x) = -1*max(0,x) + 0.5 -// PS_INPUTMAPPING_SIGNED_IDENTITY : y = x = 1* x + 0.0 -// PS_INPUTMAPPING_SIGNED_NEGATE : y = -x = -1* x + 0.0 -// -// (Note : I don't know for sure if the max() operation mentioned above is indeed what happens, -// as there's no further documentation available on this. Native Direct3D can clamp with the -// '_sat' instruction modifier, but that's not really the same as these Xbox1 input mappings.) -// -// When the input register is PS_ZERO, the above mappings result in the following constants: -// -// PS_REGISTER_NEGATIVE_ONE (PS_INPUTMAPPING_EXPAND_NORMAL on zero) : y = -1.0 -// PS_REGISTER_NEGATIVE_ONE_HALF (PS_INPUTMAPPING_HALFBIAS_NORMAL on zero) : y = -0.5 -// PS_REGISTER_ZERO itself : y = 0.0 -// PS_REGISTER_ONE_HALF (PS_INPUTMAPPING_HALFBIAS_NEGATE on zero) : y = 0.5 -// PS_REGISTER_ONE (PS_INPUTMAPPING_UNSIGNED_INVERT on zero) : y = 1.0 -// (Note : It has no define, but PS_INPUTMAPPING_EXPAND_NEGATE on zero results in ONE too!) - -enum PS_INPUTMAPPING -{ - PS_INPUTMAPPING_UNSIGNED_IDENTITY= 0x00L, // max(0,x) OK for final combiner: y = abs(x) - PS_INPUTMAPPING_UNSIGNED_INVERT= 0x20L, // 1 - max(0,x) OK for final combiner: y = 1 - x - PS_INPUTMAPPING_EXPAND_NORMAL= 0x40L, // 2*max(0,x) - 1 invalid for final combiner - PS_INPUTMAPPING_EXPAND_NEGATE= 0x60L, // 1 - 2*max(0,x) invalid for final combiner - PS_INPUTMAPPING_HALFBIAS_NORMAL= 0x80L, // max(0,x) - 1/2 invalid for final combiner - PS_INPUTMAPPING_HALFBIAS_NEGATE= 0xa0L, // 1/2 - max(0,x) invalid for final combiner - PS_INPUTMAPPING_SIGNED_IDENTITY= 0xc0L, // x invalid for final combiner - PS_INPUTMAPPING_SIGNED_NEGATE= 0xe0L, // -x invalid for final combiner -}; - -enum PS_REGISTER -{ - PS_REGISTER_ZERO= 0x00L, // r - PS_REGISTER_DISCARD= 0x00L, // w - PS_REGISTER_C0= 0x01L, // r - PS_REGISTER_C1= 0x02L, // r - PS_REGISTER_FOG= 0x03L, // r - PS_REGISTER_V0= 0x04L, // r/w - PS_REGISTER_V1= 0x05L, // r/w - PS_REGISTER_T0= 0x08L, // r/w - PS_REGISTER_T1= 0x09L, // r/w - PS_REGISTER_T2= 0x0aL, // r/w - PS_REGISTER_T3= 0x0bL, // r/w - PS_REGISTER_R0= 0x0cL, // r/w - PS_REGISTER_R1= 0x0dL, // r/w - PS_REGISTER_V1R0_SUM= 0x0eL, // r - PS_REGISTER_EF_PROD= 0x0fL, // r - - PS_REGISTER_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // 0x20 OK for final combiner - PS_REGISTER_NEGATIVE_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL, // 0x40 invalid for final combiner - PS_REGISTER_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NEGATE, // 0xa0 invalid for final combiner - PS_REGISTER_NEGATIVE_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NORMAL, // 0x80 invalid for final combiner - - PS_REGISTER_CXBX_PROD = PS_REGISTER_ZERO | PS_INPUTMAPPING_SIGNED_IDENTITY, // Cxbx internal use -}; - -// FOG ALPHA is only available in final combiner -// V1R0_SUM and EF_PROD are only available in final combiner (A,B,C,D inputs only) -// V1R0_SUM_ALPHA and EF_PROD_ALPHA are not available -// R0_ALPHA is initialized to T0_ALPHA in stage0 - -enum PS_CHANNEL -{ - PS_CHANNEL_RGB= 0x00, // used as RGB source - PS_CHANNEL_BLUE= 0x00, // used as ALPHA source - PS_CHANNEL_ALPHA= 0x10, // used as RGB or ALPHA source -}; - -constexpr DWORD PS_ChannelMask = (DWORD)PS_CHANNEL_ALPHA; -constexpr DWORD PS_NoChannelMask = (DWORD)(~PS_ChannelMask); -constexpr DWORD PS_AlphaChannelsMask = (DWORD)(PS_ChannelMask | (PS_ChannelMask << 8) | (PS_ChannelMask << 16) | (PS_ChannelMask << 24)); -constexpr DWORD PS_NoChannelsMask = (DWORD)(~PS_AlphaChannelsMask); - -enum PS_FINALCOMBINERSETTING -{ - PS_FINALCOMBINERSETTING_CLAMP_SUM= 0x80, // V1+R0 sum clamped to [0,1] - PS_FINALCOMBINERSETTING_COMPLEMENT_V1= 0x40, // unsigned invert mapping (1 - v1) is used as an input to the sum rather than v1 - PS_FINALCOMBINERSETTING_COMPLEMENT_R0= 0x20, // unsigned invert mapping (1 - r0) is used as an input to the sum rather than r0 -}; - -// ========================================================================================================= -// PSRGBOutputs[0-7] -// PSAlphaOutputs[0-7] -// --------.--------.--------.----xxxx // CD register -// --------.--------.--------.xxxx---- // AB register -// --------.--------.----xxxx.-------- // SUM register -// --------.--------.---x----.-------- // CD output (0= multiply, 1= dot product) -// --------.--------.--x-----.-------- // AB output (0= multiply, 1= dot product) -// --------.--------.-x------.-------- // AB_CD mux/sum select (0= sum, 1= mux) -// --------.------xx.x-------.-------- // Output mapping -// --------.-----x--.--------.-------- // CD blue to alpha -// --------.----x---.--------.-------- // AB blue to alpha - -#define PS_COMBINEROUTPUTS(ab,cd,mux_sum,flags) (((flags)<<12)|((mux_sum)<<8)|((ab)<<4)|(cd)) -// ab,cd,mux_sum contain a value from PS_REGISTER -// flags contains values from PS_COMBINEROUTPUT - -enum PS_COMBINEROUTPUT -{ - PS_COMBINEROUTPUT_IDENTITY= 0x00L, // y = x - PS_COMBINEROUTPUT_BIAS= 0x08L, // y = x - 0.5 - PS_COMBINEROUTPUT_SHIFTLEFT_1= 0x10L, // y = x*2 - PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS= 0x18L, // y = (x - 0.5)*2 - PS_COMBINEROUTPUT_SHIFTLEFT_2= 0x20L, // y = x*4 - // PS_COMBINEROUTPUT_SHIFTLEFT_2_BIAS= 0x28L, // y = (x - 0.5)*4 - PS_COMBINEROUTPUT_SHIFTRIGHT_1= 0x30L, // y = x/2 - // PS_COMBINEROUTPUT_SHIFTRIGHT_1_BIAS= 0x38L, // y = (x - 0.5)/2 - - PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA= 0x80L, // RGB only - - PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA= 0x40L, // RGB only - - PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L, - PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only - - PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L, - PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only - - PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD - PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a -}; - -// AB_CD register output must be DISCARD if either AB_DOT_PRODUCT or CD_DOT_PRODUCT are set - -// ========================================================================================================= -// PSC0Mapping -// PSC1Mapping -// --------.--------.--------.----xxxx // offset of D3D constant for stage 0 -// --------.--------.--------.xxxx---- // offset of D3D constant for stage 1 -// --------.--------.----xxxx.-------- // offset of D3D constant for stage 2 -// --------.--------.xxxx----.-------- // offset of D3D constant for stage 3 -// --------.----xxxx.--------.-------- // offset of D3D constant for stage 4 -// --------.xxxx----.--------.-------- // offset of D3D constant for stage 5 -// ----xxxx.--------.--------.-------- // offset of D3D constant for stage 6 -// xxxx----.--------.--------.-------- // offset of D3D constant for stage 7 - -#define PS_CONSTANTMAPPING(s0,s1,s2,s3,s4,s5,s6,s7) \ - (((DWORD)(s0)&0xf)<< 0) | (((DWORD)(s1)&0xf)<< 4) | \ - (((DWORD)(s2)&0xf)<< 8) | (((DWORD)(s3)&0xf)<<12) | \ - (((DWORD)(s4)&0xf)<<16) | (((DWORD)(s5)&0xf)<<20) | \ - (((DWORD)(s6)&0xf)<<24) | (((DWORD)(s7)&0xf)<<28) -// s0-s7 contain the offset of the D3D constant that corresponds to the -// c0 or c1 constant in stages 0 through 7. These mappings are only used in -// SetPixelShaderConstant(). - -// ========================================================================================================= -// PSFinalCombinerConstants -// --------.--------.--------.----xxxx // offset of D3D constant for C0 -// --------.--------.--------.xxxx---- // offset of D3D constant for C1 -// --------.--------.-------x.-------- // Adjust texture flag - -#define PS_FINALCOMBINERCONSTANTS(c0,c1,flags) (((DWORD)(flags) << 8) | ((DWORD)(c0)&0xf)<< 0) | (((DWORD)(c1)&0xf)<< 4) -// c0 and c1 contain the offset of the D3D constant that corresponds to the -// constants in the final combiner. These mappings are only used in -// SetPixelShaderConstant(). Flags contains values from PS_GLOBALFLAGS - -enum PS_GLOBALFLAGS -{ - // if this flag is set, the texture mode for each texture stage is adjusted as follows: - // if set texture is a cubemap, - // change PS_TEXTUREMODES_PROJECT2D to PS_TEXTUREMODES_CUBEMAP - // change PS_TEXTUREMODES_PROJECT3D to PS_TEXTUREMODES_CUBEMAP - // change PS_TEXTUREMODES_DOT_STR_3D to PS_TEXTUREMODES_DOT_STR_CUBE - // if set texture is a volume texture, - // change PS_TEXTUREMODES_PROJECT2D to PS_TEXTUREMODES_PROJECT3D - // change PS_TEXTUREMODES_CUBEMAP to PS_TEXTUREMODES_PROJECT3D - // change PS_TEXTUREMODES_DOT_STR_CUBE to PS_TEXTUREMODES_DOT_STR_3D - // if set texture is neither cubemap or volume texture, - // change PS_TEXTUREMODES_PROJECT3D to PS_TEXTUREMODES_PROJECT2D - // change PS_TEXTUREMODES_CUBEMAP to PS_TEXTUREMODES_PROJECT2D - - PS_GLOBALFLAGS_NO_TEXMODE_ADJUST= 0x0000L, // don"t adjust texture modes - PS_GLOBALFLAGS_TEXMODE_ADJUST= 0x0001L, // adjust texture modes according to set texture -}; - -enum PSH_OPCODE -{ - PO_COMMENT, - PO_PS, - PO_DEF, - PO_DCL, // Note : ps.2.0 and up only - PO_DCL_2D, // Note : ps.2.0 and up only - PO_DCL_CUBE, // Note : ps.2.0 and up only - PO_DCL_VOLUME, // Note : ps.2.0 and up only - PO_TEX, - PO_TEXLD, // Note : ps.1.4 only - PO_TEXLD2, // Note : ps.2.0 and up only - PO_TEXBEM, - PO_TEXBEML, - PO_TEXBRDF, // Xbox ext. - PO_TEXCOORD, - PO_TEXCRD, // Note: ps.1.4 only - PO_TEXKILL, - PO_TEXREG2AR, - PO_TEXREG2GB, - PO_TEXDP3, // Note : ps.1.3 only - PO_TEXDP3TEX, // Note : ps.1.3 only - PO_TEXM3X2TEX, - PO_TEXM3X2DEPTH, // Note : requires ps.1.3 and a preceding texm3x2pad - PO_TEXM3X3DIFF, // Xbox ext. - PO_TEXM3X3VSPEC, - PO_TEXM3X3TEX, // Note : Uses a cube texture - PO_TEXM3X2PAD, // Note : Must be combined with texm3x2tex or texm3x2depth - PO_TEXM3X3PAD, - PO_TEXM3X3SPEC, // NOTE : NEEDS 3 ARGUMENTS! - // Direct3D8 arithmetic instructions : - PO_ADD, - PO_CMP, - PO_CND, - PO_DP3, // dp3 d, s1,s2 : d=s0 dot s1 (replicated to all channels, .rgb=color only, .a=color+alpha) - PO_DP4, // dp3 d, s1,s2 : d.r=d.g=d.b=d.a=(s1.r*s2.r)+(s1.g*s2.g)+(s1.b*s2.b)+(s1.a*s2.a) - PO_LRP, - PO_MAD, - PO_MOV, - PO_MUL, - PO_NOP, - PO_SUB, - PO_RCP, // Note: ps.2.0 and up only - // Xbox1 opcodes : - PO_XMMA, - PO_XMMC, - PO_XDM, - PO_XDD, - PO_XFC, - PO_XPS, -}; - -const struct { char *mn; int _Out; int _In; char *note; } PSH_OPCODE_DEFS[/*PSH_OPCODE*/] = { - // Pixel shader header opcodes (must be specified in this order) : - {/* PO_COMMENT */ /*mn:*/";", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, // - {/* PO_PS */ /*mn:*/"ps", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, // Must occur once - {/* PO_DEF */ /*mn:*/"def", /*_Out: */ 1, /*_In: */ 4, /*note:*/"" }, // Output must be a PARAM_C, arguments must be 4 floats [0.00f .. 1.00f] - {/* PO_DCL */ /*mn:*/"dcl", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only - {/* PO_DCL_2D */ /*mn:*/"dcl_2d", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only - {/* PO_DCL_CUBE */ /*mn:*/"dcl_cube", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only - {/* PO_DCL_VOLUME */ /*mn:*/"dcl_volume", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only - {/* PO_TEX */ /*mn:*/"tex", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, - {/* PO_TEXLD */ /*mn:*/"texld", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // Note : ps.1.4 and up only - {/* PO_TEXLD2 */ /*mn:*/"texld", /*_Out: */ 1, /*_In: */ 2, /*note:*/"" }, // Note : ps.1.4 and up only - {/* PO_TEXBEM */ /*mn:*/"texbem", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXBEML */ /*mn:*/"texbeml", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXBRDF */ /*mn:*/"texbrdf", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ Not supported by Direct3D8 ? - {/* PO_TEXCOORD */ /*mn:*/"texcoord", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, - {/* PO_TEXCRD */ /*mn:*/"texcrd", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // Note: ps.1.4 only - {/* PO_TEXKILL */ /*mn:*/"texkill", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, - {/* PO_TEXDP3 */ /*mn:*/"texdp3", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXDP3TEX */ /*mn:*/"texdp3tex", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXM3X2TEX */ /*mn:*/"texm3x2tex", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXM3X2DEPTH */ /*mn:*/"texm3x2depth", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ requires ps.1.3 and a preceding texm3x2pad - {/* PO_TEXM3X3DIFF */ /*mn:*/"texm3x3diff", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ Not supported by Direct3D8 ? - {/* PO_TEXM3X3VSPEC */ /*mn:*/"texm3x3vspec", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXM3X3TEX */ /*mn:*/"texm3x3tex", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ Uses a cube texture - {/* PO_TEXREG2AR */ /*mn:*/"texreg2ar", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXREG2GB */ /*mn:*/"texreg2gb", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXM3X2PAD */ /*mn:*/"texm3x2pad", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXM3X3PAD */ /*mn:*/"texm3x3pad", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, - {/* PO_TEXM3X3SPEC */ /*mn:*/"texm3x3spec", /*_Out: */ 1, /*_In: */ 2, /*note:*/"" }, - // Arithmetic opcodes : - {/* PO_ADD */ /*mn:*/"add", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0+s1" }, - {/* PO_CMP */ /*mn:*/"cmp", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0={s0>=0?s1:s2}" }, - {/* PO_CND */ /*mn:*/"cnd", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0={s0.a>0.5?s1:s2}" }, // 1st input must be "r0.a" - {/* PO_DP3 */ /*mn:*/"dp3", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0 dot3 s1" }, - {/* PO_DP4 */ /*mn:*/"dp4", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0 dot4 s1" }, - {/* PO_LRP */ /*mn:*/"lrp", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0=s0*s1+{1-s0}*s2=s0*{s1-s2}+s2" }, - {/* PO_MAD */ /*mn:*/"mad", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0=s0*s1+s2" }, - {/* PO_MOV */ /*mn:*/"mov", /*_Out: */ 1, /*_In: */ 1, /*note:*/"d0=s0" }, - {/* PO_MUL */ /*mn:*/"mul", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0*s1" }, - {/* PO_NOP */ /*mn:*/"nop", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, - {/* PO_SUB */ /*mn:*/"sub", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0-s1" }, - {/* PO_RCP */ /*mn:*/"rcp", /*_Out: */ 1, /*_In: */ 1, /*note:*/"d0=1/s0" }, // Note: ps.2.0 and up only - // Xbox-only {NV2A} opcodes : - {/* PO_XMMA */ /*mn:*/"xmma", /*_Out: */ 3, /*_In: */ 4, /*note:*/"d0=s0*s1, d1=s2*s3, d2={s0*s1}+{s2*s3}" }, - {/* PO_XMMC */ /*mn:*/"xmmc", /*_Out: */ 3, /*_In: */ 4, /*note:*/"d0=s0*s1, d1=s2*s3, d2={r0.a>0.5}?{s0*s1}:{s2*s3}" }, - {/* PO_XDM */ /*mn:*/"xdm", /*_Out: */ 2, /*_In: */ 4, /*note:*/"d0=s0 dot s1, d1=s2*s3" }, - {/* PO_XDD */ /*mn:*/"xdd", /*_Out: */ 2, /*_In: */ 4, /*note:*/"d0=s0 dot s1, d1=s2 dot s3" }, - {/* PO_XFC */ /*mn:*/"xfc", /*_Out: */ 0, /*_In: */ 7, /*note:*/"r0.rgb=s0*s1+{1-s0}*s2+s3, r0.a=s6.a, prod=s4*s5, sum=r0+v1" }, - {/* PO_XPS */ /*mn:*/"xps", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, // Must occur once -}; - -enum PSH_ARGUMENT_TYPE -{ - PARAM_VALUE, // Xbox only; Numberic constants used in Xbox-only opcodes - PARAM_DISCARD, // Xbox only; - PARAM_FOG, // Final combiner only; Read-only register fog register - PARAM_V1R0_SUM, // Final combiner only; Read-only register that contains the result of V1+R0 - PARAM_EF_PROD, // Final combiner only; Read-only register that contains the result of final combiner parameters E * F - PARAM_oDepth, // Output depth register - PARAM_R, // Temporary registers (unassigned except r0.a, which on NV2A is initially set to t0.a) - PARAM_T, // Textures - PARAM_V, // Vertex colors - PARAM_C, // Constant registers, set by def opcodes or SetPixelShaderConstant - PARAM_S, // Sampler registers - PARAM_oC, // Output color registers -}; - -const char *PSH_ARGUMENT_TYPE_Str[/*PSH_ARGUMENT_TYPE*/] = { -// Prefix # r/w Input? Output? Note - "", // * r No No Used for numeric constants like -1, 0, 1 - "discard", // * w No Yes Only for xbox opcodes (native opcodes have single output - discards must be removed) - "fog", // 1 r Yes No Only for final combiner parameter - "sum", // 1 r Yes No Only for final combiner parameter - "prod", // 1 r Yes No Only for final combiner parameter - "oDepth", // - "r", // 2 r/w Yes Yes We fake a few extra registers and resolve them in FixupPixelShader - "t", // 4 r/w Yes Yes D3D9 cannot write to these! - "v", // 2 r Yes Yes - "c", // 16 r Yes No Xbox has 8*c0,c1=16, while PC D3D8 has only 8, we try to reduce that in FixupPixelShader - "s", // 16 - No Yes - "oC", // -}; - -constexpr int XFC_COMBINERSTAGENR = XTL::X_PSH_COMBINECOUNT; // Always call XFC 'stage 9', 1 after the 8th combiner - -constexpr int PSH_XBOX_MAX_C_REGISTER_COUNT = 16; -constexpr int PSH_XBOX_MAX_R_REGISTER_COUNT = 2; -constexpr int PSH_XBOX_MAX_T_REGISTER_COUNT = 4; -constexpr int PSH_XBOX_MAX_V_REGISTER_COUNT = 2; -// Extra constants to support features not present in Native D3D : -constexpr int PSH_XBOX_CONSTANT_FOG = PSH_XBOX_MAX_C_REGISTER_COUNT; // = 16 -constexpr int PSH_XBOX_CONSTANT_FC0 = PSH_XBOX_CONSTANT_FOG + 1; // = 17 -constexpr int PSH_XBOX_CONSTANT_FC1 = PSH_XBOX_CONSTANT_FC0 + 1; // = 18 -constexpr int PSH_XBOX_CONSTANT_MUL0 = PSH_XBOX_CONSTANT_FC1 + 1; // = 19 -constexpr int PSH_XBOX_CONSTANT_MUL1 = PSH_XBOX_CONSTANT_MUL0 + 1; // = 20 -constexpr int PSH_XBOX_CONSTANT_BEM = PSH_XBOX_CONSTANT_MUL1 + 1; // = 21 -constexpr int PSH_XBOX_CONSTANT_LUM = PSH_XBOX_CONSTANT_BEM + 4; // = 25 -constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_LUM + 4; // = 29 - -constexpr int FakeRegNr_Sum = PSH_XBOX_MAX_T_REGISTER_COUNT + 0; -constexpr int FakeRegNr_Prod = PSH_XBOX_MAX_T_REGISTER_COUNT + 1; -constexpr int FakeRegNr_Xmm1 = PSH_XBOX_MAX_T_REGISTER_COUNT + 2; -constexpr int FakeRegNr_Xmm2 = PSH_XBOX_MAX_T_REGISTER_COUNT + 3; - -enum PSH_INST_MODIFIER { - INSMOD_NONE, // y = x - INSMOD_BIAS, // y = x - 0.5 // Xbox only : TODO : Fixup occurrances! - INSMOD_X2, // y = x * 2 - INSMOD_BX2, // y = (x - 0.5) * 2 // Xbox only : TODO : Fixup occurrances! - INSMOD_X4, // y = x * 4 - INSMOD_D2, // y = x * 0.5 - INSMOD_SAT, // Xbox doesn"t support this, but has ARGMOD_SATURATE instead - INSMOD_X8, // y = x * 8 // ps 1.4 only - INSMOD_D4, // y = x * 0.25 // ps 1.4 only - INSMOD_D8, // y = x * 0.125 // ps 1.4 only -}; - -const char *PSH_INST_MODIFIER_Str[/*PSH_INST_MODIFIER*/] = { - "", - "_bias", - "_x2", - "_bx2", - "_x4", - "_d2", - "_sat", - "_x8", - "_d4", - "_d8", -}; - -// Four argument modifiers (applied in this order) : -// 1: Inversion (invert or negate : "1-" or "-") -// 2: Apply bias ("_bias") -// 3: Apply scale ("_x2", "_bx2", "_x4", or "_d2") -// 4: Apply clamp ("_sat") -enum PSH_ARG_MODIFIER { - ARGMOD_IDENTITY, // y = x - - ARGMOD_INVERT, // y = 1-x -> 0..1 > 1..0 - ARGMOD_NEGATE, // y = -x -> 0..1 > 0..-1 - - ARGMOD_BIAS, // y = x-0.5 -> 0..1 > -0.5..0.5 - - ARGMOD_SCALE_X2, // y = x*2 -> 0..1 > 0..2 - ARGMOD_SCALE_BX2, // y = (x*2)-1 -> 0..1 > -1..1 - ARGMOD_SCALE_X4, // y = x*4 -> 0..1 > 0..4 - ARGMOD_SCALE_D2, // y = x/2 -> 0..1 > 0..0.5 - - ARGMOD_SATURATE, // Xbox - not available in PS1.3 (can be done on output instead) - - ARGMOD_ALPHA_REPLICATE, - ARGMOD_BLUE_REPLICATE // PS1.1-PS1.3 only allow this if destination writemask = .a -}; - -typedef DWORD PSH_ARG_MODIFIERs; // = set of PSH_ARG_MODIFIER; - -const char *PSH_ARG_MODIFIER_Str[/*PSH_ARG_MODIFIER*/] = { - "%s", - - "1-%s", - "-%s", - - "%s_bias", - - "%s_x2", - "%s_bx2", - "%s_x4", - "%s_d2", - - "%s_sat", - - "%s", // .a is added via Mask - "%s" // .b idem -}; - -struct RPSRegisterObject { - bool IsAlpha; - PS_REGISTER Reg; - - void Decode(uint8_t Value, bool aIsAlpha); - std::string DecodedToString(); -}; - -struct RPSInputRegister : RPSRegisterObject { - PS_CHANNEL Channel; - PS_INPUTMAPPING InputMapping; - - void Decode(uint8_t Value, bool aIsAlpha); - std::string DecodedToString(); -}; - -struct RPSCombinerOutput : RPSRegisterObject { - RPSInputRegister Input1; // Called InputA or InputC (depending if it's inside the AB or CD combiner) - RPSInputRegister Input2; // Called InputC or InputD (depending if it's inside the AB or CD combiner) - bool DotProduct; // False=Multiply, True=DotProduct - bool BlueToAlpha; // False=Alpha-to-Alpha, True=Blue-to-Alpha - - void Decode(uint8_t Value, DWORD PSInputs, bool aIsAlpha); -}; - -struct RPSCombinerOutputMuxSum : RPSRegisterObject { - RPSCombinerOutput OutputAB; // Contains InputA and InputB (as Input1 and Input2) - RPSCombinerOutput OutputCD; // Contains InputC and InputD (as Input1 and Input2) -}; - -struct RPSCombinerStageChannel { - RPSCombinerOutputMuxSum OutputSUM; // Contains OutputAB, OutputCD - PS_COMBINEROUTPUT CombinerOutputFlags; - bool AB_CD_SUM; // True=AB+CD, False=MUX(AB;CD) based on R0.a - - void Decode(DWORD PSInputs, DWORD PSOutputs, bool aIsAlpha = false); -}; - -struct RPSCombinerStage { - RPSCombinerStageChannel RGB; - RPSCombinerStageChannel Alpha; -}; - -struct RPSFinalCombiner { - RPSInputRegister InputA; - RPSInputRegister InputB; - RPSInputRegister InputC; - RPSInputRegister InputD; - RPSInputRegister InputE; - RPSInputRegister InputF; - RPSInputRegister InputG; - - PS_FINALCOMBINERSETTING FinalCombinerFlags; - - uint8_t FinalCombinerC0Mapping; - uint8_t FinalCombinerC1Mapping; - - DWORD dwPS_GLOBALFLAGS; - - void Decode(const DWORD PSFinalCombinerInputsABCD, const DWORD PSFinalCombinerInputsEFG, const DWORD PSFinalCombinerConstants); -}; - -constexpr DWORD MASK_NONE = 0x000; -constexpr DWORD MASK_R = 0x001; -constexpr DWORD MASK_G = 0x002; -constexpr DWORD MASK_B = 0x004; -constexpr DWORD MASK_A = 0x008; -constexpr DWORD MASK_RGB = MASK_R | MASK_G | MASK_B; -constexpr DWORD MASK_RGBA = MASK_R | MASK_G | MASK_B | MASK_A; - -enum - TArgumentType { - atInput, atOutput, atFinalCombiner -}; - -typedef struct _PSH_RECOMPILED_SHADER { - XTL::X_D3DPIXELSHADERDEF PSDef; - std::string NewShaderStr; - DWORD ConvertedHandle; - bool ConstInUse[PSH_XBOX_CONSTANT_MAX]; - int ConstMapping[PSH_XBOX_CONSTANT_MAX]; -} PSH_RECOMPILED_SHADER, -*PPSH_RECOMPILED_SHADER; - -typedef struct _PSH_IMD_ARGUMENT { - PSH_ARGUMENT_TYPE Type; // For parameters: R, T, V or C For output : Discard, R, T or V - int16_t Address; // Register address - DWORD Mask; - PSH_ARG_MODIFIERs Modifiers; - float Multiplier; - - void SetConstValue(float Value); - float GetConstValue(); - bool UsesRegister(); - bool IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); // overload; - bool IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask); // overload; - void SetRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask); - bool HasModifier(PSH_ARG_MODIFIER modifier); - bool SetScaleConstRegister(float factor, const PSH_RECOMPILED_SHADER& pRecompiled); - bool SetScaleBemLumRegister(D3DTEXTURESTAGESTATETYPE factor, int stage, const PSH_RECOMPILED_SHADER& pRecompiled); - std::string ToString(); - bool Decode(const DWORD Value, DWORD aMask, TArgumentType ArgumentType); - void Invert(); - void Negate(); -} PSH_IMD_ARGUMENT, -*PPSH_IMD_ARGUMENT; - -//TPSH_IMD_ARGUMENTArray = array[0..(MaxInt div SizeOf(PSH_IMD_ARGUMENT)) - 1] of PSH_IMD_ARGUMENT; -//PPSH_IMD_ARGUMENTs = ^TPSH_IMD_ARGUMENTArray; - -typedef struct _PSH_INTERMEDIATE_FORMAT { - int CombinerStageNr; - bool IsCombined; - PSH_OPCODE Opcode; - std::string CommentString; - PSH_INST_MODIFIER Modifier; - PSH_IMD_ARGUMENT Output[3]; // 3 = xmm* output count - PSH_IMD_ARGUMENT Parameters[7]; // 7 = xfc parameter count - - _PSH_INTERMEDIATE_FORMAT *Initialize(const PSH_OPCODE aOpcode); - std::string ToString(); - bool IsArithmetic(); - void ScaleOutput(float aFactor); - bool ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress); // overload; - bool ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask); // overload; - bool ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, int& addressCount, int& total); // overload; - bool WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress); // overload; - bool WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask); // overload; - void SwapParameter(const int Index1, const int Index2); - void XSwapOutput(); - bool MoveRemovableParametersRight(const int Index1, const int Index2); - bool XMoveNonRegisterOutputsRight(); - void XCopySecondOpcodeToFirst(const PSH_OPCODE aOpcode); - bool Decode(DWORD aCombinerStageNr, DWORD PSInputs, DWORD PSOutputs, DWORD aMask); - bool DecodeFinalCombiner(DWORD aPSFinalCombinerInputsABCD, DWORD aPSFinalCombinerInputsEFG); -} PSH_INTERMEDIATE_FORMAT, -*PPSH_INTERMEDIATE_FORMAT; - -struct PSH_XBOX_SHADER { - uint32_t m_PSVersion; // see D3DPS_VERSION - https://msdn.microsoft.com/en-us/library/windows/desktop/bb172592(v=vs.85).aspx - int MaxConstantFloatRegisters; - int MaxTemporaryRegisters; - int MaxSamplerRegisters; // Sampler (Direct3D 9 asm-ps) - int MaxTextureCoordinateRegisters; - int MaxInputColorRegisters; - int PSH_PC_MAX_REGISTER_COUNT; - - // Reserve enough slots for all shaders, so we need space for 2 constants, 5 lines per texture addressing codes and 10 lines per opcode : : - PSH_INTERMEDIATE_FORMAT Intermediate[2 + (XTL::X_D3DTS_STAGECOUNT * 5) + (XTL::X_PSH_COMBINECOUNT * 10) + 1]; - int IntermediateCount; - - PS_TEXTUREMODES PSTextureModes[XTL::X_D3DTS_STAGECOUNT]; - PS_DOTMAPPING PSDotMapping[XTL::X_D3DTS_STAGECOUNT]; - DWORD PSCompareMode[XTL::X_D3DTS_STAGECOUNT]; - int PSInputTexture[XTL::X_D3DTS_STAGECOUNT]; - - PS_FINALCOMBINERSETTING FinalCombinerFlags; - // Note : The following constants are only needed for PSH_XBOX_SHADER::DecodedToString, - // they are not involved in the actual pixel shader recompilation anymore : - RPSFinalCombiner FinalCombiner; - RPSCombinerStage Combiners[XTL::X_PSH_COMBINECOUNT]; - int NumberOfCombiners; - DWORD CombinerCountFlags; // For PS_COMBINERCOUNTFLAGS - // Read from CombinerCountFlags : - bool CombinerMuxesOnMsb; - bool CombinerHasUniqueC0; - bool CombinerHasUniqueC1; - - int StartPos; - - PSH_RECOMPILED_SHADER Recompiled = {}; - - void SetPSVersion(const uint32_t PSVersion); - - std::string ToString(); - void Log(const char *PhaseStr); - PPSH_INTERMEDIATE_FORMAT NewIntermediate(); - void InsertIntermediate(PPSH_INTERMEDIATE_FORMAT pIntermediate, int Index); - void DeleteIntermediate(int Index); - void DeleteLastIntermediate(); - std::string static OriginalToString(XTL::X_D3DPIXELSHADERDEF *pPSDef); - void Decode(XTL::X_D3DPIXELSHADERDEF *pPSDef); - PSH_RECOMPILED_SHADER Convert(XTL::X_D3DPIXELSHADERDEF *pPSDef); - std::string DecodedToString(XTL::X_D3DPIXELSHADERDEF *pPSDef); - bool _NextIs2D(int Stage); - bool DecodeTextureModes(XTL::X_D3DPIXELSHADERDEF *pPSDef); - int GetTextureStageModifiers(int Stage); - void InsertTex3x2Instructions(int Stage, int inputStage, std::vector& InsertIns); - void InsertTex3x3Instructions(int Stage, int inputStage, std::vector& InsertIns); - bool InsertTextureModeInstruction(XTL::X_D3DPIXELSHADERDEF *pPSDef, int Stage, PSH_OPCODE opcode, std::vector& InsertIns, int& InsertPos); - bool MoveRemovableParametersRight(); - void ConvertXboxOpcodesToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef); - void _SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLOR ConstColor); - void _SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLORVALUE ConstColor); - int _MapConstant(int ConstNr, bool *NativeConstInUse); - int _HandleConst(int XboxConst, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled, bool *NativeConstInUse, bool *EmittedNewConstant); - bool ConvertConstantsToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled); - bool RemoveUselessWrites(); - int MaxRegisterCount(PSH_ARGUMENT_TYPE aRegType); - bool IsValidNativeOutputRegister(PSH_ARGUMENT_TYPE aRegType, int index = -1); - int RegisterIsFreeFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); - int RegisterIsUsedFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); - int NextFreeRegisterFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int bIndex = -1, int startAddress = 0, int excludeAddress = -1); - bool IsRegisterFreeFromIndexOnwards(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); - void ReplaceInputRegisterFromIndexOnwards(int aIndex, - PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, - PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex = -1); - void ReplaceOutputRegisterFromIndexOnwards(int aIndex, - PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, - PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex = -1); - void ReplaceRegisterFromIndexOnwards(int aIndex, - PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, - PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex = -1, bool replaceInput = true, bool replaceOutput = true); - bool ConvertXMMToNative_Except3RdOutput(int i); - void ConvertXPSToNative(int i); - void ConvertXMMAToNative(int i); - void ConvertXMMCToNative(int i); - void ConvertXDMToNative(int i); - void ConvertXDDToNative(int i); - void ConvertXFCToNative(int i); - bool FixArgumentModifiers(); - bool CombineInstructions(); - bool RemoveNops(); - bool SimplifyMOV(PPSH_INTERMEDIATE_FORMAT Cur); - bool SimplifyADD(PPSH_INTERMEDIATE_FORMAT Cur); - bool SimplifyMAD(PPSH_INTERMEDIATE_FORMAT Cur, int index); - bool SimplifySUB(PPSH_INTERMEDIATE_FORMAT Cur); - bool SimplifyMUL(PPSH_INTERMEDIATE_FORMAT Cur); - bool SimplifyLRP(PPSH_INTERMEDIATE_FORMAT Cur, int index); - bool FixupCND(PPSH_INTERMEDIATE_FORMAT Cur, int index); - bool FixupPixelShader(); - bool FixInvalidSrcSwizzle(); - bool FixMissingR0a(); - bool FixMissingR1a(); - bool FixCoIssuedOpcodes(); - bool FixInvalidDstRegister(); - bool FixConstantParameters(); - bool FixInstructionModifiers(); - bool FixUninitializedReads(); - bool FixOverusedRegisters(); - bool FinalizeShader(); - - static void GetPSTextureModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_TEXTUREMODES psTextureModes[XTL::X_D3DTS_STAGECOUNT]); - static void GetPSDotMapping(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_DOTMAPPING psDotMapping[XTL::X_D3DTS_STAGECOUNT]); - static void GetPSCompareModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, DWORD psCompareModes[XTL::X_D3DTS_STAGECOUNT]); - static void GetPSInputTexture(XTL::X_D3DPIXELSHADERDEF* pPSDef, int psInputTexture[XTL::X_D3DTS_STAGECOUNT]); -}; - -/* -* Blueshogun's code (useful for debugging the PixelShader binary format) -*/ - -// PS Texture Modes -char* PS_TextureModesStr[/*PS_TEXTUREMODES*/] = -{ - "PS_TEXTUREMODES_NONE", // 0x00 - "PS_TEXTUREMODES_PROJECT2D", // 0x01 - "PS_TEXTUREMODES_PROJECT3D", // 0x02 - "PS_TEXTUREMODES_CUBEMAP", // 0x03 - "PS_TEXTUREMODES_PASSTHRU", // 0x04 - "PS_TEXTUREMODES_CLIPPLANE", // 0x05 - "PS_TEXTUREMODES_BUMPENVMAP", // 0x06 - "PS_TEXTUREMODES_BUMPENVMAP_LUM", // 0x07 - "PS_TEXTUREMODES_BRDF", // 0x08 - "PS_TEXTUREMODES_DOT_ST", // 0x09 - "PS_TEXTUREMODES_DOT_ZW", // 0x0A - "PS_TEXTUREMODES_DOT_RFLCT_DIFF", // 0x0B - "PS_TEXTUREMODES_DOT_RFLCT_SPEC", // 0x0C - "PS_TEXTUREMODES_DOT_STR_3D", // 0x0D - "PS_TEXTUREMODES_DOT_STR_CUBE", // 0x0E - "PS_TEXTUREMODES_DPNDNT_AR", // 0x0F - "PS_TEXTUREMODES_DPNDNT_GB", // 0x10 - "PS_TEXTUREMODES_DOTPRODUCT", // 0x11 - "PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST", // 0x12 - "???", // 0x13 - "???", // 0x14 - "???", // 0x15 - "???", // 0x16 - "???", // 0x17 - "???", // 0x18 - "???", // 0x19 - "???", // 0x1A - "???", // 0x1B - "???", // 0x1C - "???", // 0x1D - "???", // 0x1E - "???", // 0x1F -}; - -// PS DotMapping -char* PS_DotMappingStr[/*PS_DOTMAPPING*/] = -{ - "PS_DOTMAPPING_ZERO_TO_ONE", // 0x00 - "PS_DOTMAPPING_MINUS1_TO_1_D3D", // 0x01 - "PS_DOTMAPPING_MINUS1_TO_1_GL", // 0x02 - "PS_DOTMAPPING_MINUS1_TO_1", // 0x03 - "PS_DOTMAPPING_HILO_1", // 0x04 - "???", // 0x05 - "???", // 0x06 - "PS_DOTMAPPING_HILO_HEMISPHERE", // 0x07 -}; - -#if 1 // array unusable for bitflags -// PS CompareMode -char* PS_CompareModeStr[/*PS_COMPAREMODE*/] = -{ - "PS_COMPAREMODE_S_LT", // 0x00L - "PS_COMPAREMODE_S_GE", // 0x01L - - "PS_COMPAREMODE_T_LT", // 0x00L - "PS_COMPAREMODE_T_GE", // 0x02L - - "???", - - "PS_COMPAREMODE_R_LT", // 0x00L - "PS_COMPAREMODE_R_GE", // 0x04L - - "???", - "???", - "???", - - "PS_COMPAREMODE_Q_LT", // 0x00L - "PS_COMPAREMODE_Q_GE", // 0x08L -}; -#endif - -#if 1 // array unfit for bitflags -// PS CombinerCountFlags -char* PS_CombinerCountFlagsStr[/*PS_COMBINERCOUNTFLAGS*/] = -{ - "PS_COMBINERCOUNT_MUX_LSB", // 0x0000L, // mux on r0.a lsb - "PS_COMBINERCOUNT_MUX_MSB", // 0x0001L, // mux on r0.a msb - - "PS_COMBINERCOUNT_SAME_C0", // 0x0000L, // c0 same in each stage - "PS_COMBINERCOUNT_UNIQUE_C0", // 0x0010L, // c0 unique in each stage - - "PS_COMBINERCOUNT_SAME_C1", // 0x0000L, // c1 same in each stage - "PS_COMBINERCOUNT_UNIQUE_C1", // 0x0100L // c1 unique in each stage -}; -#endif - -// PS InputMapping -std::string PS_InputMappingStr[/*PS_INPUTMAPPING*/] = -{ - "PS_INPUTMAPPING_UNSIGNED_IDENTITY", // 0x00L, // max(0,x) OK for final combiner: y = abs(x) - "PS_INPUTMAPPING_UNSIGNED_INVERT", // 0x20L, // 1 - max(0,x) OK for final combiner: y = 1 - x - "PS_INPUTMAPPING_EXPAND_NORMAL", // 0x40L, // 2*max(0,x) - 1 invalid for final combiner - "PS_INPUTMAPPING_EXPAND_NEGATE", // 0x60L, // 1 - 2*max(0,x) invalid for final combiner - "PS_INPUTMAPPING_HALFBIAS_NORMAL", // 0x80L, // max(0,x) - 1/2 invalid for final combiner - "PS_INPUTMAPPING_HALFBIAS_NEGATE", // 0xa0L, // 1/2 - max(0,x) invalid for final combiner - "PS_INPUTMAPPING_SIGNED_IDENTITY", // 0xc0L, // x invalid for final combiner - "PS_INPUTMAPPING_SIGNED_NEGATE", // 0xe0L, // -x invalid for final combiner -}; - -// PS Register (note, a few have one space, to line up the output a little) -std::string PS_RegisterStr[/*PS_REGISTER*/] = -{ - "PS_REGISTER_ZERO", // 0x00L, // r - "PS_REGISTER_DISCARD", // 0x00L, // w - "PS_REGISTER_C0 ", // 0x01L, // r - "PS_REGISTER_C1 ", // 0x02L, // r - "PS_REGISTER_FOG", // 0x03L, // r - "PS_REGISTER_V0 ", // 0x04L, // r/w - "PS_REGISTER_V1 ", // 0x05L, // r/w - "??", // 0x06 - "??", // 0x07 - "PS_REGISTER_T0 ", // 0x08L, // r/w - "PS_REGISTER_T1 ", // 0x09L, // r/w - "PS_REGISTER_T2 ", // 0x0aL, // r/w - "PS_REGISTER_T3 ", // 0x0bL, // r/w - "PS_REGISTER_R0 ", // 0x0cL, // r/w - "PS_REGISTER_R1 ", // 0x0dL, // r/w - "PS_REGISTER_V1R0_SUM", // 0x0eL, // r - "PS_REGISTER_EF_PROD", // 0x0fL, // r - - "PS_REGISTER_ONE", // PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // OK for final combiner - "PS_REGISTER_NEGATIVE_ONE", // PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL, // invalid for final combiner - "PS_REGISTER_ONE_HALF", // PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NEGATE, // invalid for final combiner - "PS_REGISTER_NEGATIVE_ONE_HALF" // PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NORMAL, // invalid for final combiner -}; - -// PS Channel -char* PS_ChannelStr[/*PS_CHANNEL*/] = -{ - "PS_CHANNEL_RGB", // 0x00, // used as RGB source - "PS_CHANNEL_BLUE", // 0x00, // used as ALPHA source - "PS_CHANNEL_ALPHA", // 0x10, // used as RGB or ALPHA source -}; - -// PS FinalCombinerSetting -char* PS_FinalCombinerSettingStr[/*PS_FINALCOMBINERSETTING*/] = -{ - "PS_FINALCOMBINERSETTING_CLAMP_SUM", // 0x80, // V1+R0 sum clamped to [0,1] - "PS_FINALCOMBINERSETTING_COMPLEMENT_V1", // 0x40, // unsigned invert mapping - "PS_FINALCOMBINERSETTING_COMPLEMENT_R0", // 0x20, // unsigned invert mapping -}; - -// PS CombineOutput -char* PS_CombineOutputStr[/*PS_COMBINEROUTPUT*/] = -{ - "PS_COMBINEROUTPUT_IDENTITY", // 0x00L, // y = x - "PS_COMBINEROUTPUT_BIAS", // 0x08L, // y = x - 0.5 - "PS_COMBINEROUTPUT_SHIFTLEFT_1", // 0x10L, // y = x*2 - "PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS", // 0x18L, // y = (x - 0.5)*2 = x*2 - 1.0 - "PS_COMBINEROUTPUT_SHIFTLEFT_2", // 0x20L, // y = x*4 - "PS_COMBINEROUTPUT_SHIFTRIGHT_1", // 0x30L, // y = x/2 = x*0.5 - - "PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA", // 0x80L, // RGB only - - "PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA", // 0x40L, // RGB only - - "PS_COMBINEROUTPUT_AB_MULTIPLY", // 0x00L, - "PS_COMBINEROUTPUT_AB_DOT_PRODUCT", // 0x02L, // RGB only - - "PS_COMBINEROUTPUT_CD_MULTIPLY", // 0x00L, - "PS_COMBINEROUTPUT_CD_DOT_PRODUCT", // 0x01L, // RGB only - - "PS_COMBINEROUTPUT_AB_CD_SUM", // 0x00L, // 3rd output is AB+CD - "PS_COMBINEROUTPUT_AB_CD_MUX", // 0x04L, // 3rd output is MUX(AB,CD) based on R0.a -}; - -// PS GlobalFlags -char* PS_GlobalFlagsStr[/*PS_GLOBALFLAGS*/] = -{ - "PS_GLOBALFLAGS_NO_TEXMODE_ADJUST", // 0x0000L, // don't adjust texture modes - "PS_GLOBALFLAGS_TEXMODE_ADJUST", // 0x0001L, // adjust texture modes according to set texture -}; - -const int CONST_NEG_ONE = -2; -const int CONST_NEG_HALF = -1; -const int CONST_ZERO = 0; -const int CONST_POS_HALF = 1; // Note : Instead of 0.5 we use 1 (so we can keep using integers) -const int CONST_POS_ONE = 2; - -/// - -std::string PSCombinerOutputFlagsToStr(const DWORD dwFlags, bool aIsAlpha = false) -{ - std::string Result = PS_CombineOutputStr[0 + ((dwFlags & 0x38) >> 3)]; - Result = Result + " | " + PS_CombineOutputStr[8 + ((dwFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) >> 1)]; - Result = Result + " | " + PS_CombineOutputStr[10 + ((dwFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) >> 0)]; - Result = Result + " | " + PS_CombineOutputStr[12 + ((dwFlags & PS_COMBINEROUTPUT_AB_CD_MUX) >> 2)]; - - if (!aIsAlpha) { - if (dwFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) - Result = Result + " | " + PS_CombineOutputStr[6]; - - if (dwFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) - Result = Result + " | " + PS_CombineOutputStr[7]; - } - - return Result; -} - -std::string PSFinalCombinerSettingToStr(const DWORD dwPS_FINALCOMBINERSETTING) -{ - std::string Result = ""; - if (dwPS_FINALCOMBINERSETTING & PS_FINALCOMBINERSETTING_CLAMP_SUM) - Result = Result + " | " + PS_FinalCombinerSettingStr[0]; - - if (dwPS_FINALCOMBINERSETTING & PS_FINALCOMBINERSETTING_COMPLEMENT_V1) - Result = Result + " | " + PS_FinalCombinerSettingStr[1]; - - if (dwPS_FINALCOMBINERSETTING & PS_FINALCOMBINERSETTING_COMPLEMENT_R0) - Result = Result + " | " + PS_FinalCombinerSettingStr[2]; - - if (!Result.empty()) - Result.erase(0, 3); - - return Result; -} - -/* PSH_IMD_ARGUMENT */ - -void PSH_IMD_ARGUMENT::SetConstValue(float Value) -{ - Type = PARAM_VALUE; - Address = CONST_ZERO; - Multiplier = Value; - Modifiers = 0; -} - -float PSH_IMD_ARGUMENT::GetConstValue() -{ - if (Type != PARAM_VALUE) { - // Anything other than a value-parameter returns a value never checked for : - return INFINITY; - } - - float Result = Multiplier; - - // y = 1-x -> 0..1 > 1..0 - if (HasModifier(ARGMOD_INVERT)) Result = 1.0f-Result; - - // y = -x -> 0..1 > 0..-1 - if (HasModifier(ARGMOD_NEGATE)) Result = -Result; - - // y = x-0.5 -> 0..1 > -0.5..0.5 - if (HasModifier(ARGMOD_BIAS)) Result = Result-0.5f; - - // y = x*2 -> 0..1 > 0..2 - if (HasModifier(ARGMOD_SCALE_X2)) Result = Result*2.0f; - - // y = (x*2)-1 -> 0..1 > -1..1 - if (HasModifier(ARGMOD_SCALE_BX2)) Result = (Result*2.0f)-1.0f; - - // y = x*4 -> 0..1 > 0..4 - if (HasModifier(ARGMOD_SCALE_X4)) Result = Result*4.0f; - - // y = x/2 -> 0..1 > 0..0.5 - if (HasModifier(ARGMOD_SCALE_D2)) Result = Result/2.0f; - - return Result; -} // GetConstValue - -bool PSH_IMD_ARGUMENT::UsesRegister() -{ - return (Type > PARAM_DISCARD); -} - -bool PSH_IMD_ARGUMENT::IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) -{ - return (Type == aRegType) - && (Address == aAddress || aAddress == -1); -} - -bool PSH_IMD_ARGUMENT::IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask) -{ - return IsRegister(aRegType, aAddress) - // Check the mask itself, but also 'mask-less' : - && (((Mask & aMask) == aMask) || (Mask == 0)); -} - -void PSH_IMD_ARGUMENT::SetRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask) -{ - Type = aRegType; - Address = aAddress; - Mask = aMask; -} - -bool PSH_IMD_ARGUMENT::HasModifier(PSH_ARG_MODIFIER modifier) -{ - return (Modifiers & (1 << modifier)) != 0; -} - -bool PSH_IMD_ARGUMENT::SetScaleConstRegister(float factor, const PSH_RECOMPILED_SHADER& pRecompiled) -{ - bool result = false; - - PSH_ARG_MODIFIERs modifiers = 0; - DWORD mask = Mask; - int address = Address; - - const int mappedConstant0 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_MUL0]; - const int mappedConstant1 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_MUL1]; - - if (factor < 0.0f) - { - factor = -factor; - modifiers = (1 << ARGMOD_NEGATE); - } - - if (factor == 1.0f) - { - address = mappedConstant0; - mask = MASK_R; - result = true; - } - - else if (factor == 2.0f) - { - address = mappedConstant0; - mask = MASK_G; - result = true; - } - - else if (factor == 4.0f) - { - address = mappedConstant0; - mask = MASK_B; - result = true; - } - - else if (factor == 8.0f) - { - address = mappedConstant0; - mask = MASK_A; - result = true; - } - - else if (factor == 0.0f) - { - address = mappedConstant1; - mask = MASK_R; - result = true; - } - - else if (factor == 1.0f / 2.0f) - { - address = mappedConstant1; - mask = MASK_G; - result = true; - } - - else if (factor == 1.0f / 4.0f) - { - address = mappedConstant1; - mask = MASK_B; - result = true; - } - - else if (factor == 1.0f / 8.0f) - { - address = mappedConstant1; - mask = MASK_A; - result = true; - } - - if (result) - { - Type = PARAM_C; - Address = address; - Mask = mask; - Modifiers = modifiers; - Multiplier = 1.0f; - } - - return result; -} - -bool PSH_IMD_ARGUMENT::SetScaleBemLumRegister(D3DTEXTURESTAGESTATETYPE factor, int stage, const PSH_RECOMPILED_SHADER& pRecompiled) -{ - bool result = false; - - PSH_ARG_MODIFIERs modifiers = 0; - DWORD mask = Mask; - int address = Address; - - const int mappedConstant0 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_BEM + stage]; - const int mappedConstant1 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_LUM + stage]; - - switch (factor) - { - case D3DTSS_BUMPENVMAT00: - { - address = mappedConstant0; - mask = MASK_R; - result = true; - break; - } - case D3DTSS_BUMPENVMAT01: - { - address = mappedConstant0; - mask = MASK_G; - result = true; - break; - } - case D3DTSS_BUMPENVMAT11: - { - address = mappedConstant0; - mask = MASK_B; - result = true; - break; - } - case D3DTSS_BUMPENVMAT10: - { - address = mappedConstant0; - mask = MASK_A; - result = true; - break; - } - case D3DTSS_BUMPENVLSCALE: - { - address = mappedConstant1; - mask = MASK_R; - result = true; - break; - } - case D3DTSS_BUMPENVLOFFSET: - { - address = mappedConstant1; - mask = MASK_G; - result = true; - break; - } - } - - if (result) - { - Type = PARAM_C; - Address = address; - Mask = mask; - Modifiers = modifiers; - Multiplier = 1.0f; - } - - return result; -} - -std::string PSH_IMD_ARGUMENT::ToString() -{ - std::string Result; - - if (Type == PARAM_VALUE) - { - Result = std::to_string(GetConstValue()); - if (Result.find(".") > 0) - Result = Result + 'f'; - - return Result; - } - - Result = PSH_ARGUMENT_TYPE_Str[Type]; - - if (Type >= PARAM_R) - Result = Result + std::to_string(Address); - - if (UsesRegister()) - { - for (DWORD Modifier = ARGMOD_IDENTITY; Modifier < ARGMOD_BLUE_REPLICATE; Modifier++) - if (HasModifier((PSH_ARG_MODIFIER)Modifier)) { - char buffer[256]; - Result = std::string(buffer, sprintf(buffer, PSH_ARG_MODIFIER_Str[Modifier], Result.c_str())); - } - - if ((Mask > 0) && (Mask != MASK_RGBA)) - { - Result = Result + '.'; - if ((Mask & MASK_R) > 0) Result = Result + 'r'; - if ((Mask & MASK_G) > 0) Result = Result + 'g'; - if ((Mask & MASK_B) > 0) Result = Result + 'b'; - if ((Mask & MASK_A) > 0) Result = Result + 'a'; - } - } - return Result; -} // ToString - -bool PSH_IMD_ARGUMENT::Decode(const DWORD Value, DWORD aMask, TArgumentType ArgumentType) -{ - PS_REGISTER Reg; - PS_INPUTMAPPING InputMapping; - PS_CHANNEL Channel; - - bool Result = true; - Address = 0; - Mask = aMask; - Modifiers = (1 << ARGMOD_IDENTITY); - Multiplier = 1.0; - - // Determine PS_REGISTER for this argument type : - { - Reg = (PS_REGISTER)(Value & 0xF); - if (ArgumentType == atOutput) - { - // Output arguments may not write to C0 or C1, prevent that : - if ((Reg == PS_REGISTER_C0) || (Reg == PS_REGISTER_C1)) - Reg = PS_REGISTER_CXBX_PROD; // unhandled case - will reach "invalid" else-block - } - else - { - // Input arguments (normal or final combiners) can use the extended PS_REGISTER values : - if (Reg == PS_REGISTER_ZERO) - Reg = (PS_REGISTER)(Value & 0xE0); - - // 'Signed Identity' flag on PS_REGISTER_ZERO has no meaning, treat as zero : - if (Reg == PS_REGISTER_CXBX_PROD) - Reg = PS_REGISTER_ZERO; - - // Prevent decoding final combiner registers outside that mode : - if (ArgumentType != atFinalCombiner) - if ((Reg == PS_REGISTER_FOG) || (Reg == PS_REGISTER_V1R0_SUM) || (Reg == PS_REGISTER_EF_PROD)) - Reg = PS_REGISTER_CXBX_PROD; // unhandled case - will reach "invalid" else-block - } - } - - switch (Reg) { - case PS_REGISTER_ZERO: - { - if (ArgumentType == atOutput) - { - // Mark output arguments as 'discard' and return that fact : - Type = PARAM_DISCARD; - Result = false; - } - else - Type = PARAM_VALUE; - - Address = CONST_ZERO; - Multiplier = 0.0f; - break; - } - case PS_REGISTER_C0: - Type = PARAM_C; - break; - case PS_REGISTER_C1: - { - Type = PARAM_C; - Address = 1; - break; - } - case PS_REGISTER_V0: - Type = PARAM_V; - break; - case PS_REGISTER_V1: - { - Type = PARAM_V; - Address = 1; - break; - } - case PS_REGISTER_T0: - Type = PARAM_T; - break; - case PS_REGISTER_T1: - { - Type = PARAM_T; - Address = 1; - break; - } - case PS_REGISTER_T2: - { - Type = PARAM_T; - Address = 2; - break; - } - case PS_REGISTER_T3: - { - Type = PARAM_T; - Address = 3; - break; - } - case PS_REGISTER_R0: - Type = PARAM_R; - break; - case PS_REGISTER_R1: - { - Type = PARAM_R; - Address = 1; - break; - } - // Registers only available when ArgumentType != atOutput (Reg is capped otherwise) : - case PS_REGISTER_ONE: - { - Type = PARAM_VALUE; - Address = CONST_POS_ONE; - Multiplier = 1.0f; - break; - } - case PS_REGISTER_NEGATIVE_ONE: - { - Type = PARAM_VALUE; - Address = CONST_NEG_ONE; - Multiplier = -1.0f; - break; - } - case PS_REGISTER_ONE_HALF: - { - Type = PARAM_VALUE; - Address = CONST_POS_HALF; - Multiplier = 0.5f; - break; - } - case PS_REGISTER_NEGATIVE_ONE_HALF: - { - Type = PARAM_VALUE; - Address = CONST_NEG_HALF; - Multiplier = -0.5f; - break; - } - // Registers only available when ArgumentType == atFinalCombiner (Reg is capped otherwise) : - case PS_REGISTER_FOG: - Type = PARAM_FOG; - break; - case PS_REGISTER_V1R0_SUM: - Type = PARAM_V1R0_SUM; - break; - case PS_REGISTER_EF_PROD: - Type = PARAM_EF_PROD; - break; - default : - EmuLog(LOG_LEVEL::DEBUG, "INVALID ARGUMENT!"); - - Result = false; - } - - // We're done if this decoding is meant for output parameters, - // or when the input is a value-parameter (already read above) : - if ((ArgumentType == atOutput) - || (Type == PARAM_VALUE) ) - return Result; - - // Handle the Channel Designator : - { - Channel = (PS_CHANNEL)(Value & PS_CHANNEL_ALPHA); - if (Channel == PS_CHANNEL_ALPHA) - // Input comes from alpha portion of input register (valid for both RGB and alpha portions) : - Mask = MASK_A; - else // = PS_CHANNEL_BLUE (for Alpha step) = PS_CHANNEL_BLUE (for RGB step) : - if (aMask == MASK_A) - // Input comes from b portion of input register (valid for alpha portion only) : - Mask = MASK_B; // Note : This is not the same as ARGMOD_BLUE_REPLICATE! - else - // Input comes from the RGB portion of the input register (valid for RGB portion only) : - Mask = aMask; // Note : Is already put here, but makes this code clearer - } - - InputMapping = (PS_INPUTMAPPING)(Value & 0xe0); - -// ARGMOD_BIAS, -// -// ARGMOD_SCALE_X2, ARGMOD_SCALE_BX2, ARGMOD_SCALE_X4, ARGMOD_SCALE_D2, -// -// ARGMOD_SATURATE, -// -// ARGMOD_ALPHA_REPLICATE, ARGMOD_BLUE_REPLICATE]; - - switch (InputMapping) { - case PS_INPUTMAPPING_UNSIGNED_IDENTITY: - Modifiers = (1 << ARGMOD_IDENTITY); - break; - case PS_INPUTMAPPING_UNSIGNED_INVERT: - Modifiers = (1 << ARGMOD_INVERT); - break; - case PS_INPUTMAPPING_EXPAND_NORMAL: - { - Modifiers = (1 << ARGMOD_SCALE_BX2); - Multiplier = 2.0f * Multiplier; - break; - } - case PS_INPUTMAPPING_EXPAND_NEGATE: - { - Modifiers = (1 << ARGMOD_NEGATE); - Multiplier = -Multiplier; - break; - } - case PS_INPUTMAPPING_HALFBIAS_NORMAL: - Modifiers = (1 << ARGMOD_BIAS); - break; -// case PS_INPUTMAPPING_HALFBIAS_NEGATE: -// Modifiers = (1 << ARGMOD_IDENTITY); ??? -// break; - case PS_INPUTMAPPING_SIGNED_IDENTITY: - Modifiers = (1 << ARGMOD_IDENTITY); - break; - case PS_INPUTMAPPING_SIGNED_NEGATE: - { - Modifiers = (1 << ARGMOD_NEGATE); - Multiplier = -Multiplier; - break; - } - } - return Result; -} // Decode - -void PSH_IMD_ARGUMENT::Invert() -{ - if (!HasModifier(ARGMOD_INVERT)) - Modifiers = Modifiers | (1 << ARGMOD_INVERT); - else - Modifiers = Modifiers & ~(1 << ARGMOD_INVERT); -} - -void PSH_IMD_ARGUMENT::Negate() -{ - if (!HasModifier(ARGMOD_NEGATE)) - Modifiers = Modifiers | (1 << ARGMOD_NEGATE); - else - Modifiers = Modifiers & ~(1 << ARGMOD_NEGATE); -} - -/* PSH_INTERMEDIATE_FORMAT */ - -_PSH_INTERMEDIATE_FORMAT *PSH_INTERMEDIATE_FORMAT::Initialize(const PSH_OPCODE aOpcode) -{ - int i; - - Opcode = aOpcode; - Modifier = INSMOD_NONE; - for (i = 0; i < 3; i++) - { - Output[i] = {}; - Output[i].Multiplier = 1.0f; - } - for (i = 0; i < 7; i++) - { - Parameters[i] = {}; - Parameters[i].Multiplier = 1.0f; - } - - return this; -} - -std::string PSH_INTERMEDIATE_FORMAT::ToString() -{ - std::string Result = {}; - int i; - char SeparatorChar; - - switch (Opcode) { - case PO_COMMENT: - { - Result = "; " + CommentString; - return Result; - } - case PO_PS: { - // 1.1 allows reading from 2 textures (which we use in 'cnd') and reading from the .b (blue) channel - // 1.3 allows the use of texm3x2depth (which can occur sometimes) - // 2.0 allows up to r12, c32, t8 and s16 (requires Direct3D9) - // 3.0 allows up to r32, c224, v10 (instead of t via dcl), s16 and vFace (which can do two-sided lighting) - - // Use supplied pixel shader version (if any is given) - DWORD PSVersion = Parameters[6].Mask; - - Result = "ps_" + std::to_string(D3DSHADER_VERSION_MAJOR(PSVersion)) - + "_" + std::to_string(D3DSHADER_VERSION_MINOR(PSVersion)); - return Result; - } - case PO_XPS: { - Result = "xps.1.1"; - return Result; - } - } - - if (IsCombined) - Result = "+"; - else - Result = ""; - - Result = Result + PSH_OPCODE_DEFS[Opcode].mn + PSH_INST_MODIFIER_Str[Modifier]; - - // Output a comma-separated list of output registers : - SeparatorChar = ' '; - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._Out; i++) - { - Result = Result + SeparatorChar + Output[i].ToString(); - SeparatorChar = ','; - } - - // If this opcode has both output and input, put a space between them : - if ((PSH_OPCODE_DEFS[Opcode]._Out > 0) && (PSH_OPCODE_DEFS[Opcode]._In > 0)) - { - Result = Result + ","; - SeparatorChar = ' '; - } - - // Output a comma-separated list of parameters : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) - { - Result = Result + SeparatorChar + Parameters[i].ToString(); - SeparatorChar = ','; - } - - if ((!CommentString.empty()) - || (PSH_OPCODE_DEFS[Opcode].note != "")) - Result = Result + " ; " + PSH_OPCODE_DEFS[Opcode].note + " " + CommentString; - - return Result; -} // ToString - -bool PSH_INTERMEDIATE_FORMAT::IsArithmetic() -{ - return (Opcode >= PO_ADD); -} - -void PSH_INTERMEDIATE_FORMAT::ScaleOutput(float aFactor) -{ - assert(aFactor > 0.0f); - - if (aFactor == 1.0f) - return; - - if (aFactor == 0.5f) - { - // Half the output modifier : - switch (Modifier) { - case INSMOD_X8: - Modifier = INSMOD_X4; - break; - case INSMOD_X4: - Modifier = INSMOD_X2; - break; - case INSMOD_X2: - Modifier = INSMOD_NONE; - break; - case INSMOD_NONE: - Modifier = INSMOD_D2; - break; - case INSMOD_D2: - Modifier = INSMOD_D4; - break; - case INSMOD_D4: - Modifier = INSMOD_D8; - break; - } - - return; - } - - if (aFactor == 2.0f) - { - // Double the output modifier : - switch (Modifier) { - case INSMOD_D8: - Modifier = INSMOD_D4; - break; - case INSMOD_D4: - Modifier = INSMOD_D2; - break; - case INSMOD_D2: - Modifier = INSMOD_NONE; - break; - case INSMOD_NONE: - Modifier = INSMOD_X2; - break; - case INSMOD_X2: - Modifier = INSMOD_X4; - break; - case INSMOD_X4: - Modifier = INSMOD_X8; - break; - } - - return; - } -} - -bool PSH_INTERMEDIATE_FORMAT::ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress) // overload; -{ - int i; - bool Result; - - // Check all parameters : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) - { - // Check if one of them reads from the given register : - Result = Parameters[i].IsRegister(aRegType, aAddress); - if (Result) - return true; - } - - return false; -} - -bool PSH_INTERMEDIATE_FORMAT::ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask) // overload; -{ - int i; - bool Result; - - // Check all parameters : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) - { - // Check if one of them reads from the given register : - Result = Parameters[i].IsRegister(aRegType, aAddress, aMask); - if (Result) - return true; - } - - return false; -} - -// Used to determine the number of accesses to a register type within an instruction -// For use when determining register access limitations on certain instructions -// addressCount = the number of different registers read of the specified type -// total = the number of accesses to the spcified register type -bool PSH_INTERMEDIATE_FORMAT::ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, int& addressCount, int& total) // overload; -{ - int i; - bool Result; - bool RegisterUsage[256] = { false }; - - addressCount = 0; - total = 0; - - // Check all parameters : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) - { - // Check if one of them reads from the given register : - Result = Parameters[i].IsRegister(aRegType, aAddress, 0); - if (Result) - { - ++total; - if (!RegisterUsage[Parameters[i].Address]) - { - RegisterUsage[Parameters[i].Address] = true; - ++addressCount; - } - } - } - - return total > 0; -} - -bool PSH_INTERMEDIATE_FORMAT::WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress) // overload; -{ - int i; - bool Result; - - // Check the output : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._Out; i++) - { - // Check if one of them writes to the given register : - Result = Output[i].IsRegister(aRegType, aAddress); - if (Result) - return true; - } - - return false; -} - -bool PSH_INTERMEDIATE_FORMAT::WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask) // overload; -{ - int i; - bool Result; - - // Check the output : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._Out; i++) - { - // Check if one of them writes to the given register : - Result = Output[i].IsRegister(aRegType, aAddress, aMask); - if (Result) - return true; - } - - return false; -} - -void PSH_INTERMEDIATE_FORMAT::SwapParameter(const int Index1, const int Index2) -// Swaps two parameters. -{ - PSH_IMD_ARGUMENT TmpParameters; - - TmpParameters = Parameters[Index1]; - Parameters[Index1] = Parameters[Index2]; - Parameters[Index2] = TmpParameters; -} - -void PSH_INTERMEDIATE_FORMAT::XSwapOutput() -// Swaps the two outputs, along with their arguments. Applies only to Xbox opcodes. -{ - PSH_IMD_ARGUMENT TmpOutput; - - // Swap output 0 with 1 : - TmpOutput = Output[0]; - Output[0] = Output[1]; - Output[1] = TmpOutput; - - // Swap parameters 0 with 2 and 1 with 3 : - SwapParameter(0, 2); - SwapParameter(1, 3); -} - -bool PSH_INTERMEDIATE_FORMAT::MoveRemovableParametersRight(const int Index1, const int Index2) -// Swaps discarded (and const) parameters to the right position, to ease later conversions. -{ - bool Result = false; - - if ( (!Parameters[Index1].UsesRegister()) - && (Parameters[Index2].UsesRegister())) - { - SwapParameter(Index1, Index2); - Result = true; - } - return Result; -} - -bool PSH_INTERMEDIATE_FORMAT::XMoveNonRegisterOutputsRight() -// Swap discards and constants to the right position, to ease later conversions. Applies only to Xbox opcodes. -{ - bool Result = false; - - // First, check if the left output is discarded, while the second isn't : - if ( (!Output[0].UsesRegister()) - && (Output[1].UsesRegister())) - { - // Swap the outputs, so the discarded version is positioned rightmost : - XSwapOutput(); - Result = true; - } - - // Also try to swap the parameters to the first operation : - if (MoveRemovableParametersRight(0, 1)) - Result = true; - - // Idem for the parameters to second operation : - if (MoveRemovableParametersRight(2, 3)) - Result = true; - return Result; -} - -void PSH_INTERMEDIATE_FORMAT::XCopySecondOpcodeToFirst(const PSH_OPCODE aOpcode) -// Copies second opcode to first position, changing the opcode type on the fly. -{ - Opcode = aOpcode; - Output[0] = Output[1]; - Parameters[0] = Parameters[2]; - Parameters[1] = Parameters[3]; -} - -bool PSH_INTERMEDIATE_FORMAT::Decode(DWORD aCombinerStageNr, DWORD PSInputs, DWORD PSOutputs, DWORD aMask) -{ - DWORD CombinerOutputFlags; - int i; - - bool Result = false; - CombinerStageNr = aCombinerStageNr; - IsCombined = aMask == MASK_A; - - // Decode first two outputs : - if (Output[0].Decode((PSOutputs >> 4) & 0xF, aMask, atOutput)) - Result = true; - if (Output[1].Decode((PSOutputs >> 0) & 0xF, aMask, atOutput)) - Result = true; - - // Get the combiner output flags : - CombinerOutputFlags = (PS_COMBINEROUTPUT)(PSOutputs >> 12); - - // Use that to choose between the four possible operations : - // - xdd (dot/dot/discard) > calculating AB=A.B and CD=C.D - // - xdm (dot/mul/discard) > calculating AB=A.B and CD=C*D - // - xmmc (mul/mul/mux) > calculating AB=A*B and CD=C*D and Mux=AB?CD - // - xmma (mul/mul/sum) > calculating AB=A*B and CD=C*D and Sum=AB+CD - if ((CombinerOutputFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) > 0) // false=Multiply, true=DotProduct - { - if ((CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0) // false=Multiply, true=DotProduct - Opcode = PO_XDD; - else - Opcode = PO_XDM; - - // Note : All arguments are already in-place for these two opcodes. - - // No 3rd output; Assert that (PSOutputs >> 8) & 0xF == PS_REGISTER_DISCARD ? - } - else - if ((CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0) // false=Multiply, true=DotProduct - { - // The first operation is a multiply, but the second is a dot-product; - // There's no opcode for that, but we can reverse the two and still use XDM : - Opcode = PO_XDM; - XSwapOutput(); - - // No 3rd output; Assert that (PSOutputs >> 8) & 0xF == PS_REGISTER_DISCARD ? - } - else - { - if (/*AB_CD_SUM=*/(CombinerOutputFlags & PS_COMBINEROUTPUT_AB_CD_MUX) == 0) // true=AB+CD, false=MUX(AB,CD) based on R0.a - Opcode = PO_XMMA; - else - Opcode = PO_XMMC; - - // This has a 3rd output, set that already : - if (Output[2].Decode((PSOutputs >> 8) & 0xF, aMask, atOutput)) - Result = true; - } - - if (Result) - { - // Handle the Output Mapping : - switch (CombinerOutputFlags & 0x38) { - case PS_COMBINEROUTPUT_BIAS: Modifier = INSMOD_BIAS; break; // TODO : Fixup occurrances! - case PS_COMBINEROUTPUT_SHIFTLEFT_1: Modifier = INSMOD_X2; break; - case PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS: Modifier = INSMOD_BX2; break; // TODO : Fixup occurrances! - case PS_COMBINEROUTPUT_SHIFTLEFT_2: Modifier = INSMOD_X4; break; - case PS_COMBINEROUTPUT_SHIFTRIGHT_1: Modifier = INSMOD_D2; break; - default /*PS_COMBINEROUTPUT_IDENTITY*/: Modifier = INSMOD_NONE; break; - } - - if ((CombinerOutputFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) > 0) // false=Alpha-to-Alpha, true=Blue-to-Alpha - { - // Note : The effect of this flag is not entirely clear - blue to alpha itself is an easy to understand operation, - // but on what output does it operate? AB? or the mux_sum destination register (which doesn't occur when a dot - // operation is executed)? What if AB is discarded, but AB+CD is registered? Also, what happens to the other - // color channels (R,G and A) in that register? The docs seem to imply that AB itself is not changed (as they - // state that the alpha portion is not necessarily discarded), which would mean that only the mux_sum output - // is influenced, but that would imply that this flag has no effect for dot-products (XDD or XDM)... - // And if this is true, how do the blue-to-alpha flags behave if present on both AB and CD? - - // TODO : Rayman does this in some shaders, requires a fixup (as output.b is incorrect and not allowed) - Output[0].Modifiers = Output[0].Modifiers | (1 << ARGMOD_BLUE_REPLICATE); - Output[0].Mask = MASK_B; - // TODO Handle blue-to-alpha flag (only valid for RGB) - // Note : We can't use the '+ ' prefix, as the blue channel is not determined yet! - // Note 2: Pixel shader 1.1-1.3 'blue replicate' on source, uses an alpha destination write mask. - } - - if ((CombinerOutputFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) > 0) // false=Alpha-to-Alpha, true=Blue-to-Alpha - { - Output[1].Modifiers = Output[1].Modifiers | (1 << ARGMOD_BLUE_REPLICATE); - Output[1].Mask = MASK_B; - } - - // Decode all four inputs : - for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) - Parameters[i].Decode((PSInputs >> ((3-i) * 8)) & 0xFF, aMask, atInput); - } - return Result; -} // Decode - -bool PSH_INTERMEDIATE_FORMAT::DecodeFinalCombiner(DWORD aPSFinalCombinerInputsABCD, DWORD aPSFinalCombinerInputsEFG) -{ - int i; -// Note : The sign bit is lost upon input to the final combiner! - -// The final combiner performs the following operations : -// -// prod register = E*F // PS_REGISTER_EF_PROD, useable in A,B,C,D,G -// -// rgbout = A*B + (1-A)*C + D // lrp tmp.rgb, A, B, C // Note : tmp can be r0 if [A,B,C,D] * r0 = [] -// // add r0.rgb, tmp.rgb, D.rgb // Otherwise use a writable register from A;B or C -// -// alphaout = G.a // mov r0.a, G.a // Not necessary if G = r0 -// -// (also the final combiner can read PS_REGISTER_V1R0_SUM, which is equal to v1 + r0) -// Normal optimizations apply, like when A = PS_REGISTER_ZERO, all we have left is C + D (add r0.rgb, C.rgb, D.rgb) -// Also, if D = PS_REGISTER_ZERO, the add can be changed into a mov (if the result isn't already in r0.rgb) - - // Note : Previously, XSokoban lost it's font rendering when the final combiner was emitted, - // when disabled, the font reappeared (in various colors). This was because constants where - // not properly set locally. - - Opcode = PO_XFC; - CombinerStageNr = XFC_COMBINERSTAGENR; - - // Decode A,B,C and D : - for (i = 0; i < 4; i++) - Parameters[i].Decode((aPSFinalCombinerInputsABCD >> ((3-i) * 8)) & 0xFF, MASK_RGB/*?*/, atFinalCombiner); - - // Decode E,F and G : - for (i = 0; i < 3; i++) - Parameters[4+i].Decode((aPSFinalCombinerInputsEFG >> ((3-i) * 8)) & 0xFF, MASK_RGB/*?*/, atFinalCombiner); - - return true; -} - -/* PSH_XBOX_SHADER */ - -void PSH_XBOX_SHADER::SetPSVersion(const uint32_t PSVersion) -{ - m_PSVersion = PSVersion; - - // Source : https://en.wikipedia.org/wiki/High-Level_Shading_Language#Pixel_shader_comparison - if (m_PSVersion >= D3DPS_VERSION(4, 0)) { - MaxInputColorRegisters = 32; - MaxTemporaryRegisters = 4096; - MaxConstantFloatRegisters = 16*4096; - MaxSamplerRegisters = 16; - MaxTextureCoordinateRegisters = 0; // In shader model 4 and up, Dependent texture limit (T) is unlimited - // Note : Input Registers (v#) are now fully floating point and the Texture Coordinate Registers (t#) have been consolidated into it. - - PSH_PC_MAX_REGISTER_COUNT = 16 * 4096; - } - else if (m_PSVersion >= D3DPS_VERSION(3, 0)) { - // Source https://msdn.microsoft.com/en-us/library/windows/desktop/bb172920(v=vs.85).aspx - MaxInputColorRegisters = 10; - MaxTemporaryRegisters = 32; - MaxConstantFloatRegisters = 224; - MaxSamplerRegisters = 16; - MaxTextureCoordinateRegisters = 0; // In shader model 3 and up, Dependent texture limit (T) is unlimited - - PSH_PC_MAX_REGISTER_COUNT = 224; - } - else if (m_PSVersion >= D3DPS_VERSION(2, 0)) { - // Source https://msdn.microsoft.com/en-us/library/windows/desktop/bb172918(v=vs.85).aspx - MaxInputColorRegisters = 2; - MaxTemporaryRegisters = 12; // 12 min/32 max: The number of r# registers is determined by D3DCAPS9.D3DPSHADERCAPS2_0.NumTemps (which ranges from 12 to 32). - MaxConstantFloatRegisters = 32; - MaxSamplerRegisters = 16; - MaxTextureCoordinateRegisters = 8; - - PSH_PC_MAX_REGISTER_COUNT = 32; - } - else - assert(false); // We no longer support less than Direct3D 9 - /* For documentation purposes, keep the below information around : - else if (m_PSVersion >= D3DPS_VERSION(1, 4)) { - // Source https://msdn.microsoft.com/en-us/library/windows/desktop/bb172917(v=vs.85).aspx - MaxConstantFloatRegisters = 8; - MaxTemporaryRegisters = 6; - MaxTextureCoordinateRegisters = 4; - MaxInputColorRegisters = 2; // 2 in phase 2 - MaxSamplerRegisters = 0; // Not yet in shader model 1 - - PSH_PC_MAX_REGISTER_COUNT = 8; - } - else if (m_PSVersion >= D3DPS_VERSION(1, 3)) { - MaxConstantFloatRegisters = 8; - MaxTemporaryRegisters = 2; - MaxTextureCoordinateRegisters = 4; - MaxInputColorRegisters = 2; - MaxSamplerRegisters = 0; // Not yet in shader model 1 - - PSH_PC_MAX_REGISTER_COUNT = 8; - } - else if (m_PSVersion >= D3DPS_VERSION(1, 2)) { - MaxConstantFloatRegisters = 8; - MaxTemporaryRegisters = 2; - MaxTextureCoordinateRegisters = 4; - MaxInputColorRegisters = 2; - MaxSamplerRegisters = 0; // Not yet in shader model 1 - - PSH_PC_MAX_REGISTER_COUNT = 8; - } - else { - // m_PSVersion >= D3DPS_VERSION(1, 1) - MaxConstantFloatRegisters = 8; - MaxTemporaryRegisters = 2; - MaxTextureCoordinateRegisters = 4; // Some sources say 2? - MaxInputColorRegisters = 2; - MaxSamplerRegisters = 0; // Not yet in shader model 1 - - PSH_PC_MAX_REGISTER_COUNT = 8; - } */ -} - -std::string PSH_XBOX_SHADER::ToString() -{ - std::string Result; - int i; - - for (i = 0; i < IntermediateCount; i++) - Result = Result + Intermediate[i].ToString() + "\n"; - - return Result; -} - -void PSH_XBOX_SHADER::Log(const char *PhaseStr) -{ - //if (MayLog(lfUnit)) - { - EmuLog(LOG_LEVEL::DEBUG, "New decoding - %s :", PhaseStr); - EmuLog(LOG_LEVEL::DEBUG, "%s", ToString().c_str()); - } -} - -PPSH_INTERMEDIATE_FORMAT PSH_XBOX_SHADER::NewIntermediate() -{ - PPSH_INTERMEDIATE_FORMAT Result = &Intermediate[IntermediateCount]; - Result->Initialize(PO_COMMENT); - ++IntermediateCount; - return Result; -} - -void PSH_XBOX_SHADER::InsertIntermediate(PPSH_INTERMEDIATE_FORMAT pIntermediate, int Index) -{ - int i; - i = IntermediateCount - 1; - while (i >= Index) - { - Intermediate[i + 1] = Intermediate[i]; - --i; - } - - Intermediate[Index] = *pIntermediate; - ++IntermediateCount; -} - -void PSH_XBOX_SHADER::DeleteIntermediate(int Index) -{ - int i; - for (i = Index; i < IntermediateCount - 1; i++) - Intermediate[i] = Intermediate[i + 1]; - - --IntermediateCount; -} - -void PSH_XBOX_SHADER::DeleteLastIntermediate() -{ - if (IntermediateCount > 0) - DeleteIntermediate(IntermediateCount - 1); -} - -std::string PSH_XBOX_SHADER::OriginalToString(XTL::X_D3DPIXELSHADERDEF *pPSDef) // static -{ - char buffer[4096]; - return std::string(buffer, sprintf(buffer, "PSAphaInputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" - "PSFinalCombinerInputsABCD = 0x%.08X\n" - "PSFinalCombinerInputsEFG = 0x%.08X\n" - "PSConstant0[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" - "PSConstant1[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" - "PSAlphaOutputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" - "PSRGBInputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" - "PSCompareMode = 0x%.08X\n" - "PSFinalCombinerConstant0 = 0x%.08X\n" - "PSFinalCombinerConstant1 = 0x%.08X\n" - "PSRGBOutputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" - "PSCombinerCount = 0x%.08X\n" - "PSTextureModes = 0x%.08X\n" - "PSDotMapping = 0x%.08X\n" - "PSInputTexture = 0x%.08X\n" - "PSC0Mapping = 0x%.08X\n" - "PSC1Mapping = 0x%.08X\n" - "PSFinalCombinerConstants = 0x%.08X\n", - pPSDef->PSAlphaInputs[0], pPSDef->PSAlphaInputs[1], pPSDef->PSAlphaInputs[2], pPSDef->PSAlphaInputs[3], - pPSDef->PSAlphaInputs[4], pPSDef->PSAlphaInputs[5], pPSDef->PSAlphaInputs[6], pPSDef->PSAlphaInputs[7], - pPSDef->PSFinalCombinerInputsABCD, - pPSDef->PSFinalCombinerInputsEFG, - pPSDef->PSConstant0[0], pPSDef->PSConstant0[1], pPSDef->PSConstant0[2], pPSDef->PSConstant0[3], - pPSDef->PSConstant0[4], pPSDef->PSConstant0[5], pPSDef->PSConstant0[6], pPSDef->PSConstant0[7], - pPSDef->PSConstant1[0], pPSDef->PSConstant1[1], pPSDef->PSConstant1[2], pPSDef->PSConstant1[3], - pPSDef->PSConstant1[4], pPSDef->PSConstant1[5], pPSDef->PSConstant1[6], pPSDef->PSConstant1[7], - pPSDef->PSAlphaOutputs[0], pPSDef->PSAlphaOutputs[1], pPSDef->PSAlphaOutputs[2], pPSDef->PSAlphaOutputs[3], - pPSDef->PSAlphaOutputs[4], pPSDef->PSAlphaOutputs[5], pPSDef->PSAlphaOutputs[6], pPSDef->PSAlphaOutputs[7], - pPSDef->PSRGBInputs[0], pPSDef->PSRGBInputs[1], pPSDef->PSRGBInputs[2], pPSDef->PSRGBInputs[3], - pPSDef->PSRGBInputs[4], pPSDef->PSRGBInputs[5], pPSDef->PSRGBInputs[6], pPSDef->PSRGBInputs[7], - pPSDef->PSCompareMode, - pPSDef->PSFinalCombinerConstant0, - pPSDef->PSFinalCombinerConstant1, - pPSDef->PSRGBOutputs[0], pPSDef->PSRGBOutputs[1], pPSDef->PSRGBOutputs[2], pPSDef->PSRGBOutputs[3], - pPSDef->PSRGBOutputs[4], pPSDef->PSRGBOutputs[5], pPSDef->PSRGBOutputs[6], pPSDef->PSRGBOutputs[7], - pPSDef->PSCombinerCount, - XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES), /* pPSDef->PSTextureModes is stored in a different place than pPSDef*/ - pPSDef->PSDotMapping, - pPSDef->PSInputTexture, - pPSDef->PSC0Mapping, - pPSDef->PSC1Mapping, - pPSDef->PSFinalCombinerConstants)); -} - -void PSH_XBOX_SHADER::GetPSTextureModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_TEXTUREMODES psTextureModes[XTL::X_D3DTS_STAGECOUNT]) -{ - for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) - { - psTextureModes[i] = (PS_TEXTUREMODES)((XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> (i * 5)) & 0x1F); - } -} - -void PSH_XBOX_SHADER::GetPSDotMapping(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_DOTMAPPING psDotMapping[XTL::X_D3DTS_STAGECOUNT]) -{ - psDotMapping[0] = (PS_DOTMAPPING)(0); - psDotMapping[1] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> 0) & 0x7); - psDotMapping[2] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> 4) & 0x7); - psDotMapping[3] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> 8) & 0x7); -} - -void PSH_XBOX_SHADER::GetPSCompareModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, DWORD psCompareModes[XTL::X_D3DTS_STAGECOUNT]) -{ - for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) - { - psCompareModes[i] = (pPSDef->PSCompareMode >> (i * 4)) & 0xF; - } -} - -void PSH_XBOX_SHADER::GetPSInputTexture(XTL::X_D3DPIXELSHADERDEF* pPSDef, int psInputTexture[XTL::X_D3DTS_STAGECOUNT]) -{ - psInputTexture[0] = -1; // Stage 0 has no predecessors - psInputTexture[1] = 0; // Stage 1 can only use stage 0 - psInputTexture[2] = (pPSDef->PSInputTexture >> 16) & 0x1; // Stage 2 can use stage 0 or 1 - psInputTexture[3] = (pPSDef->PSInputTexture >> 20) & 0x3; // Stage 3 can only use stage 0, 1 or 2 -} - -void PSH_XBOX_SHADER::Decode(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ - int i; - - /* Azurik likes to create and destroy the same shader every frame! O_o - LogFlags = lfUnit; - if (IsRunning(TITLEID_AZURIK)) - LogFlags = LogFlags | lfExtreme;*/ - - GetPSTextureModes(pPSDef, PSTextureModes); - GetPSCompareModes(pPSDef, PSCompareMode); - GetPSDotMapping(pPSDef, PSDotMapping); - GetPSInputTexture(pPSDef, PSInputTexture); - - NumberOfCombiners = (pPSDef->PSCombinerCount >> 0) & 0xF; - CombinerCountFlags = (pPSDef->PSCombinerCount >> 8); - - CombinerMuxesOnMsb = (CombinerCountFlags & PS_COMBINERCOUNT_MUX_MSB) > 0; - CombinerHasUniqueC0 = (CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C0) > 0; - CombinerHasUniqueC1 = (CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C1) > 0; - - // Backwards compatible decoding (purely for logging) : - { - for (i = 0; i < XTL::X_PSH_COMBINECOUNT; i++) { - Combiners[i].RGB.Decode(pPSDef->PSRGBInputs[i], pPSDef->PSRGBOutputs[i]); - Combiners[i].Alpha.Decode(pPSDef->PSAlphaInputs[i], pPSDef->PSAlphaOutputs[i], /*aIsAlpha=*/true); - } - - FinalCombiner.Decode(pPSDef->PSFinalCombinerInputsABCD, pPSDef->PSFinalCombinerInputsEFG, pPSDef->PSFinalCombinerConstants); - } -} - -PSH_RECOMPILED_SHADER PSH_XBOX_SHADER::Convert(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ - int i; - Recompiled = {}; - Recompiled.PSDef = *pPSDef; - - // Use a fluent interface to start with a pixel shader version opcode that knowns the host version - NewIntermediate()->Initialize(PO_XPS)->Parameters[6].Mask = m_PSVersion; - - for (i = 0; i < NumberOfCombiners; i++) - { - // Check that the RGB and Alpha inputs do the same operation : - if ( ((pPSDef->PSRGBInputs[i] & PS_NoChannelsMask) == (pPSDef->PSAlphaInputs[i] & PS_NoChannelsMask)) - // Check if all RGB channels are set to read from PS_CHANNEL_RGB : - && ((pPSDef->PSRGBInputs[i] & PS_AlphaChannelsMask) == 0) - // Check if all Alpha channels are set to read from PS_CHANNEL_ALPHA : - && ((pPSDef->PSAlphaInputs[i] & PS_AlphaChannelsMask) == PS_AlphaChannelsMask) - // Check that RGB and Alpha output to the same register(s) : - && (pPSDef->PSRGBOutputs[i] == pPSDef->PSAlphaOutputs[i])) - { - // In this case, we can convert RGB and Alpha together : - if (!NewIntermediate()->Decode(i, pPSDef->PSRGBInputs[i], pPSDef->PSRGBOutputs[i], MASK_RGBA)) - DeleteLastIntermediate(); - } - else - { - // Otherwise, we need to convert RGB and Alpha separately : - if (!NewIntermediate()->Decode(i, pPSDef->PSRGBInputs[i], pPSDef->PSRGBOutputs[i], MASK_RGB)) - DeleteLastIntermediate(); - - if (!NewIntermediate()->Decode(i, pPSDef->PSAlphaInputs[i], pPSDef->PSAlphaOutputs[i], MASK_A)) - DeleteLastIntermediate(); - } - } - - if ((pPSDef->PSFinalCombinerInputsABCD > 0) - || (pPSDef->PSFinalCombinerInputsEFG > 0)) { - if (NewIntermediate()->DecodeFinalCombiner(pPSDef->PSFinalCombinerInputsABCD, pPSDef->PSFinalCombinerInputsEFG)) - { - FinalCombinerFlags = (PS_FINALCOMBINERSETTING)((pPSDef->PSFinalCombinerInputsEFG >> 0) & 0xFF); -// dwPS_GLOBALFLAGS = (pPSDef->PSFinalCombinerConstants >> 8) & 0x1; - } - else - DeleteLastIntermediate(); - } - // Dump the contents of the PixelShader def - //if (MayLog(LogFlags)) - // dump pixel shader definition to string - // TODO : Reinstate : XTL_DumpPixelShaderToFile(pPSDef); - - //if (MayLog(LogFlags)) - { - // print relevant contents to the debug console - EmuLog(LOG_LEVEL::DEBUG, "%s", DecodedToString(pPSDef).c_str()); - } - - // TODO: - // - Insert tex* and def instructions - - Log("Parse result"); - - if (MoveRemovableParametersRight()) - Log("MoveRemovableParametersRight"); - - if (RemoveNops()) - Log("RemoveNops"); - - while (RemoveUselessWrites()) { - Log("RemoveUselessWrites"); - if (RemoveNops()) - Log("RemoveNops"); - } - - if (ConvertConstantsToNative(pPSDef, /*Recompiled=*/&Recompiled)) - Log("ConvertConstantsToNative"); - - // Handle Texture declarations : - if (DecodeTextureModes(pPSDef)) - Log("DecodeTextureModes"); - - ConvertXboxOpcodesToNative(pPSDef); - Log("ConvertXboxOpcodesToNative"); - - while (RemoveUselessWrites()) { // again - Log("RemoveUselessWrites"); - if (RemoveNops()) - Log("RemoveNops"); - } - - // Resolve all differences : - if (FixupPixelShader()) - Log("FixupPixelShader"); - - if (FixInvalidDstRegister()) - Log("FixInvalidDstRegister"); - - if (FixConstantParameters()) - Log("FixConstantParameters"); - - if (FixArgumentModifiers()) - Log("FixArgumentModifiers"); - - if (FixInstructionModifiers()) - Log("FixInstructionModifiers"); - - if (FixInvalidSrcSwizzle()) - Log("FixInvalidSrcSwizzle"); - - if (FixMissingR0a()) - Log("FixMissingR0a"); - - if (FixMissingR1a()) - Log("FixMissingR1a"); - - if (FixCoIssuedOpcodes()) - Log("FixCoIssuedOpcodes"); - - if (FixOverusedRegisters()) - Log("FixOverusedRegisters"); - - if (FixUninitializedReads()) - Log("FixUninitializedReads"); - - if (FinalizeShader()) - Log("FinalizeShader"); - - Log("End result"); - - Recompiled.NewShaderStr = ToString(); - return Recompiled; -} - -std::string PSH_XBOX_SHADER::DecodedToString(XTL::X_D3DPIXELSHADERDEF *pPSDef) -// print relevant contents to the debug console - - #define _AddStr1(aStr) \ - \ - Result = Result + aStr + "\n"; - - #define _AddStr(aStr, ...) \ - {\ - _AddStr1(std::string(buf, sprintf(buf, aStr, __VA_ARGS__))); \ - } -{ - char buf[256]; - int i; - - std::string Result = ""; - // Show the contents to the user - _AddStr1("\n-----PixelShader Definition Contents-----"); - _AddStr1(OriginalToString(pPSDef)); - - if (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) > 0) - { - _AddStr1("\nPSTextureModes ->"); // Texture addressing modes - _AddStr("Stage 0: %s", PS_TextureModesStr[PSTextureModes[0]]); - _AddStr("Stage 1: %s", PS_TextureModesStr[PSTextureModes[1]]); - _AddStr("Stage 2: %s", PS_TextureModesStr[PSTextureModes[2]]); - _AddStr("Stage 3: %s", PS_TextureModesStr[PSTextureModes[3]]); - } - - if (pPSDef->PSDotMapping > 0) // Input mapping for dot product modes - { - _AddStr1("\nPSDotMapping ->"); - _AddStr("Stage 1: %s", PS_DotMappingStr[PSDotMapping[1]]); - _AddStr("Stage 2: %s", PS_DotMappingStr[PSDotMapping[2]]); - _AddStr("Stage 3: %s", PS_DotMappingStr[PSDotMapping[3]]); - } - - if (pPSDef->PSCompareMode > 0) // Compare modes for clipplane texture mode - { - _AddStr1("\nPSCompareMode ->"); - _AddStr("Stage 0: %s", PS_CompareModeStr[(PSCompareMode[0] == 0) ? 0 : 1]); - _AddStr("Stage 1: %s", PS_CompareModeStr[(PSCompareMode[1] == 0) ? 2 : 3]); - _AddStr("Stage 2: %s", PS_CompareModeStr[(PSCompareMode[2] == 0) ? 4 : 5]); - _AddStr("Stage 3: %s", PS_CompareModeStr[(PSCompareMode[3] == 0) ? 6 : 7]); - } - - if (pPSDef->PSInputTexture > 0) // Texture source for some texture modes - { - _AddStr1("\nPSInputTexture ->"); - _AddStr("Stage 1: %d", PSInputTexture[1]); - _AddStr("Stage 2: %d", PSInputTexture[2]); - _AddStr("Stage 3: %d", PSInputTexture[3]); - } - - if (pPSDef->PSCombinerCount > 0) // Active combiner count (Stages 0-7) - { - _AddStr1("\nPSCombinerCount ->"); - _AddStr("Combiners: %d", NumberOfCombiners); - _AddStr("Mux: %s", PS_CombinerCountFlagsStr[(CombinerCountFlags & PS_COMBINERCOUNT_MUX_MSB) == 0 ? 0 : 1]); - _AddStr("C0: %s", PS_CombinerCountFlagsStr[(CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C0) == 0 ? 2 : 3]); - _AddStr("C1: %s", PS_CombinerCountFlagsStr[(CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C1) == 0 ? 4 : 5]); - } - - // Dxbx additions from here onwards : - - for (i = 0; i < NumberOfCombiners; i++) // Loop over all combiner stages - { - _AddStr1("\n"); - - _AddStr("PSRGBOutputs[%d] AB: %s", i, Combiners[i].RGB.OutputSUM.OutputAB.DecodedToString().c_str()); - _AddStr("PSRGBOutputs[%d] CD: %s", i, Combiners[i].RGB.OutputSUM.OutputCD.DecodedToString().c_str()); - _AddStr("PSRGBOutputs[%d] SUM: %s", i, Combiners[i].RGB.OutputSUM.DecodedToString().c_str()); - _AddStr("PSRGBOutputs[%d] flags: %s", i, PSCombinerOutputFlagsToStr(Combiners[i].RGB.CombinerOutputFlags, /*aIsAlpha=*/false).c_str()); - - _AddStr1("\n"); - _AddStr("PSRGBInputs[%d] A: %s", i, Combiners[i].RGB.OutputSUM.OutputAB.Input1.DecodedToString().c_str()); - _AddStr("PSRGBInputs[%d] B: %s", i, Combiners[i].RGB.OutputSUM.OutputAB.Input2.DecodedToString().c_str()); - _AddStr("PSRGBInputs[%d] C: %s", i, Combiners[i].RGB.OutputSUM.OutputCD.Input1.DecodedToString().c_str()); - _AddStr("PSRGBInputs[%d] D: %s", i, Combiners[i].RGB.OutputSUM.OutputCD.Input2.DecodedToString().c_str()); - - _AddStr1("\n"); - _AddStr("PSAlphaOutputs[%d] AB: %s", i, Combiners[i].Alpha.OutputSUM.OutputAB.DecodedToString().c_str()); - _AddStr("PSAlphaOutputs[%d] CD: %s", i, Combiners[i].Alpha.OutputSUM.OutputCD.DecodedToString().c_str()); - _AddStr("PSAlphaOutputs[%d] SUM: %s", i, Combiners[i].Alpha.OutputSUM.DecodedToString().c_str()); - _AddStr("PSAlphaOutputs[%d] flags: %s", i, PSCombinerOutputFlagsToStr(Combiners[i].Alpha.CombinerOutputFlags, /*aIsAlpha=*/true).c_str()); - - _AddStr1("\n"); - _AddStr("PSAlphaInputs[%d] A: %s", i, Combiners[i].Alpha.OutputSUM.OutputAB.Input1.DecodedToString().c_str()); - _AddStr("PSAlphaInputs[%d] B: %s", i, Combiners[i].Alpha.OutputSUM.OutputAB.Input2.DecodedToString().c_str()); - _AddStr("PSAlphaInputs[%d] C: %s", i, Combiners[i].Alpha.OutputSUM.OutputCD.Input1.DecodedToString().c_str()); - _AddStr("PSAlphaInputs[%d] D: %s", i, Combiners[i].Alpha.OutputSUM.OutputCD.Input2.DecodedToString().c_str()); - - _AddStr1("\n"); - _AddStr("PSConstant0[%d] : %x", i, pPSDef->PSConstant0[i]); // C0 for each stage - _AddStr("PSConstant1[%d] : %x", i, pPSDef->PSConstant1[i]); // C1 for each stage - } - - if ((pPSDef->PSFinalCombinerInputsABCD > 0) - || (pPSDef->PSFinalCombinerInputsEFG > 0)) // Final combiner inputs - { - _AddStr("\nPSFinalCombinerConstant0 : %x", pPSDef->PSFinalCombinerConstant0); // C0 in final combiner - _AddStr("PSFinalCombinerConstant1 : %x", pPSDef->PSFinalCombinerConstant1); // C1 in final combiner - - _AddStr1("\nPSFinalCombinerInputsABCD ->"); - _AddStr("Input A: %s", FinalCombiner.InputA.DecodedToString().c_str()); - _AddStr("Input B: %s", FinalCombiner.InputB.DecodedToString().c_str()); - _AddStr("Input C: %s", FinalCombiner.InputC.DecodedToString().c_str()); - _AddStr("Input D: %s", FinalCombiner.InputD.DecodedToString().c_str()); - - _AddStr1("\nPSFinalCombinerInputsEFG ->"); - _AddStr("Input E: %s", FinalCombiner.InputE.DecodedToString().c_str()); - _AddStr("Input F: %s", FinalCombiner.InputF.DecodedToString().c_str()); - _AddStr("Input G: %s", FinalCombiner.InputG.DecodedToString().c_str()); - _AddStr("Final combiner setting: %s", PSFinalCombinerSettingToStr((DWORD)(FinalCombiner.FinalCombinerFlags)).c_str()); - - _AddStr1("\nPSFinalCombinerConstants ->"); // Final combiner constant mapping - _AddStr("Offset of D3D constant for (C0: %d", FinalCombiner.FinalCombinerC0Mapping); - _AddStr("Offset of D3D constant for (C1: %d", FinalCombiner.FinalCombinerC1Mapping); - _AddStr("Adjust texture flag: %s", PS_GlobalFlagsStr[PS_GLOBALFLAGS(FinalCombiner.dwPS_GLOBALFLAGS)]); - } - - _AddStr1("\n"); - return Result; -} - - bool _OpcodeMustStayBeforeTextureMode(PSH_OPCODE Opcode, int i) - { - if (Opcode == PO_XPS) - return true; - - // Before texture modes, only keep the first comment (the one mentioning "xps" got converted into "ps") - if (Opcode == PO_COMMENT) - return (i == 0); - - if (Opcode == PO_PS) - return true; - - if (Opcode == PO_DEF) - return true; - - if (Opcode >= PO_DCL && Opcode <= PO_DCL_VOLUME) - return true; - - return false; - } - - bool PSH_XBOX_SHADER::_NextIs2D(int Stage) - { - if (Stage < XTL::X_D3DTS_STAGECOUNT-1) - return (PSTextureModes[Stage + 1] == PS_TEXTUREMODES_DOT_ST) || (PSTextureModes[Stage + 1] == PS_TEXTUREMODES_DOT_ZW); - else - return false; - } - -bool PSH_XBOX_SHADER::DecodeTextureModes(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ - int InsertPos; - PSH_INTERMEDIATE_FORMAT Ins = {}; - std::vector InsertIns; - int Stage; - - InsertIns.reserve(32); // arbitrary allotment of instructions - InsertIns.resize(XTL::X_D3DTS_STAGECOUNT); // default initialized to PO_COMMENT instructions - - bool Result = false; - - InsertPos = -1; - do { - ++InsertPos; - } while (_OpcodeMustStayBeforeTextureMode(Intermediate[InsertPos].Opcode, InsertPos)); - - Ins.Initialize(PO_DCL); - for (Stage = 0; Stage < XTL::X_D3DTS_STAGECOUNT; Stage++) - { - if (PSTextureModes[Stage] != PS_TEXTUREMODES_NONE || Stage < PSH_XBOX_MAX_T_REGISTER_COUNT) - { - switch (PSTextureModes[Stage]) - { - case PS_TEXTUREMODES_PROJECT2D: // argb = texture(r/q, s/q) TODO : Apply the division via D3DTOP_BUMPENVMAP ? - case PS_TEXTUREMODES_BUMPENVMAP: - case PS_TEXTUREMODES_BUMPENVMAP_LUM: - case PS_TEXTUREMODES_DOT_ST: - case PS_TEXTUREMODES_DPNDNT_AR: - case PS_TEXTUREMODES_DPNDNT_GB: - { - Ins.Opcode = PO_DCL_2D; - Ins.Output[0].SetRegister(PARAM_S, Stage, MASK_RGBA); - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - Result = true; - break; - } - case PS_TEXTUREMODES_PROJECT3D: // argb = texture(r/q, s/q, t/q) Note : 3d textures are sampled using PS_TEXTUREMODES_CUBEMAP - case PS_TEXTUREMODES_BRDF: - case PS_TEXTUREMODES_DOT_STR_3D: - { - Ins.Opcode = PO_DCL_VOLUME; - Ins.Output[0].SetRegister(PARAM_S, Stage, MASK_RGBA); - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - Result = true; - break; - } - case PS_TEXTUREMODES_CUBEMAP: // argb = cubemap(r/q, s/q, t/q) - case PS_TEXTUREMODES_DOT_RFLCT_DIFF: - case PS_TEXTUREMODES_DOT_RFLCT_SPEC: - case PS_TEXTUREMODES_DOT_STR_CUBE: - case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST: - { - Ins.Opcode = PO_DCL_CUBE; - Ins.Output[0].SetRegister(PARAM_S, Stage, MASK_RGBA); - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - Result = true; - break; - } - } - - Ins.Opcode = PO_DCL; - Ins.Output[0].SetRegister(PARAM_T, Stage, MASK_RGBA); - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - Result = true; - } - } - - for (int j = 0; j < PSH_XBOX_MAX_V_REGISTER_COUNT; ++j) - { - Ins.Opcode = PO_DCL; - Ins.Output[0].SetRegister(PARAM_V, j, MASK_RGBA); - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - Result = true; - } - - PSH_OPCODE Opcode; - - Opcode = PO_TEXLD2; - - for (Stage = 0; Stage < XTL::X_D3DTS_STAGECOUNT; Stage++) - { - // TODO : Apply conversions when PS_GLOBALFLAGS_TEXMODE_ADJUST is set (but ... how to check the texture type? read D3DRS_PSTEXTUREMODES?) - - // Convert the texture mode to a texture addressing instruction : - switch (PSTextureModes[Stage]) { // input = q,s,t,r (same layout as a,r,g,b, also known as w,x,y,z) - case PS_TEXTUREMODES_PROJECT2D: // argb = texture(r/q, s/q) TODO : Apply the division via D3DTOP_BUMPENVMAP ? - case PS_TEXTUREMODES_PROJECT3D: // argb = texture(r/q, s/q, t/q) Note : 3d textures are sampled using PS_TEXTUREMODES_CUBEMAP - case PS_TEXTUREMODES_CUBEMAP: { // argb = cubemap(r/q, s/q, t/q) - Opcode = PO_TEXLD2; - - if (m_PSVersion >= D3DPS_VERSION(3, 0)) - continue; - break; - } - case PS_TEXTUREMODES_NONE: - case PS_TEXTUREMODES_PASSTHRU: - Opcode = PO_MOV; - break; - case PS_TEXTUREMODES_CLIPPLANE: Opcode = PO_TEXKILL; break; - case PS_TEXTUREMODES_BUMPENVMAP: Opcode = PO_TEXBEM; break; - case PS_TEXTUREMODES_BUMPENVMAP_LUM: Opcode = PO_TEXBEML; break; - case PS_TEXTUREMODES_BRDF: Opcode = PO_TEXBRDF; break; // Note : Not supported by Direct3D8 ? - case PS_TEXTUREMODES_DOT_ST: Opcode = PO_TEXM3X2TEX; break; - case PS_TEXTUREMODES_DOT_ZW: Opcode = PO_TEXM3X2DEPTH; break; // Note : requires ps.1.3 and a preceding texm3x2pad - case PS_TEXTUREMODES_DOT_RFLCT_DIFF: Opcode = PO_TEXM3X3DIFF; break; // Note : Not supported by Direct3D8 ? - case PS_TEXTUREMODES_DOT_RFLCT_SPEC: Opcode = PO_TEXM3X3VSPEC; break; - case PS_TEXTUREMODES_DOT_STR_3D: Opcode = PO_TEXM3X3TEX; break; // Note : Uses a 3d texture - case PS_TEXTUREMODES_DOT_STR_CUBE: Opcode = PO_TEXM3X3TEX; break; // Note : Uses a cube texture - case PS_TEXTUREMODES_DPNDNT_AR: Opcode = PO_TEXREG2AR; break; - case PS_TEXTUREMODES_DPNDNT_GB: Opcode = PO_TEXREG2GB; break; - case PS_TEXTUREMODES_DOTPRODUCT: - if (_NextIs2D(Stage)) - Opcode = PO_TEXM3X2PAD; - else - Opcode = PO_TEXM3X3PAD; - break; - case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST: Opcode = PO_TEXM3X3SPEC; break; // Note : Needs 3 arguments! - default: - continue; - } - - InsertTextureModeInstruction(pPSDef, Stage, Opcode, InsertIns, InsertPos); - Result = true; - } - if (Result) - { - for (unsigned i = 0; i < InsertIns.size(); ++i) - { - if (i >= XTL::X_D3DTS_STAGECOUNT || InsertIns[i].Opcode != PO_COMMENT) - { - InsertIntermediate(&InsertIns[i], InsertPos); - ++InsertPos; - } - } - } - StartPos = InsertPos + 1; - return Result; -} - -int PSH_XBOX_SHADER::GetTextureStageModifiers(int Stage) -{ - int modifiers = 0; - switch (PSDotMapping[Stage]) - { - case PS_DOTMAPPING_ZERO_TO_ONE: - break; - case PS_DOTMAPPING_MINUS1_TO_1_D3D: - modifiers = (1 << ARGMOD_SCALE_BX2); - break; - case PS_DOTMAPPING_MINUS1_TO_1_GL: - break; - case PS_DOTMAPPING_MINUS1_TO_1: - break; - case PS_DOTMAPPING_HILO_1: - break; - case PS_DOTMAPPING_HILO_HEMISPHERE: - break; - default: - break; - } - - return modifiers; -} - -void PSH_XBOX_SHADER::InsertTex3x2Instructions(int Stage, int inputStage, std::vector& InsertIns) -{ - PSH_INTERMEDIATE_FORMAT Ins = {}; - - const int modifiers = GetTextureStageModifiers(Stage); - - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 1, 0); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); - Ins.Parameters[1].Modifiers = modifiers; - InsertIns.emplace_back(Ins); - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 0, 0); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); - Ins.Parameters[1].Modifiers = modifiers; - InsertIns.emplace_back(Ins); -} - -void PSH_XBOX_SHADER::InsertTex3x3Instructions(int Stage, int inputStage, std::vector& InsertIns) -{ - PSH_INTERMEDIATE_FORMAT Ins = {}; - - const int modifiers = GetTextureStageModifiers(Stage); - - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 2, 0); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); - Ins.Parameters[1].Modifiers = modifiers; - InsertIns.emplace_back(Ins); - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 1, 0); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); - Ins.Parameters[1].Modifiers = modifiers; - InsertIns.emplace_back(Ins); - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); - Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 0, 0); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); - Ins.Parameters[1].Modifiers = modifiers; - InsertIns.emplace_back(Ins); -} - -bool PSH_XBOX_SHADER::InsertTextureModeInstruction(XTL::X_D3DPIXELSHADERDEF *pPSDef, int Stage, PSH_OPCODE opcode, std::vector& InsertIns, int& InsertPos) -{ - PSH_INTERMEDIATE_FORMAT Ins = {}; - - bool Result = false; - - PSH_ARGUMENT_TYPE type = PARAM_T; - int inputStage = Stage; - int mask = 0; - - // TODO: Refactor and optimize - // TODO: Update handling to support 1.4? - bool needsInitialization = false; - switch (opcode) - { - case PO_TEXBEM: - case PO_TEXBEML: - { - inputStage = PSInputTexture[Stage]; - - // If the bump-map texture format is X_D3DFMT_X8L8V8U8 or X_D3DFMT_L6V5U5 we need to apply a bias - // This happens because these formats are an alias of unsigned texture formats. - // Fixes an issue with the JSRF boost-dash effect - // NOTE: This assumes that this shader will only ever be used for the input bumpmap texture - // If this causes regressions in other titles, we'll need to be smarter about this - // and include the texture formats in the shader hash, somehow. - bool bias = false; - auto biasModifier = (1 << ARGMOD_SCALE_BX2); - auto pXboxTexture = g_pXbox_SetTexture[inputStage]; - if (pXboxTexture != nullptr) { - extern XTL::X_D3DFORMAT GetXboxPixelContainerFormat(const XTL::X_D3DPixelContainer *pXboxPixelContainer); // TODO : Move to XTL-independent header file - - switch (GetXboxPixelContainerFormat(pXboxTexture)) { - case XTL::X_D3DFMT_L6V5U5: { - extern XTL::X_D3DRESOURCETYPE GetXboxD3DResourceType(const XTL::X_D3DResource *pXboxResource); // TODO : Move to XTL-independent header file - extern bool IsSupportedFormat(XTL::X_D3DFORMAT X_Format, XTL::X_D3DRESOURCETYPE XboxResourceType, DWORD D3DUsage); // TODO : Move to XTL-independent header file - - // L6V5U5 format is converted incorrectly if not supported by the device - XTL::X_D3DRESOURCETYPE XboxResourceType = GetXboxD3DResourceType(pXboxTexture); - DWORD D3DUsage = 0; // TODO : Since it's not yet know how to determine D3DUsage in this case, 'hack' it by using no specific D3DUSAGE_* flags. - - bias = !IsSupportedFormat(/*XboxFormat=*/XTL::X_D3DFMT_L6V5U5, XboxResourceType, D3DUsage); - break; - } - case XTL::X_D3DFMT_X8L8V8U8: { - bias = true; - break; - } - } - } - - Ins.Initialize(PO_MAD); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT00, Stage, Recompiled); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_R); - - if (bias) { - Ins.Parameters[1].Modifiers = biasModifier; - } - - Ins.Parameters[2].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, MASK_R); - InsertIns.emplace_back(Ins); - Ins.Initialize(PO_MAD); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT10, Stage, Recompiled); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_G); - if (bias) { - Ins.Parameters[1].Modifiers = biasModifier; - } - Ins.Parameters[2].SetRegister(PARAM_R, 1, MASK_R); - InsertIns.emplace_back(Ins); - // - Ins.Initialize(PO_MAD); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT01, Stage, Recompiled); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_R); - if (bias) { - Ins.Parameters[1].Modifiers = biasModifier; - } - Ins.Parameters[2].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, MASK_G); - InsertIns.emplace_back(Ins); - Ins.Initialize(PO_MAD); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT11, Stage, Recompiled); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_G); - if (bias) { - Ins.Parameters[1].Modifiers = biasModifier; - } - Ins.Parameters[2].SetRegister(PARAM_R, 1, MASK_G); - InsertIns.emplace_back(Ins); - - Ins.CommentString = ""; - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - Ins.Parameters[1].Modifiers = 0; - InsertIns.emplace_back(Ins); - - if (opcode == PO_TEXBEML) - { - // - Ins.Initialize(PO_MAD); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); - Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVLSCALE, Stage, Recompiled); - Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_B); - Ins.Parameters[2].SetScaleBemLumRegister(D3DTSS_BUMPENVLOFFSET, Stage, Recompiled); - InsertIns.emplace_back(Ins); - // - Ins.Initialize(PO_MUL); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[1].SetRegister(PARAM_R, 1, MASK_B); - InsertIns.emplace_back(Ins); - } - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - - break; - } - case PO_TEXBRDF: - inputStage = PSInputTexture[Stage]; - break; - case PO_TEXM3X2TEX: - { - inputStage = PSInputTexture[Stage]; - - InsertTex3x2Instructions(Stage, inputStage, InsertIns); - - Ins.CommentString = ""; - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXM3X3TEX: - { - inputStage = PSInputTexture[Stage]; - - InsertTex3x3Instructions(Stage, inputStage, InsertIns); - - Ins.CommentString = ""; - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXM3X2DEPTH: - { - inputStage = PSInputTexture[Stage]; - - InsertTex3x2Instructions(Stage, inputStage, InsertIns); - - Ins.CommentString = ""; - Ins.Initialize(PO_RCP); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); - Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_G); - InsertIns.emplace_back(Ins); - - Ins.Initialize(PO_MUL); - Ins.Modifier = INSMOD_SAT; - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); - Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[1].SetRegister(PARAM_R, 1, MASK_B); - InsertIns.emplace_back(Ins); - - Ins.Initialize(PO_CMP); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); - Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); - Ins.Parameters[1].SetScaleConstRegister(1.0, Recompiled); - Ins.Parameters[2].SetRegister(PARAM_R, 1, MASK_B); - InsertIns.emplace_back(Ins); - - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_oDepth, 0, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_B); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXM3X3DIFF: - { - inputStage = PSInputTexture[Stage]; - - InsertTex3x3Instructions(Stage, inputStage, InsertIns); - - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXM3X3VSPEC: - case PO_TEXM3X3SPEC: - { - inputStage = PSInputTexture[Stage]; - - InsertTex3x3Instructions(Stage, inputStage, InsertIns); - - int baseRegister = PSH_XBOX_MAX_R_REGISTER_COUNT + PSH_XBOX_MAX_T_REGISTER_COUNT; - - // get eye-ray vector - Ins.Initialize(PO_COMMENT); - Ins.CommentString = "; get eye-ray vector"; - InsertIns.emplace_back(Ins); - if (opcode == PO_TEXM3X3VSPEC) - { - // E.x - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_T, Stage - 2, MASK_A); - InsertIns.emplace_back(Ins); - // E.y - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_T, Stage - 1, MASK_A); - InsertIns.emplace_back(Ins); - // E.z - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_B); - Ins.Parameters[0].SetRegister(PARAM_T, Stage - 0, MASK_A); - InsertIns.emplace_back(Ins); - // E.w - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_A); - Ins.Parameters[0].SetScaleConstRegister(0.0, Recompiled); - InsertIns.emplace_back(Ins); - } - else - { - // E - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, 0); - Ins.Parameters[0].SetRegister(PARAM_C, 0, 0); - InsertIns.emplace_back(Ins); - } - - // compute reflection vector - Ins.Initialize(PO_COMMENT); - Ins.CommentString = "; compute reflection vector"; - InsertIns.emplace_back(Ins); - // N.E - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_R, baseRegister + 0, 0); - InsertIns.emplace_back(Ins); - // 2 * (N.E) - Ins.Initialize(PO_MUL); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); - Ins.Parameters[1].SetScaleConstRegister(2.0, Recompiled); - InsertIns.emplace_back(Ins); - // N.N - Ins.Initialize(PO_DP3); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_R, 1, 0); - InsertIns.emplace_back(Ins); - // 1 / (N.N) - Ins.Initialize(PO_RCP); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_R, baseRegister + 1, MASK_G); - InsertIns.emplace_back(Ins); - // 2 * N.E / N.N - Ins.Initialize(PO_MUL); - Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); - Ins.Parameters[1].SetRegister(PARAM_R, baseRegister + 1, MASK_G); - InsertIns.emplace_back(Ins); - // 2 * N.E / N.N * N - E - Ins.Initialize(PO_MAD); - Ins.Output[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_R, baseRegister + 1, MASK_R); - Ins.Parameters[2].SetRegister(PARAM_R, baseRegister + 0, 0); - Ins.Parameters[2].Modifiers = (1 << ARGMOD_NEGATE); - InsertIns.emplace_back(Ins); - - Ins.CommentString = ""; - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXREG2AR: - { - inputStage = PSInputTexture[Stage]; - - // E.x - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_A); - InsertIns.emplace_back(Ins); - // E.y - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_R); - InsertIns.emplace_back(Ins); - - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXREG2GB: - { - inputStage = PSInputTexture[Stage]; - - // E.x - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); - Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_G); - InsertIns.emplace_back(Ins); - // E.y - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); - Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_B); - InsertIns.emplace_back(Ins); - - Ins.Initialize(PO_TEXLD2); - Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - InsertIns.emplace_back(Ins); - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - case PO_TEXM3X2PAD: - case PO_TEXM3X3PAD: - { - inputStage = PSInputTexture[Stage]; - - opcode = PO_MOV; - inputStage = Stage; - needsInitialization = true; - break; - } - - case PO_TEXLD: - case PO_TEXLD2: - case PO_TEXCRD: - case PO_MOV: - needsInitialization = true; - break; - default: - break; - } - - Ins.Initialize(opcode); - - if (needsInitialization) - { - type = PARAM_R; - - // Insert move instructions in reverse order to prevent overwriting wrong register - // Create instructions to move loaded temporary registers into extra temporary registers - InsertIns[XTL::X_D3DTS_STAGECOUNT - Stage - 1].Initialize(PO_MOV); - InsertIns[XTL::X_D3DTS_STAGECOUNT - Stage - 1].Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); - InsertIns[XTL::X_D3DTS_STAGECOUNT - Stage - 1].Parameters[0].SetRegister(PARAM_R, Stage, 0); - - if (Ins.Opcode == PO_TEXCRD) - { - mask = MASK_RGB; - } - else - { - } - - // Replace texture coordinate register usage up until first usage as output - int lastUsed = RegisterIsUsedFromIndexUntil(InsertPos, PARAM_T, Stage); - - if (lastUsed >= 0) - { - ReplaceInputRegisterFromIndexOnwards(InsertPos, PARAM_T, Stage, PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, lastUsed); - } - } - Ins.Output[0].SetRegister(type, Stage, mask); - - // For those texture modes that need it, add the source stage as argument : - if (PSH_OPCODE_DEFS[Ins.Opcode]._In >= 1) - { - Ins.Parameters[0].SetRegister(PARAM_T, inputStage, 0); - - if (Ins.Opcode >= PO_TEXDP3TEX && Ins.Opcode <= PO_TEXM3X3SPEC) - { - Ins.Parameters[0].Modifiers = GetTextureStageModifiers(Stage); - } - } - - if (PSH_OPCODE_DEFS[Ins.Opcode]._In >= 2) - { - if (Ins.Opcode == PO_TEXLD2) - { - Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); - } - - // Add the third argument : - switch (PSTextureModes[Stage]) { - case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST: - { - Ins.Parameters[1].SetRegister(PARAM_C, 0, 0); - Ins.CommentString = "Dxbx guess"; // TODO : Where do we get the 3rd argument to this? - break; - } - } - } - -// // Warn about unprocessed flag : -// if ((dwPS_GLOBALFLAGS & PS_GLOBALFLAGS_TEXMODE_ADJUST) > 0) -// Ins.CommentString = Ins.CommentString + " PS_GLOBALFLAGS_TEXMODE_ADJUST unhandled!"; - - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - Result = true; - - return Result; -} - -bool PSH_XBOX_SHADER::MoveRemovableParametersRight() -{ - int i; - - bool Result = false; - - // For all opcodes, try to put constant and discarded arguments in the rightmost slot, to ease following analysis : - i = IntermediateCount; - while (i > StartPos) - { - --i; - - switch (Intermediate[i].Opcode) { -// case PO_SUB: // 1-x is not the same as x-1, but can still be reduced - see SimplifySUB - case PO_ADD: - case PO_DP3: - case PO_DP4: - case PO_MUL: // All these opcodes have two swappable parameters, so try that : - if (Intermediate[i].MoveRemovableParametersRight(0, 1)) - Result = true; - break; - - case PO_XMMA: - case PO_XMMC: - case PO_XDD: - if (Intermediate[i].XMoveNonRegisterOutputsRight()) - Result = true; - break; - - case PO_XDM: - { - // Parameters may be swapped for both dot and mul, - // but the opcodes themselves may not, as we handle - // both XDM operations separately below : - if (Intermediate[i].MoveRemovableParametersRight(0, 1)) - Result = true; - - if (Intermediate[i].MoveRemovableParametersRight(2, 3)) - Result = true; - break; - } - } - } - return Result; -} // MoveRemovableParametersRight - -//bool PSH_XBOX_SHADER::ConvertConstantsToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled) - - void PSH_XBOX_SHADER::_SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLOR ConstColor) - { - D3DXCOLOR XColor; - - // Colors are defined in RGBA format, and range 0.0 - 1.0 (negative values - // can be obtained by supplying PS_INPUTMAPPING_SIGNED_NEGATE to the combiner - // that reads from these constants). - XColor = ConstColor; - NewIns.Parameters[0].SetConstValue(XColor.r); - NewIns.Parameters[1].SetConstValue(XColor.g); - NewIns.Parameters[2].SetConstValue(XColor.b); - NewIns.Parameters[3].SetConstValue(XColor.a); - } - - void PSH_XBOX_SHADER::_SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLORVALUE ConstColor) - { - NewIns.Parameters[0].SetConstValue(ConstColor.r); - NewIns.Parameters[1].SetConstValue(ConstColor.g); - NewIns.Parameters[2].SetConstValue(ConstColor.b); - NewIns.Parameters[3].SetConstValue(ConstColor.a); - } - - // Try to fixup constants above the limit (c7 for PS.1.3) : - int PSH_XBOX_SHADER::_MapConstant(int ConstNr, bool *NativeConstInUse) - { - // 1-to-1 mapping for constants that can be supported native (if not used already) : - if ((ConstNr < MaxConstantFloatRegisters) && (!NativeConstInUse[ConstNr])) - { - return ConstNr; - } - - // Assign not-yet-defined constants bottom-to-up : - int Result = 0; - while (Result < MaxConstantFloatRegisters) - { - if (!NativeConstInUse[Result]) - return Result; - - ++Result; - } - - // Unresolved - fallback to 1st constant : - if (Result >= MaxConstantFloatRegisters) - Result = 0; - - EmuLog(LOG_LEVEL::WARNING, "; Too many constants to emulate, this pixel shader will give unexpected output!"); - return Result; - } - - int PSH_XBOX_SHADER::_HandleConst(int XboxConst, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled, bool *NativeConstInUse, bool *EmittedNewConstant) - { - int NativeConst; - - if (!Recompiled->ConstInUse[XboxConst]) - { - // Determine and remember a new mapping to native : - NativeConst = _MapConstant(XboxConst, NativeConstInUse); - NativeConstInUse[NativeConst] = true; - Recompiled->ConstMapping[XboxConst] = NativeConst; - Recompiled->ConstInUse[XboxConst] = true; - // Make sure we can check this is a new constant (so we can emit a constant declaration - // for any final combiner constants - because those cannot be set via SetPixelShaderConstant) : - *EmittedNewConstant = true; - } - - // Return the (previously) determined mapping : - return Recompiled->ConstMapping[XboxConst]; - } - -bool PSH_XBOX_SHADER::ConvertConstantsToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled) -{ - int i, j; - PPSH_INTERMEDIATE_FORMAT Cur; - PPSH_IMD_ARGUMENT CurArg; - bool NativeConstInUse[224]; // Note : 224 = highest possible MaxConstantFloatRegisters - int16_t OriginalConstantNr; - bool EmittedNewConstant = false; - PSH_INTERMEDIATE_FORMAT NewIns = {}; - - bool Result = false; - - // Note : Recompiled.ConstMapping and Recompiled.ConstInUse[i] are still empty here. - for (i = 0; i < MaxConstantFloatRegisters; i++) - NativeConstInUse[i] = false; - - Result = true; - - NewIns.Initialize(PO_DEF); - - // Add constants used to represent common powers of 2 used by instruction and argument modifiers - // Represent constant 0.0 and common powers of 2 divisions - NewIns.Output[0].SetRegister(PARAM_C, _HandleConst(PSH_XBOX_CONSTANT_MUL1, Recompiled, &NativeConstInUse[0], &EmittedNewConstant), MASK_RGBA); - _SetColor(NewIns, { 0.0, 1.0 / 2.0, 1.0 / 4.0, 1.0 / 8.0 }); - InsertIntermediate(&NewIns, 1); - - // Represent common powers of 2 constants, also used as multipliers - NewIns.Output[0].SetRegister(PARAM_C, _HandleConst(PSH_XBOX_CONSTANT_MUL0, Recompiled, &NativeConstInUse[0], &EmittedNewConstant), MASK_RGBA); - _SetColor(NewIns, {1.0, 2.0, 4.0, 8.0}); - InsertIntermediate(&NewIns, 1); - - for (i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) - { - _HandleConst(PSH_XBOX_CONSTANT_BEM + i, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - _HandleConst(PSH_XBOX_CONSTANT_LUM + i, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - } - - // Loop over all opcodes to update the constant-indexes (Xbox uses C0 and C1 in each combiner) : - for (i = 0; i < IntermediateCount; i++) - { - // Loop over this opcodes' input arguments : - Cur = &(Intermediate[i]); - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) - { - // Only handle arguments that address a constant register : - CurArg = &(Cur->Parameters[j]); - - // The Fog register is not supported on PC so we convert it to a constant too : - // (But only if the MASK is not solely accessing the alpha-channel - we don't support that) - if (CurArg->Type == PARAM_FOG) - { - if (CurArg->Mask != MASK_A) - { - CurArg->Type = PARAM_C; - CurArg->Address = _HandleConst(PSH_XBOX_CONSTANT_FOG, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - CurArg->Mask = CurArg->Mask & (!MASK_A); - } - else - { - // Until we can get Alpha fog from the vertex shader somehow, - // set it to a constant value, so these shaders (like appearing - // in Dolphin samples) still compile and give reasonable output : - CurArg->SetConstValue(1.0); - Cur->CommentString = "FOG.a not emulated, using 1."; - } - - continue; - } - - if (CurArg->Type != PARAM_C) - continue; - - // Make sure we can detect new constants (and if it was C0 or C1), - // as we need this for fixing up final combiner constants : - EmittedNewConstant = false; - OriginalConstantNr = CurArg->Address; - - // For each constant being addressed, we find out which Xbox constant it is, - // and map it to a native constant (as far as we have space for them) : - switch (CurArg->Address) { - case 0: // Handle C0 (if present) : - { - // The final combiner has a separate C0 constant : - if (Cur->CombinerStageNr == XFC_COMBINERSTAGENR) - CurArg->Address = _HandleConst(PSH_XBOX_CONSTANT_FC0, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - else - { - // See if C0 has a unique index per combiner stage : - if (CombinerHasUniqueC0) - // C0 actually ranges from c0 to c7, one for each possible combiner stage (X_D3DRS_PSCONSTANT0_0..X_D3DRS_PSCONSTANT0_7) : - CurArg->Address = _HandleConst(Cur->CombinerStageNr, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - else - // Non-unique just reads the same C0 in every stage : - CurArg->Address = _HandleConst(0, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - } - break; - } - - case 1: // Handle C1 (if present) : - { - // The final combiner has a separate C1 constant : - if (Cur->CombinerStageNr == XFC_COMBINERSTAGENR) - CurArg->Address = _HandleConst(PSH_XBOX_CONSTANT_FC1, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - else - { - // See if C1 has a unique index per combiner stage : - if (CombinerHasUniqueC1) - // C1 actually ranges from c8 to c15, one for each possible combiner stage (X_D3DRS_PSCONSTANT1_0..X_D3DRS_PSCONSTANT1_7) : - CurArg->Address = _HandleConst(Cur->CombinerStageNr + 8, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - else - // Non-unique just reads the same C1 in every stage : - CurArg->Address = _HandleConst(1, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); - } - break; - } - } // switch - - // New constants solely used for the final combiner must be DEFined separately, - // as there's no other way to set these (SetPixelShaderConstant can only write - // to the 16 slots X_D3DRS_PSCONSTANT1_0..X_D3DRS_PSCONSTANT1_7) : - if ((Cur->CombinerStageNr == XFC_COMBINERSTAGENR) && EmittedNewConstant) - { - // Output a new opcode to define this constant : - NewIns.Initialize(PO_DEF); - NewIns.Output[0].SetRegister(PARAM_C, CurArg->Address, MASK_RGBA); - if (OriginalConstantNr == 0) - _SetColor(NewIns, pPSDef->PSFinalCombinerConstant0); - else - _SetColor(NewIns, pPSDef->PSFinalCombinerConstant1); - - // PO_DEF opcodes go after the initial PO_XPS (which is not yet replaced by PO_COMMENT+PO_PS, - // see ConvertXboxOpcodesToNative calling ConvertXPSToNative for that) - InsertIntermediate(&NewIns, 1); - Result = true; - } - } // for arguments - } // for opcodes - - return Result; -} // ConvertConstantsToNative - -bool PSH_XBOX_SHADER::RemoveUselessWrites() -// Note : Xbox allows writing to V0 (diffuse color) and V1 (specular color), but native ps.1.3 doesn't! -// Some examples of this behaviour can be seen when running RayMan Arena. -{ - int i, j; - PPSH_INTERMEDIATE_FORMAT Cur; - PPSH_IMD_ARGUMENT CurArg; - DWORD RegUsage[/*PSH_ARGUMENT_TYPE*/PARAM_C - PARAM_VALUE + 1][224] = {}; // 224 = highest possible PSH_PC_MAX_REGISTER_COUNT - - // TODO : In Polynomial Texture Maps, one extra opcode could be deleted (sub r1.rgb, v0,v0), why doesn't it? - bool Result = false; - - // Mark only R0 (and discard) as initially 'read', as these may not result in a removal : - RegUsage[PARAM_R][0] = MASK_RGBA; - for (i = 0; i < PSH_PC_MAX_REGISTER_COUNT; i++) - RegUsage[PARAM_DISCARD][i] = MASK_RGBA; - - i = IntermediateCount; - while (i > StartPos) - { - --i; - Cur = &(Intermediate[i]); - if (!Cur->IsArithmetic()) - continue; - - // Loop over the output arguments : - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) - { - CurArg = &(Cur->Output[j]); - - // Remove useless flag, to ease up later comparisions : - CurArg->Modifiers = CurArg->Modifiers & ~(1 << ARGMOD_IDENTITY); - - // Discard useless writes : - if ( (CurArg->Address < MaxTemporaryRegisters) - && ((RegUsage[CurArg->Type][CurArg->Address] & CurArg->Mask) == 0)) - { - EmuLog(LOG_LEVEL::DEBUG, "; Removed useless assignment to register %s", CurArg->ToString().c_str()); - CurArg->Type = PARAM_DISCARD; - Result = true; - } - } - - // Loop over the input arguments : - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) - { - CurArg = &(Cur->Parameters[j]); - // Skip non-register parameters : - if (!CurArg->UsesRegister()) - continue; - - // Remove useless flag, to ease up later comparisions : - CurArg->Modifiers = CurArg->Modifiers & ~(1 << ARGMOD_IDENTITY); - - // Keep track of all register reads, so that we can discard useless writes : - if (CurArg->Address < MaxTemporaryRegisters) - RegUsage[CurArg->Type][CurArg->Address] = RegUsage[CurArg->Type][CurArg->Address] | CurArg->Mask; - } - } - return Result; -} // RemoveUselessWrites - -void PSH_XBOX_SHADER::ConvertXboxOpcodesToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - std::string CommentString; - - // Do a bottom-to-top pass, converting all xbox opcodes into a native set of opcodes : - i = IntermediateCount; - while (i > 0) - { - --i; - Cur = &(Intermediate[i]); - - // Convert all Xbox opcodes into native opcodes : - CommentString = Cur->ToString(); - switch (Cur->Opcode) { - case PO_XPS: ConvertXPSToNative(i); break; - case PO_XMMA: ConvertXMMAToNative(i); break; - case PO_XMMC: ConvertXMMCToNative(i); break; - case PO_XDM: ConvertXDMToNative(i); break; - case PO_XDD: ConvertXDDToNative(i); break; - case PO_XFC: ConvertXFCToNative(i); break; // Can only occur once, as the last instruction - default: - CommentString = ""; break; - } - - if (!CommentString.empty()) { - PSH_INTERMEDIATE_FORMAT NewIns = {}; - NewIns.Initialize(PO_COMMENT)->CommentString = CommentString; - InsertIntermediate(&NewIns, i); - } - } -} // ConvertXboxOpcodesToNative - -void PSH_XBOX_SHADER::ConvertXPSToNative(int i) -{ - PPSH_INTERMEDIATE_FORMAT Cur; - - Cur = &(Intermediate[i]); - Cur->Opcode = PO_PS; -} - -bool PSH_XBOX_SHADER::ConvertXMMToNative_Except3RdOutput(int i) -{ - PPSH_INTERMEDIATE_FORMAT Cur; - int InsertPos; - PSH_INTERMEDIATE_FORMAT Ins = {}; - - bool Result = false; - Cur = &(Intermediate[i]); - InsertPos = i; - - // This block is meant for cases where XMMA/XMMC discards the 3rd output : - if (Cur->Output[2].Type == PARAM_DISCARD) - { - // Mark that this XMMA/XMMC opcode is already handled here : - Result = true; - - // The opcode must unconditionally change into a MUL (or two) : - Cur->Opcode = PO_MUL; - - // Is the second output ignored? - if (Cur->Output[1].Type == PARAM_DISCARD) - { - // If the first output is also ignored : - if (Cur->Output[0].Type == PARAM_DISCARD) - // The complete opcode can already be removed early on : - DeleteIntermediate(i); - else - ;// The first output is just a MUL, it's output (and first two parameters) are already in-place, so we're done - - return Result; - } - ++InsertPos; - - // Create a second MUL opcode for the second result : - Ins = *Cur; - Ins.XCopySecondOpcodeToFirst(PO_MUL); - InsertIntermediate(&Ins, InsertPos); - return Result; - } - - // The third output is needed, but what about the first and second output ? - - if (Cur->Output[0].Type == PARAM_DISCARD) - { - Cur->Output[0].Type = PARAM_T; - Cur->Output[0].Address = FakeRegNr_Xmm1; // 'r4' - } - - if (Cur->Output[1].Type == PARAM_DISCARD) - { - Cur->Output[1].Type = PARAM_T; - Cur->Output[1].Address = FakeRegNr_Xmm2; // 'r5' - } - - // Generate a MUL for the 1st output : - Ins = *Cur; - Ins.Opcode = PO_MUL; - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - - // Generate a MUL for the 2nd output : - Ins = *Cur; - Ins.XCopySecondOpcodeToFirst(PO_MUL); - InsertIntermediate(&Ins, InsertPos); - - // Note : If XMMA or XMMC writes to the third argument, we now have - // the first and second stored already (if they where not ignored). - // IF one (or both) are ignored, the intermediate result might be - // needed, but let XMMA/XMMC figure that out first - the resulting - // opcode(s) will probably require the initial opcode's removal! - return Result; -} // ConvertXMMToNative_Except3RdOutput - -void PSH_XBOX_SHADER::ConvertXMMAToNative(int i) -{ - PPSH_INTERMEDIATE_FORMAT Cur; - - // Handle the generic case of XMM, and check if the 3rd (Add) argument is ignored : - if (!ConvertXMMToNative_Except3RdOutput(i)) - { - // Add needs to be stored, we already have 2 MULs, so change the XMMA into an ADD : - Cur = &(Intermediate[i+2]); - Cur->Opcode = PO_ADD; - Cur->Modifier = INSMOD_NONE; - Cur->Parameters[0] = Cur->Output[0]; - Cur->Parameters[1] = Cur->Output[1]; - Cur->Output[0] = Cur->Output[2]; - } -} - -void PSH_XBOX_SHADER::ConvertXMMCToNative(int i) -{ - PPSH_INTERMEDIATE_FORMAT Cur; - - // Handle the generic case of XMM, and check if the 3rd (Compare) argument is ignored : - if (!ConvertXMMToNative_Except3RdOutput(i)) - { - // Add needs to be stored, we already have 2 MULs, so change the XMMC into an CND : - Cur = &(Intermediate[i+2]); - // TODO : If CombinerMuxesOnMsb is False, we should compare to the LeastSignificantBit of r0.a - but how? - Cur->Opcode = PO_CND; - Cur->Modifier = INSMOD_NONE; - // Begin the input of CND with the required r0.a parameter : - Cur->Parameters[0].SetRegister(PARAM_R, 0, MASK_A); - Cur->Parameters[0].Modifiers = (1 << ARGMOD_IDENTITY); - Cur->Parameters[0].Multiplier = 1.0; - // Follow that with the 2 selection registers : - Cur->Parameters[1] = Cur->Output[0]; - Cur->Parameters[2] = Cur->Output[1]; - // And put the result it in the final register : - Cur->Output[0] = Cur->Output[2]; - } -} - -void PSH_XBOX_SHADER::ConvertXDMToNative(int i) -{ - PPSH_INTERMEDIATE_FORMAT Cur; - PSH_INTERMEDIATE_FORMAT Ins = {}; - - Cur = &(Intermediate[i]); - - // XDM does two operations : - - // a multiply : - if (Cur->Output[1].Type != PARAM_DISCARD) - { - Ins = *Cur; - Ins.XCopySecondOpcodeToFirst(PO_MUL); - InsertIntermediate(&Ins, i+1); - } - - // and a dot product : - if (Cur->Output[0].Type == PARAM_DISCARD) - DeleteIntermediate(i); - else - Cur->Opcode = PO_DP3; -} - -void PSH_XBOX_SHADER::ConvertXDDToNative(int i) -{ - PPSH_INTERMEDIATE_FORMAT Cur; - PSH_INTERMEDIATE_FORMAT Ins = {}; - - Cur = &(Intermediate[i]); - - // XDD does two operations : - - // ...a dot product : - Cur->Opcode = PO_DP3; - - // and another dot product : - if (Cur->Output[1].Type != PARAM_DISCARD) - { - Ins = *Cur; - Ins.XCopySecondOpcodeToFirst(PO_DP3); - InsertIntermediate(&Ins, i+1); - } -} - -void PSH_XBOX_SHADER::ConvertXFCToNative(int i) -{ - PSH_INTERMEDIATE_FORMAT Cur = {}; - int InsertPos; - bool NeedsProd; - bool NeedsSum; - PPSH_IMD_ARGUMENT CurArg; - PSH_INTERMEDIATE_FORMAT Ins = {}; - - // Get a copy of XFC and remove it already, new instructions will replace it : - Cur = Intermediate[i]; - DeleteIntermediate(i); - InsertPos = i; - // 'final combiner - r0 = A*B + (1-A)*C + D'; - - // See if the final combiner uses the prod or sum input parameters : - NeedsProd = false; - NeedsSum = false; - for (i = 0; i < PSH_OPCODE_DEFS[Cur.Opcode]._In; i++) - { - CurArg = &(Cur.Parameters[i]); - - // Check for the three final-combiner-specific argument types : - switch (CurArg->Type) { - case PARAM_V1R0_SUM: - { - // Change SUM into a fake register, which will be resolved later : - CurArg->Type = PARAM_T; - CurArg->Address = FakeRegNr_Sum; // 'r2' - NeedsSum = true; - break; - } - - case PARAM_EF_PROD: - { - // Change PROD into a fake register, which will be resolved later : - CurArg->Type = PARAM_T; - CurArg->Address = FakeRegNr_Prod; // 'r3' - NeedsProd = true; - break; - } - - case PARAM_FOG: - { - // Change FOG into a constant of 1.0, as we can't simulate it otherwise : -// CurArg->SetConstValue(1.0); -// Cur->CommentString = "final combiner - FOG not emulated, using 1."; - break; - } - } - } // for input - - if (NeedsSum) - { - // Add a new opcode that calculates r0*v1 : - Ins.Initialize(PO_MUL); - Ins.Output[0].SetRegister(PARAM_T, FakeRegNr_Sum, MASK_RGBA); // 'r2' - - Ins.Parameters[0].SetRegister(PARAM_R, 0, MASK_RGB); - Ins.Parameters[1].SetRegister(PARAM_V, 1, MASK_RGB); - - // Take the FinalCombinerFlags that influence this result into account : - if ((FinalCombinerFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0) > 0) - Ins.Parameters[0].Modifiers = (1 << ARGMOD_INVERT); // (1-r0) is used as an input to the sum rather than r0 - if ((FinalCombinerFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_V1) > 0) - Ins.Parameters[1].Modifiers = (1 << ARGMOD_INVERT); // (1-v1) is used as an input to the sum rather than v1 - if ((FinalCombinerFlags & PS_FINALCOMBINERSETTING_CLAMP_SUM) > 0) - Ins.Modifier = INSMOD_SAT; // V1+R0 sum clamped to [0,1] - - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted final combiner calculation of V1R0_sum register"); - } - - if (NeedsProd) - { - // Add a new opcode that calculates E*F : - Ins.Initialize(PO_MUL); - Ins.Output[0].SetRegister(PARAM_T, FakeRegNr_Prod, MASK_RGBA); // 'r3' - Ins.Parameters[0] = Cur.Parameters[4]; // E - Ins.Parameters[1] = Cur.Parameters[5]; // F - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted final combiner calculation of EF_prod register"); - } - - // The final combiner calculates : r0.rgb=s0*s1 + (1-s0)*s2 + s3 - // Change that into a LRP + ADD, and let the optimizer reduce it; - - // Add a new opcode that calculates r0.rgb=s0*s1 + (1-s0)*s2 via a LRP : - // Set the output to r0.rgb (as r0.a is determined via s6.a) : - - // Watch out! If s3=r0.rgb, then the LRP cannot use r0, but must use r1 as temp! - if (Cur.Parameters[3].IsRegister(PARAM_R, 0, 0)) - Cur.Output[0].SetRegister(PARAM_R, 1, MASK_RGB); - else - Cur.Output[0].SetRegister(PARAM_R, 0, MASK_RGB); - - Ins = Cur; - Ins.Opcode = PO_LRP; - Ins.Modifier = INSMOD_NONE; - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - - // Add a new opcode that calculates r0.rgb=r0.rgb+s3 : - Ins.Opcode = PO_ADD; - Ins.Modifier = Cur.Modifier; - Ins.Output[0] = Cur.Output[0]; // = r0.rgb - Ins.Parameters[0] = Cur.Output[0]; // = r0.rgb - Ins.Parameters[1] = Cur.Parameters[3]; // =s3 from XFC - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - - // See if s6 is something else than "r0.a" : - if (Cur.Parameters[6].ToString() != "r0.a") - { - // Add a new opcode that moves s6 over to r0.a : - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, 0, MASK_A); - Ins.Parameters[0] = Cur.Parameters[6]; - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - } -} - -bool PSH_XBOX_SHADER::RemoveNops() -{ - int i, j; - PPSH_INTERMEDIATE_FORMAT Cur; - bool HasOutput; - - bool Result = false; - i = IntermediateCount; - while (i > StartPos) - { - --i; - Cur = &(Intermediate[i]); - - // Skip opcodes that have no output, but should stay anyway : - if (PSH_OPCODE_DEFS[Cur->Opcode]._Out == 0) - if (Cur->Opcode != PO_NOP) - continue; - - // See if this opcode writes to any of it's outputs : - { - HasOutput = false; - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) - if (Cur->Output[j].Type != PARAM_DISCARD) - { - HasOutput = true; - break; - } - - if (!HasOutput) - { - // Remove the opcode (as it doesn't change anything) : - // This applies to PO_NOP and opcodes that discard all their results : - DeleteIntermediate(i); - Result = true; - continue; - } - } - } - return Result; -} - -int PSH_XBOX_SHADER::MaxRegisterCount(PSH_ARGUMENT_TYPE aRegType) -{ - switch (aRegType) - { - case PARAM_R: - return MaxTemporaryRegisters; - case PARAM_T: - return MaxTextureCoordinateRegisters; - case PARAM_V: - return MaxInputColorRegisters; - case PARAM_C: - return MaxConstantFloatRegisters; - case PARAM_S: - return MaxSamplerRegisters; - } - - return 0; -} - -bool PSH_XBOX_SHADER::IsValidNativeOutputRegister(PSH_ARGUMENT_TYPE aRegType, int index /*= -1*/) -{ - bool valid = (PARAM_R == aRegType) && (MaxRegisterCount(PARAM_R) > index); - - return valid; -} - -int PSH_XBOX_SHADER::RegisterIsFreeFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - - for (i = aIndex; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - // Detect a read : - if (Cur->ReadsFromRegister(aRegType, aAddress)) - { - return -1; - } - // Detect a write : - if (Cur->WritesToRegister(aRegType, aAddress)) - { - break; - } - } - - return i; -} - -int PSH_XBOX_SHADER::RegisterIsUsedFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) -{ - int result = -1; - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - - for (i = aIndex; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - // Detect a read : - if (Cur->ReadsFromRegister(aRegType, aAddress)) - { - result = i; - } - // Detect a write : - if (Cur->WritesToRegister(aRegType, aAddress)) - { - break; - } - } - - return result; -} - -int PSH_XBOX_SHADER::NextFreeRegisterFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int bIndex /*= -1*/, int startAddress /*= 0*/, int excludeAddress /*= -1*/) -{ - const int registerCount = MaxRegisterCount(aRegType); - - if (bIndex < 0 || bIndex < aIndex) - bIndex = IntermediateCount; - - if (startAddress < 0) - startAddress = 0; - - int i; - - for (i = startAddress; i < registerCount; i++) - { - if (i == excludeAddress) - continue; - - if (RegisterIsFreeFromIndexUntil(aIndex, aRegType, i) >= bIndex) - { - return i; - } - } - - return -1; -} - -bool PSH_XBOX_SHADER::IsRegisterFreeFromIndexOnwards(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - - for (i = aIndex; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - // Detect a write or read : - if (Cur->WritesToRegister(aRegType, aAddress) - || Cur->ReadsFromRegister(aRegType, aAddress)) - { - return false; - } - } - - return true; -} - -void PSH_XBOX_SHADER::ReplaceInputRegisterFromIndexOnwards(int aIndex, - PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, - PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex /*= -1*/) -{ - ReplaceRegisterFromIndexOnwards(aIndex, aSrcRegType, aSrcAddress, aDstRegType, aDstAddress, endIndex, true, false); -} - -void PSH_XBOX_SHADER::ReplaceOutputRegisterFromIndexOnwards(int aIndex, - PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, - PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex /*= -1*/) -{ - ReplaceRegisterFromIndexOnwards(aIndex, aSrcRegType, aSrcAddress, aDstRegType, aDstAddress, endIndex, false, true); -} - -void PSH_XBOX_SHADER::ReplaceRegisterFromIndexOnwards(int aIndex, - PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, - PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex /*= -1*/, bool replaceInput /*= true*/, bool replaceOutput /*= true*/) -{ - int i; - int j; - PPSH_INTERMEDIATE_FORMAT Cur; - - for (i = aIndex; i < IntermediateCount && (i <= endIndex || endIndex == -1); i++) - { - Cur = &(Intermediate[i]); - - if (replaceOutput) - { - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) - if (Cur->Output[j].IsRegister(aSrcRegType, aSrcAddress)) - Cur->Output[j].SetRegister(aDstRegType, aDstAddress, Cur->Output[j].Mask); - } - - if (replaceInput) - { - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) - if (Cur->Parameters[j].IsRegister(aSrcRegType, aSrcAddress)) - Cur->Parameters[j].SetRegister(aDstRegType, aDstAddress, Cur->Parameters[j].Mask); - } - } -} - -bool PSH_XBOX_SHADER::FixArgumentModifiers() -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - - bool Result = false; - - // Do a bottom-to-top pass, preventing constant-modifiers via additional MOV's: - i = IntermediateCount; - while (i > 0) - { - --i; - Cur = &(Intermediate[i]); - if (Cur->Opcode < PO_TEX) // TODO : Check explicitly which instruction types are handled below - continue; - - int InsertPos = i; - // Detect modifiers on constant and arguments - for (int p = 0; p < 7 && p < PSH_OPCODE_DEFS[Cur->Opcode]._In; p++) { - if ((Cur->Parameters[p].Type == PARAM_C || Cur->Parameters[p].UsesRegister()) - && ((Cur->Parameters[p].Modifiers & ~(1 << ARGMOD_NEGATE)) != 0)) { - - PSH_INTERMEDIATE_FORMAT Ins = {}; - PSH_IMD_ARGUMENT Arg = {}; - - Arg = Cur->Parameters[p]; - - int excludeAddress = Cur->Output[0].Type == PARAM_R ? Cur->Output[0].Address : -1; - - PSH_ARGUMENT_TYPE type = PARAM_R; - int address = NextFreeRegisterFromIndexUntil(InsertPos, PARAM_R, InsertPos, 0, excludeAddress); - - if (IsValidNativeOutputRegister(Arg.Type, Arg.Address) && RegisterIsFreeFromIndexUntil(InsertPos + 1, Arg.Type, Arg.Address) > InsertPos) - { - type = Arg.Type; - address = Arg.Address; - } - - for (int modifier = ARGMOD_INVERT; modifier < ARGMOD_SATURATE; ++modifier) - { - Arg = Cur->Parameters[p]; - - if (!Arg.HasModifier((PSH_ARG_MODIFIER)modifier)) - continue; - - bool needInsert = false; - switch ((PSH_ARG_MODIFIER)modifier) - { - case ARGMOD_INVERT: - { - if (Arg.HasModifier(ARGMOD_NEGATE)) - { - Ins.Initialize(PO_SUB); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[1].SetScaleConstRegister(1.0f, Recompiled); - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'invert' with 'negate' argument modifier (register - 1)"; - ++modifier; - } - else - { - Ins.Initialize(PO_SUB); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[0].SetScaleConstRegister(1.0f, Recompiled); - Ins.Parameters[1] = Cur->Parameters[p]; - Ins.Parameters[1].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'invert' argument modifier (1 - register)"; - } - needInsert = true; - - break; - } - case ARGMOD_NEGATE: - { - // Skip as this modifier is still supported in current shader models - // Included here for completeness - break; - Ins.Initialize(PO_MOV); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); - Ins.CommentString = "Inserted to replace 'negate' argument modifier (-register)"; - needInsert = true; - - break; - } - case ARGMOD_BIAS: - { - Ins.Initialize(PO_SUB); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'bias' argument modifier (register - 0.5)"; - needInsert = true; - - break; - } - case ARGMOD_SCALE_X2: - { - Ins.Initialize(PO_MUL); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'x2' argument modifier (2 * register)"; - needInsert = true; - - break; - } - case ARGMOD_SCALE_BX2: - { - Ins.Initialize(PO_MAD); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[2].SetScaleConstRegister(-1.0f, Recompiled); - Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'bx2' argument modifier (2 * register - 1)"; - needInsert = true; - - break; - } - case ARGMOD_SCALE_X4: - { - Ins.Initialize(PO_MUL); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[1].SetScaleConstRegister(4.0f, Recompiled); - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'x4' argument modifier (4 * register)"; - needInsert = true; - - break; - } - case ARGMOD_SCALE_D2: - { - Ins.Initialize(PO_MUL); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace 'd2' argument modifier (0.5 * register)"; - needInsert = true; - - break; - } - default: - { - Ins.Initialize(PO_MOV); - // No need to check if output is a constant - those cannot be assigned to anyway - Ins.Output[0].SetRegister(type, address, Arg.Mask); - // Move constant into register - Ins.Parameters[0] = Cur->Parameters[p]; - Ins.Parameters[0].Modifiers = 0; - Ins.CommentString = "Inserted to replace argument with modifier"; - needInsert = true; - - break; - } - } - - if (needInsert == true) - { - for (int q = p; q < PSH_OPCODE_DEFS[Cur->Opcode]._In; q++) - { - // overwrite all matching parameters to avoid duplicate instructions - if (Arg.Type == Cur->Parameters[q].Type - && Arg.Address == Cur->Parameters[q].Address - && Arg.Mask == Cur->Parameters[q].Mask - && Arg.Modifiers == Cur->Parameters[q].Modifiers - && Arg.Multiplier == Cur->Parameters[q].Multiplier) - { - Cur->Parameters[q] = Ins.Output[0]; - // Apply modifier to register instead of constant - Cur->Parameters[q].Modifiers = (Arg.Modifiers & (1 << ARGMOD_NEGATE)) | (Arg.Modifiers & (~0 << (modifier + 1))); - } - } - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - ++Cur; - EmuLog(LOG_LEVEL::DEBUG, "; Used intermediate move to avoid argument modifier"); - Result = true; - } - } - } - } - } - return Result; -} // FixArgumentModifiers - -bool PSH_XBOX_SHADER::FixConstantParameters() -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - - bool Result = false; - - // Do a bottom-to-top pass, preventing constant-modifiers via additional MOV's: - i = IntermediateCount; - while (i > StartPos) - { - --i; - Cur = &(Intermediate[i]); - - if (!Cur->IsArithmetic()) - continue; - - for (int p = 0; p < PSH_OPCODE_DEFS[Cur->Opcode]._In; ++p) - { - if (Cur->Parameters[p].Type != PARAM_VALUE) - continue; - - if (Cur->Parameters[p].SetScaleConstRegister(Cur->Parameters[p].GetConstValue(), Recompiled)) - { - EmuLog(LOG_LEVEL::DEBUG, "; Replaced constant value with constant register"); - Result = true; - } - } - } - return Result; -} // FixConstantParameters - -bool PSH_XBOX_SHADER::FixInstructionModifiers() -{ - int i; - int InsertPos; - PPSH_INTERMEDIATE_FORMAT Cur; - PSH_INTERMEDIATE_FORMAT Ins = {}; - - bool Result = false; - - // Do a bottom-to-top pass, preventing constant-modifiers via additional MOV's: - i = IntermediateCount; - while (i > StartPos) - { - InsertPos = i; - --i; - Cur = &(Intermediate[i]); - - if (!Cur->IsArithmetic()) - continue; - - bool insert = true; - switch (Cur->Modifier) - { - case INSMOD_BIAS: // y = x - 0.5 // Xbox only : TODO : Fixup occurrances! - { - Ins.Initialize(PO_SUB); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_bias"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_bias"); - break; - } - case INSMOD_X2: // y = x * 2 - { - Ins.Initialize(PO_MUL); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_x2"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_x2"); - break; - } - case INSMOD_BX2: // y = (x - 0.5) * 2 // Xbox only : TODO : Fixup occurrances! - { - Ins.Initialize(PO_MAD); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); - Ins.Parameters[2].SetScaleConstRegister(-1.0f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_bx2"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_bx2"); - break; - } - case INSMOD_X4: // y = x * 4 - { - Ins.Initialize(PO_MUL); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(4.0f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_x4"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_x4"); - break; - } - case INSMOD_D2: // y = x * 0.5 - { - Ins.Initialize(PO_MUL); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_d2"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_d2"); - break; - } - case INSMOD_X8: // y = x * 8 // ps 1.4 only - { - Ins.Initialize(PO_MUL); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(8.0f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_x8"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_x8"); - break; - } - case INSMOD_D4: // y = x * 0.25 // ps 1.4 only - { - Ins.Initialize(PO_MUL); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(0.25f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_d4"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_d4"); - break; - } - case INSMOD_D8: // y = x * 0.125 // ps 1.4 only - { - Ins.Initialize(PO_MUL); - Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; - Ins.Parameters[1].SetScaleConstRegister(0.125f, Recompiled); - Ins.CommentString = "; Inserted adjustment by constant register for INST_d8"; - EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_d8"); - break; - } - case INSMOD_SAT: // Xbox doesn"t support this, but has ARGMOD_SATURATE instead - case INSMOD_NONE: // y = x - default: - insert = false; - break; - } - - if (insert) - { - Cur->Modifier = INSMOD_NONE; - InsertIntermediate(&Ins, InsertPos); - Result = true; - } - } - return Result; -} // FixInstructionModifiers - -bool PSH_XBOX_SHADER::FinalizeShader() -{ - PSH_INTERMEDIATE_FORMAT Ins = {}; - - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_oC, 0, MASK_RGBA); - Ins.Parameters[0].SetRegister(PARAM_R, 0, MASK_RGBA); - InsertIntermediate(&Ins, IntermediateCount); - - return true; -} // FinalizeShader - -//bool PSH_XBOX_SHADER::CombineInstructions() - - bool _CanLerp(PPSH_INTERMEDIATE_FORMAT Mul1, PPSH_INTERMEDIATE_FORMAT Mul2, PPSH_INTERMEDIATE_FORMAT AddOpcode, int Left, int Right) - { - PPSH_IMD_ARGUMENT ParamLeft, ParamRight; - - // Check if Left and Right are the same register : - ParamLeft = &(Mul1->Parameters[Left]); - ParamRight = &(Mul2->Parameters[Right]); - if ((ParamLeft->Type != ParamRight->Type) - || (ParamLeft->Address != ParamRight->Address) - || (ParamLeft->Mask != ParamRight->Mask)) - return false; - - // Is the left argument inverted and the right not (or the other way around) ? - if (ParamLeft->HasModifier(ARGMOD_INVERT) != ParamRight->HasModifier(ARGMOD_INVERT)) - { - // In that case, already move the arguments over to AddOpcode so we create a LRP : - AddOpcode->Parameters[0] = *ParamLeft; - AddOpcode->Parameters[1] = Mul1->Parameters[1-Left]; - AddOpcode->Parameters[2] = Mul2->Parameters[3-Right]; - return true; - } - return false; - } - - bool _CanMad(int ConstOne, PPSH_INTERMEDIATE_FORMAT Mul1, PPSH_INTERMEDIATE_FORMAT Mul2, PPSH_INTERMEDIATE_FORMAT AddOpcode) - { - // Check if the given parameter is 1 : - bool Result = Mul1->Parameters[ConstOne].GetConstValue() == 1.0; - if (Result) - { - // Put the other 3 parameters int the resulting opcode, so we can make it a MAD : - AddOpcode->Parameters[0] = Mul2->Parameters[0]; - AddOpcode->Parameters[1] = Mul2->Parameters[1]; - AddOpcode->Parameters[2] = Mul1->Parameters[1-ConstOne]; - } - return Result; - } - -bool PSH_XBOX_SHADER::CombineInstructions() -{ - int i; - PPSH_INTERMEDIATE_FORMAT Op0; - PPSH_INTERMEDIATE_FORMAT Op1; - PPSH_INTERMEDIATE_FORMAT Op2; - bool CanOptimize; - int j; - int k; - - bool Result = false; - - i = IntermediateCount - 1; - while (i > StartPos) - { - --i; - Op0 = &(Intermediate[i+0]); - Op1 = &(Intermediate[i+1]); - Op2 = &(Intermediate[i+2]); - - // Check if there are two consecutive opcodes reading from a fake R register; - // We outputted these ourselves, in order to ease the conversion and profit - // from having generic optimizations in one place : - if ( (Op0->Output[0].Type == PARAM_T) - && (Op0->Output[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT) - && (Op1->Output[0].Type == PARAM_T) - && (Op1->Output[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT)) - { - // Did we output those from a CND opcode (originally XMMC) ? - if (Op2->Opcode == PO_CND) - { - if ( (Op0->Opcode == PO_MOV) - && (Op1->Opcode == PO_MOV) - && (Op1->Modifier == Op0->Modifier)) - { - Op2->Modifier = Op0->Modifier; - Op2->Parameters[1] = Op0->Parameters[0]; - Op2->Parameters[2] = Op1->Parameters[0]; - DeleteIntermediate(i); - DeleteIntermediate(i); - EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,CND via MOV,MOV,CND into a single CND"); - Result = true; - continue; - } - } - - // Did we output those from a ADD opcode (originally XMMA) ? - if (Op2->Opcode == PO_ADD) - { - if ( (Op0->Opcode == PO_MUL) - && (Op1->Opcode == PO_MUL) - && (Op1->Modifier == Op0->Modifier)) - { - // Check if we can lerp - we just need the same register on both sides that's inverted on the other : - if (_CanLerp(Op0, Op1, Op2, 0, 2) - || _CanLerp(Op0, Op1, Op2, 1, 2) - || _CanLerp(Op0, Op1, Op2, 0, 3) - || _CanLerp(Op0, Op1, Op2, 1, 3)) - { - // The lerp can be done, and the correct parameters are already set to Op2, - // so all we need to do now, it fixup the rest and remove the two MOV's : - Op2->Opcode = PO_LRP; - Op2->Modifier = Op0->Modifier; - DeleteIntermediate(i); - DeleteIntermediate(i); - EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a single LRP"); - Result = true; - continue; - } - - // Check if we can mad - we just need a constant 1 in one argument : - if (_CanMad(0, Op0, Op1, Op2) - || _CanMad(1, Op0, Op1, Op2) - || _CanMad(0, Op1, Op0, Op2) - || _CanMad(1, Op1, Op0, Op2)) - { - // The mad can be done, and the correct parameters are already set to Op2, - // so all we need to do now, it fixup the rest and remove the two MOV's : - Op2->Opcode = PO_MAD; - Op2->Modifier = Op0->Modifier; - DeleteIntermediate(i); - DeleteIntermediate(i); - EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a single MAD"); - Result = true; - continue; - } - - // No single opcode possible, so change it into a MUL + MAD : - // The first mul may write to the last output register (without a modifier) : - Op0->Modifier = INSMOD_NONE; - Op0->Output[0] = Op2->Output[0]; - // Change the second MUL into a MAD : - Op1->Opcode = PO_MAD; - Op1->Output[0] = Op2->Output[0]; - Op1->Parameters[2] = Op0->Output[0]; - // Remove the trailing ADD : - DeleteIntermediate(i+2); - EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a MUL,MAD"); - Result = true; - continue; - } - - // Was it a MUL,MUL,ADD? - if ( (Op0->Opcode == PO_MUL) - && (Op1->Opcode == PO_MUL) - && (Op0->Parameters[1].GetConstValue() == 1.0) - && (Op1->Parameters[1].GetConstValue() == 1.0)) - { - // Remove the two MOV's and fold their arguments into a MUL : - Op2->Opcode = PO_MUL; - Op2->Parameters[0] = Op0->Parameters[0]; - Op2->Parameters[1] = Op1->Parameters[0]; - DeleteIntermediate(i); - DeleteIntermediate(i); - EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a MUL"); - Result = true; - continue; - } - } - } - - // Do two neighbouring opcodes output to the same register (without a modifier) ? - if ( (Op0->Output[0].ToString() == Op1->Output[0].ToString()) - && (Op0->Modifier == INSMOD_NONE) - && (Op1->Modifier == INSMOD_NONE)) - { - // Is it MUL,ADD ? - if ( (Op0->Opcode == PO_MUL) - && (Op1->Opcode == PO_ADD)) - { - // Is the output of the MUL input to the ADD ? - if ( (Op0->Output[0].Type == Op1->Parameters[0].Type) - && (Op0->Output[0].Address == Op1->Parameters[0].Address) - && (Op0->Output[0].Modifiers == Op1->Parameters[0].Modifiers)) - // Mask and Multiplier are not important here - { - Op0->Opcode = PO_MAD; - Op0->Parameters[2] = Op1->Parameters[1]; - DeleteIntermediate(i+1); - EmuLog(LOG_LEVEL::DEBUG, "; Changed MUL,ADD into a single MAD"); - Result = true; - continue; - } - } - } - -/* - // Combinations that can be made if their intermediate result is not read again or overwritten later: - - MOV+ADD > ADD (if MOV.Output[0] was only read by ADD.Parameter[0] or ADD.Parameter[1]) - MOV+SUB > SUB (if MOV.Output[0] was only read by SUB.Parameter[0] or SUB.Parameter[1]) - MOV+MUL > MUL (if MOV.Output[0] was only read by MOV.Parameter[0] or MOV.Parameter[1]) - - MUL+MOV > MUL (if MUL.Output[0] was only read by MOV.Parameter[0]) - MUL+ADD > MAD (if MUL.Output[0] was only read by ADD.Parameter[0] or ADD.Parameter[1]) - MUL+SUB > MAD (if MUL.Output[0] was only read by SUB.Parameter[0] - Do invert MAD.Parameter[2]) -*/ - - // We can remove a MOV entirely if the input is not changed while - // the output is read, up until the output is re-written; We can change all - // these occurances into a read from the input of this MOV instead : - // This fixes some shaders in Turok, that are reduced to 8 instead of 9 opcodes. - if ( (Op0->Opcode == PO_MOV) - && (Op0->Modifier == INSMOD_NONE) - && (Op0->Output[0].Mask == MASK_RGBA)) - { - CanOptimize = false; - j = i + 1; - while (j < IntermediateCount) - { - // Don't optimize if the output is needed for CND or CMP (which must read from r0) : - // This fixes : "(Validation Error) First source for cnd instruction must be 'r0.a'" in Modify Pixel Shader XDK sample. - if ( ((Intermediate[j].Opcode == PO_CND) || (Intermediate[j].Opcode == PO_CMP)) - && (Op0->Output[0].IsRegister(PARAM_R, 0))) - break; - - // TODO : Add other prevention rules here (like too many texture-reads, and other scases) - - // We can optimize if the MOV-output is written to again before the end of the shader : - CanOptimize = true; - - // ensure this is not "constant with modifier" optimization pattern to prevent infinite loop - for (int p = 0; p < PSH_OPCODE_DEFS[Intermediate[j].Opcode]._In; p++) - { - if ((Op0->Parameters[0].Type == PARAM_C) - && (Intermediate[j].Parameters[p].Type == Op0->Output[0].Type) - && (Intermediate[j].Parameters[p].Address == Op0->Output[0].Address) - && (Intermediate[j].Parameters[p].Modifiers != 0)) - { - CanOptimize = false; - break; - } - }; - - if (Intermediate[j].WritesToRegister(Op0->Output[0].Type, Op0->Output[0].Address, MASK_RGBA)) - break; - - CanOptimize = false; - ++j; - } - - if (CanOptimize) - { - // Loop over all instructions in between, and try to replace reads : - CanOptimize = false; - while (j > i) - { - // For Intermediate[j].Parameters, change all occurrances of Op0.Output[0] into Op0.Parameters[0] : - for (k = 0; k < PSH_OPCODE_DEFS[Intermediate[j].Opcode]._In; k++) - if ( (Intermediate[j].Parameters[k].Type == Op0->Output[0].Type) - && (Intermediate[j].Parameters[k].Address == Op0->Output[0].Address)) - { - Intermediate[j].Parameters[k].Type = Op0->Parameters[0].Type; - Intermediate[j].Parameters[k].Address = Op0->Parameters[0].Address; - // Signal that a replacement is actually done : - CanOptimize = true; - } - - --j; - } - - if (CanOptimize) - { - DeleteIntermediate(i); - EmuLog(LOG_LEVEL::DEBUG, "; Moved MOV input into following instructions"); - Result = true; - } - } - } - - // Fix Dolphin : - // mul r3, r0,t0 ; d0=s0*s1 - // mov r0.rgb, r3 ; d0=s0 final combiner - FOG not emulated, using 1. - if ( (Op0->Output[0].Type == PARAM_T) - && (Op0->Output[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT) - && (Op1->Parameters[0].Type == PARAM_T) - && (Op1->Parameters[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT)) - { - if ( (Op0->Opcode == PO_MUL) - && (Op1->Opcode == PO_MOV)) - { - // > mul r0.rgb, r0,t0 - Op0->Output[0] = Op1->Output[0]; - DeleteIntermediate(i+1); - EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MOV into a MUL"); - Result = true; - continue; - } - } - - // Fix Crash bandicoot xfc leftover r3 : - if (Op0->Output[0].IsRegister(PARAM_T, FakeRegNr_Prod)) // 'r3' - { - // The final combiner uses r3, try to use r1 instead : - if (IsRegisterFreeFromIndexOnwards(i, PARAM_R, 1)) - { - ReplaceRegisterFromIndexOnwards(i, Op0->Output[0].Type, Op0->Output[0].Address, PARAM_R, 1); - EmuLog(LOG_LEVEL::DEBUG, "; Changed fake register by r1"); - Result = true; - continue; - } - } - } // while - return Result; -} // CombineInstructions - -bool PSH_XBOX_SHADER::SimplifyMOV(PPSH_INTERMEDIATE_FORMAT Cur) -{ - bool CanSimplify; - float Factor; - - // NOP-out MOV's that read and write to the same register : - if ( (Cur->Output[0].Type == Cur->Parameters[0].Type) - && (Cur->Output[0].Address == Cur->Parameters[0].Address) - && (Cur->Output[0].Mask == Cur->Parameters[0].Mask)) - { - if (Cur->Output[0].Type == PARAM_VALUE) - CanSimplify = Cur->Output[0].GetConstValue() == Cur->Parameters[0].GetConstValue(); - else - CanSimplify = (Cur->Output[0].Modifiers == Cur->Parameters[0].Modifiers) - && (Cur->Output[0].Multiplier == Cur->Parameters[0].Multiplier); - - if (CanSimplify) - { - Cur->Opcode = PO_NOP; // This nop will be removed in a recursive fixup - EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV into a NOP"); - return true; - } - } - - // Does this MOV put a 0 (zero) in the output? - if (Cur->Parameters[0].GetConstValue() == 0.0) - { - // Attempt to find a constant with the value 0, and use that if present. - if (!Cur->Parameters[0].SetScaleConstRegister(0.0f, Recompiled)) - { - // Simulate 0 by subtracting a (guaranteed) register from itself : - // Fixup via "sub d0=v0,v0" : - Cur->Opcode = PO_SUB; - Cur->Parameters[0].Type = PARAM_V; - Cur->Parameters[0].Address = 0; - Cur->Parameters[0].Modifiers = 0; - Cur->Parameters[1] = Cur->Parameters[0]; - EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV 0 into a SUB v0,v0"); - } - else - { - EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV 0 into a MOV c0"); - } - - return true; - } - - // Does this MOV put a constant in the output? - if (Cur->Parameters[0].Type == PARAM_VALUE) - { - // TODO : If there's a constant equal to GetConstValue(), use that. - Factor = Cur->Parameters[0].GetConstValue(); - - if (!Cur->Parameters[0].SetScaleConstRegister(Factor, Recompiled)) - { - // Fixup via a SUB (which can calculate a constant value) : - Cur->Opcode = PO_SUB; - Cur->Parameters[0].Type = PARAM_V; - Cur->Parameters[0].Address = 0; - - if (Factor < 0.0) - { - // Simulate -1 by calculating it via a (guaranteed) register : - // We follow this : (-v0) - (1-v0) = -v0 - 1 + v0 = -1 - Cur->Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); - Cur->Parameters[1] = Cur->Parameters[0]; - Cur->Parameters[1].Modifiers = (1 << ARGMOD_INVERT); - // Go on with a positive factor, to ease the scaling : - Factor = -Factor; - } - else - { - // Simulate 1 by calculating it via a (guaranteed) register : - // We follow this : (1-v0) - (-v0) = (1-v0) + v0 = 1 - Cur->Parameters[0].Modifiers = (1 << ARGMOD_INVERT); - Cur->Parameters[1] = Cur->Parameters[0]; - Cur->Parameters[1].Modifiers = (1 << ARGMOD_NEGATE); - } - - // Try to simulate all factors (0.5, 1.0 and 2.0) using an output modifier : - Cur->ScaleOutput(Factor); - - EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV {const} into a SUB_factor 1-v0,-v0"); - } - else - { - EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV {const} into a MOV c#"); - } - return true; - } - return false; -} - -bool PSH_XBOX_SHADER::SimplifyADD(PPSH_INTERMEDIATE_FORMAT Cur) -{ - // Is this an addition of s0+0 ? - if (Cur->Parameters[1].GetConstValue() == 0.0) - { - // Change it into a MOV (the first argument is already in-place) - Cur->Opcode = PO_MOV; - EmuLog(LOG_LEVEL::DEBUG, "; Changed ADD s0,0 into a MOV s0"); - return true; - } - return false; -} - -bool PSH_XBOX_SHADER::SimplifyMAD(PPSH_INTERMEDIATE_FORMAT Cur, int index) -{ - // Is this 0*s1+s2 or s0*0+s2 ? - if (Cur->Parameters[0].GetConstValue() == 0.0 - || Cur->Parameters[1].GetConstValue() == 0.0) - { - // Change it into s2 : - Cur->Opcode = PO_MOV; - Cur->Parameters[0] = Cur->Parameters[2]; - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,0 into a MOV s0"); - return true; - } - - // Is this s0*s1+0 ? - if (Cur->Parameters[2].GetConstValue() == 0.0) - { - // Change it into s0*s1 : - Cur->Opcode = PO_MUL; - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0, s1,0 into a MUL s0, s1"); - return true; - } - - // Is this s0*1+s2 ? - if (Cur->Parameters[1].GetConstValue() == 1.0) - { - // Change it into s0+s2 : - Cur->Opcode = PO_ADD; - Cur->Parameters[1] = Cur->Parameters[2]; - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,1,s2 into a ADD s0,s2"); - return true; - } - - // Is this s0*-1+s2 ? - if (Cur->Parameters[1].GetConstValue() == -1.0) - { - // Change it into s2-s0 : - Cur->Opcode = PO_SUB; - Cur->Parameters[1] = Cur->Parameters[0]; - Cur->Parameters[0] = Cur->Parameters[2]; - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,-1,s2 into a SUB s2,s0"); - return true; - } - - PSH_INTERMEDIATE_FORMAT Ins = {}; - - // Is this 0.5*s1+s2 ? - if (Cur->Parameters[0].GetConstValue() == 0.5f && Cur->Parameters[1].UsesRegister()) - { - if (!Cur->Parameters[0].SetScaleConstRegister(0.5f, Recompiled)) - { - // Change it into s2 : - Cur->Opcode = PO_ADD; - Cur->Parameters[0] = Cur->Parameters[1]; - Cur->Parameters[1] = Cur->Parameters[2]; - - Ins.Initialize(PO_MOV); - Ins.Modifier = INSMOD_D2; - Ins.Output[0] = Ins.Parameters[0] = Cur->Parameters[1]; - Ins.CommentString = "; Inserted to perform division by 2"; - InsertIntermediate(&Ins, index); - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD 0.5,s1,s2 into a MOV_d2 s1, s1 ADD s1, s2"); - } - else - { - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD 0.5,s1,s2 into a MAD c#,s1,s2"); - } - return true; - } - - // Is this s0*0.5+s2 ? - if (Cur->Parameters[1].GetConstValue() == 0.5f && Cur->Parameters[0].UsesRegister()) - { - if (!Cur->Parameters[1].SetScaleConstRegister(0.5f, Recompiled)) - { - // Change it into s2 : - Cur->Opcode = PO_ADD; - Cur->Parameters[0] = Cur->Parameters[0]; - Cur->Parameters[1] = Cur->Parameters[2]; - - Ins.Initialize(PO_MOV); - Ins.Modifier = INSMOD_D2; - Ins.Output[0] = Ins.Parameters[0] = Cur->Parameters[0]; - Ins.CommentString = "; Inserted to perform division by 2"; - InsertIntermediate(&Ins, index); - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,0.5,s2 into a MOV_d2 s0, s0 ADD s0, s2"); - } - else - { - EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,0.5,s2 into a MAD s0,c#,s2"); - } - return true; - } - return false; -} - -bool PSH_XBOX_SHADER::SimplifySUB(PPSH_INTERMEDIATE_FORMAT Cur) -{ - // Is this an subtraction of s0-0 ? - if (Cur->Parameters[1].GetConstValue() == 0.0) - { - // Change it into a MOV (the first argument is already in-place) - Cur->Opcode = PO_MOV; - EmuLog(LOG_LEVEL::DEBUG, "; Changed SUB x, 0 into a MOV x"); - return true; - } - return false; -} - -bool PSH_XBOX_SHADER::SimplifyMUL(PPSH_INTERMEDIATE_FORMAT Cur) -{ - // Is the result of this multiplication zero ? - if (Cur->Parameters[1].GetConstValue() == 0.0) - { - // Change it into a MOV (the 0 argument will be resolve in a recursive MOV fixup) : - Cur->Opcode = PO_MOV; - Cur->Parameters[0].SetConstValue(0.0); - EmuLog(LOG_LEVEL::DEBUG, "; Changed MUL s0,0 into a MOV 0"); - return true; - } - - // Is this a multiply-by-const ? - if (Cur->Parameters[1].Type == PARAM_VALUE) - { - // Change it into a simple MOV and scale the output instead : - Cur->Opcode = PO_MOV; - Cur->ScaleOutput(Cur->Parameters[1].GetConstValue()); - EmuLog(LOG_LEVEL::DEBUG, "; Changed MUL s0,{const} into a MOV_factor s0"); - return true; - } - return false; -} // SimplifyMUL - -bool PSH_XBOX_SHADER::SimplifyLRP(PPSH_INTERMEDIATE_FORMAT Cur, int index) -{ - // LRP calculates : d0=s0*s1+(1-s0)*s2 which can also be read as : d0=s0*(s1-s2)+s2 - - // Is the right part ((1-s0)*s2) zero? - if ((Cur->Parameters[0].GetConstValue() == 1.0) || (Cur->Parameters[2].GetConstValue() == 0.0)) - { - // Change it into a MUL (calculating the left part : s0*s1 : - Cur->Opcode = PO_MUL; - EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,s1,s2 (where (1-s0)*s2=0) into a MUL s0,s1"); - return true; - } - - // Is the left part (s0*s1) zero? - if ((Cur->Parameters[0].GetConstValue() == 0.0) || (Cur->Parameters[1].GetConstValue() == 0.0)) - { - // Change it into a MUL (calculating the right part : (1-s0)*s2) : - Cur->Opcode = PO_MUL; - Cur->Parameters[0].Invert(); - Cur->Parameters[1] = Cur->Parameters[2]; - EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,s1,s2 (where s0*s1=0) into a MUL (1-s0),s2"); - return true; - } - - // Is it d0=s0*s1+(1-s0)*1 ? - if (Cur->Parameters[2].GetConstValue() == 1.0) - { - // Change it into a d0=s0*s1+(1-s0) - Cur->Opcode = PO_MAD; - Cur->Parameters[2] = Cur->Parameters[0]; - Cur->Parameters[2].Invert(); - EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,s1,1 into a MAD s0,s1,1-s0"); - return true; - } - - // Is it d0=s0*(1-s2)+s2 ? - if (Cur->Parameters[1].GetConstValue() == 1.0) - { - // Change it into a d0=s0*(1-s2)+s2 - Cur->Opcode = PO_MAD; - Cur->Parameters[1] = Cur->Parameters[2]; - Cur->Parameters[1].Invert(); - EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,1,s2 into a MAD s0,1-s2,s2"); - return true; - } - - int output = NextFreeRegisterFromIndexUntil(index, PARAM_R, index, 0, Cur->Output[0].Address); - - if (output >= 0) - { - bool insert = false; - for (int p = 0; p < PSH_OPCODE_DEFS[Cur->Opcode]._In; ++p) - { - if (Cur->Output[0].Type == Cur->Parameters[p].Type - && Cur->Output[0].Address == Cur->Parameters[p].Address) - { - insert = true; - Cur->Parameters[p].Address = output; - } - } - if (insert) - { - PSH_INTERMEDIATE_FORMAT Ins = {}; - - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(PARAM_R, output, 0); - Ins.Parameters[0].SetRegister(Cur->Output[0].Type, Cur->Output[0].Address, 0); - Ins.CommentString = "; Inserted to avoid LRP parameters referencing the output register"; - InsertIntermediate(&Ins, index); - EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,1,s2 into a MAD s0,1-s2,s2"); - return true; - } - } - - return false; -} // SimplifyLRP - -bool PSH_XBOX_SHADER::FixupCND(PPSH_INTERMEDIATE_FORMAT Cur, int index) -{ - PSH_INTERMEDIATE_FORMAT Ins = {}; - - // TODO: Look into using predicate register - Cur->Opcode = PO_CMP; - - int output = NextFreeRegisterFromIndexUntil(index, PARAM_R, index); - Ins.Initialize(PO_SUB); - Ins.Output[0].SetRegister(PARAM_R, output, Cur->Parameters[0].Mask); - Ins.Parameters[0] = Cur->Parameters[0]; - Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); - Cur->Parameters[0] = Ins.Output[0]; - Cur->Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); - std::swap(Cur->Parameters[1], Cur->Parameters[2]); - Ins.CommentString = Cur->CommentString = "; Changed CND into SUB CMP"; - InsertIntermediate(&Ins, index); - EmuLog(LOG_LEVEL::DEBUG, "; Changed CND into SUB CMP"); - return true; -} - -bool PSH_XBOX_SHADER::FixupPixelShader() -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - - bool Result = RemoveNops(); - - // TODO : Fixup writes to read-only registers (V0, V1) via another free register (if possible) - // TODO : Fixup the usage of non-existent register numbers (like FakeRegNr_Sum and FakeRegNr_Prod) - // TODO : Fixup the usage of the unsupported INSMOD_BIAS and INSMOD_BX2 instruction modifiers - // TODO : Use the INSMOD_SAT instruction modifier instead of the ARGMOD_SATURATE argument modifier - // TODO : Condense constants registers, to avoid the non-existant C8-C15 (requires a mapping in SetPixelShaderConstant too...) - // TODO : Convert numeric arguments (-2, -1, 0, 1, 2) into modifiers on the other argument - // TODO : Complete to port to D3D9 to support all 18 constants (including C8..C15 + FC0+FC1) - - if (CombineInstructions()) - Result = true; - - if (MoveRemovableParametersRight()) - Result = true; - - // Simplify instructions, which can help to compress the result : - i = IntermediateCount; - while (i > StartPos) - { - --i; - Cur = &(Intermediate[i]); - - switch (Cur->Opcode) { - case PO_MOV: - if (SimplifyMOV(Cur)) - Result = true; - break; - - case PO_ADD: - if (SimplifyADD(Cur)) - Result = true; - break; - - case PO_MAD: - if (SimplifyMAD(Cur, i)) - Result = true; - break; - - case PO_SUB: - if (SimplifySUB(Cur)) - Result = true; - break; - - case PO_MUL: - if (SimplifyMUL(Cur)) - Result = true; - break; - - case PO_LRP: - if (SimplifyLRP(Cur, i)) - Result = true; - break; - - case PO_CND: - if (FixupCND(Cur, i)) - Result = true; - break; - } // case - } // for - - // If the above code made any alteration, repeat it as some changes require a followup (like MUL>MOV>NOP) : - if (Result) - { - Log("Fixup intermediate result"); - FixupPixelShader(); - } - return Result; -} // FixupPixelShader - -bool PSH_XBOX_SHADER::FixInvalidSrcSwizzle() -{ - int i, j; - PPSH_INTERMEDIATE_FORMAT Cur; - PPSH_IMD_ARGUMENT CurArg; - - bool Result = false; - for (i = StartPos; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - if (Cur->IsArithmetic()) - { - // Loop over the input arguments : - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) - { - CurArg = &(Cur->Parameters[j]); - - // Fix "Invalid src swizzle" : - if (CurArg->Mask == MASK_RGB) - { - CurArg->Mask = MASK_RGBA; - Result = true; - } - } - } - } - return Result; -} - -bool PSH_XBOX_SHADER::FixMissingR0a() -// On the Xbox, the alpha portion of the R0 register is initialized to -// the alpha component of texture 0 if texturing is enabled for texture 0 : -{ - int R0aDefaultInsertPos; - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - PSH_INTERMEDIATE_FORMAT NewIns = {}; - - // Detect a read of r0.a without a write, as we need to insert a "MOV r0.a, t0.a" as default (like the xbox has) : - R0aDefaultInsertPos = -1; - for (i = 0; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - if (Cur->Opcode < PO_TEX) // TODO : Check explicitly which instruction types are handled below - continue; - - // Make sure if we insert at all, it'll be after the DEF's : - if (R0aDefaultInsertPos < 0) - R0aDefaultInsertPos = i; - - // First, check if r0.a is read by this opcode : - if (Cur->ReadsFromRegister(PARAM_R, 0, MASK_A)) - { - R0aDefaultInsertPos = i; - break; - } - - // If this opcode writes to r0.a, we're done : - if (Cur->WritesToRegister(PARAM_R, 0, MASK_A)) - return false; - } - - if (R0aDefaultInsertPos >= 0) - { - // Insert a new opcode : MOV r0.a, t0.a - NewIns.Initialize(PO_MOV); - NewIns.Output[0].SetRegister(PARAM_R, 0, MASK_A); - NewIns.Parameters[0] = NewIns.Output[0]; - NewIns.Parameters[0].Type = PARAM_T; - NewIns.CommentString = "Inserted r0.a default"; - InsertIntermediate(&NewIns, R0aDefaultInsertPos); - return true; - } - return false; -} // FixMissingR0a - -bool PSH_XBOX_SHADER::FixMissingR1a() -// On the Xbox, the alpha portion of the R1 register is initialized to -// the alpha component of texture 1 if texturing is enabled for texture 1 : -{ - int R1aDefaultInsertPos; - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - PSH_INTERMEDIATE_FORMAT NewIns = {}; - - // Detect a read of r1.a without a write, as we need to insert a "MOV r1.a, t1.a" as default (like the xbox has) : - R1aDefaultInsertPos = -1; - for (i = 0; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - if (Cur->Opcode < PO_TEX) // TODO : Check explicitly which instruction types are handled below - continue; - - // First, check if r1.a is read by this opcode : - if (Cur->ReadsFromRegister(PARAM_R, 1, MASK_A)) - { - // Make sure if we insert at all, it'll be after the DEF's : - if (R1aDefaultInsertPos < 0) - R1aDefaultInsertPos = i; - - R1aDefaultInsertPos = i; - break; - } - - // If this opcode writes to r1.a, we're done : - if (Cur->WritesToRegister(PARAM_R, 1, MASK_A)) - return false; - } - - if (R1aDefaultInsertPos >= 0) - { - // Insert a new opcode : MOV r1.a, t1.a - NewIns.Initialize(PO_MOV); - NewIns.Output[0].SetRegister(PARAM_R, 1, MASK_A); - NewIns.Parameters[0] = NewIns.Output[0]; - NewIns.Parameters[0].Type = PARAM_T; - NewIns.CommentString = "Inserted r1.a default"; - InsertIntermediate(&NewIns, R1aDefaultInsertPos); - return true; - } - - return false; -} // FixMissingR1a - -bool PSH_XBOX_SHADER::FixUninitializedReads() -// On the Xbox, the alpha portion of the R1 register is initialized to -// the alpha component of texture 1 if texturing is enabled for texture 1 : -{ - int R1aDefaultInsertPos; - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - PSH_INTERMEDIATE_FORMAT NewIns = {}; - bool Result = false; - - int readPositions[32][4] = {}; - int writePositions[32][4] = {}; - int initPositions[32] = {}; - int initMasks[32] = {}; - - // Detect a read of r1.a without a write, as we need to insert a "MOV r1.a, t1.a" as default (like the xbox has) : - R1aDefaultInsertPos = -1; - for (i = 0; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - - for (int j = 0; j < MaxRegisterCount(PARAM_R); ++j) - { - // check reads - if (readPositions[j][0] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_R)) - { - readPositions[j][0] = i; - } - if (readPositions[j][1] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_G)) - { - readPositions[j][1] = i; - } - if (readPositions[j][2] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_B)) - { - readPositions[j][2] = i; - } - if (readPositions[j][3] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_A)) - { - readPositions[j][3] = i; - } - - // check writes - if (writePositions[j][0] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_R)) - { - writePositions[j][0] = i; - } - if (writePositions[j][1] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_G)) - { - writePositions[j][1] = i; - } - if (writePositions[j][2] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_B)) - { - writePositions[j][2] = i; - } - if (writePositions[j][3] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_A)) - { - writePositions[j][3] = i; - } - } - } - - for (int j = 0; j < MaxRegisterCount(PARAM_R); ++j) - { - int mask = 0; - int pos = IntermediateCount; - for (int i = 0; i < 4; ++i) - { - if (readPositions[j][i] <= writePositions[j][i] && readPositions[j][i] != 0) - { - mask |= (1 << i); - pos = std::min(pos, readPositions[j][i]); - } - } - - initPositions[j] = pos; - initMasks[j] = mask; - } - - NewIns.Initialize(PO_MOV); - NewIns.CommentString = "; Inserted to initialize register"; - for (int j = 0; j < MaxRegisterCount(PARAM_R); ++j) - { - int mask = initMasks[j]; - if (mask) - { - // Insert a new opcode : MOV r#.???, c0.??? - NewIns.Output[0].SetRegister(PARAM_R, j, mask); - NewIns.Parameters[0].SetScaleConstRegister(0.0f, Recompiled); - // r0 and r1 take their alpha from the respective texture coordinate - if (j < PSH_XBOX_MAX_R_REGISTER_COUNT) - { - mask &= MASK_RGB; - } - - InsertIntermediate(&NewIns, std::min(StartPos, initPositions[j])); - Result = true; - } - } - - return Result; -} // FixUninitializedReads - - -bool PSH_XBOX_SHADER::FixCoIssuedOpcodes() -{ - int i; - PPSH_INTERMEDIATE_FORMAT Cur; - bool Result = false; - // Since we're targetting m_PSVersion >= D3DPS_VERSION(2, 0), co-issued instructions are no longer supported, thus reset all IsCombined flags : - for (i = StartPos; i < IntermediateCount; i++) - { - Cur = &(Intermediate[i]); - if (Cur->IsArithmetic()) - { - if (Cur->IsCombined) - { - Cur->IsCombined = false; - Result = true; - } - } - } - return Result; -} - -bool PSH_XBOX_SHADER::FixInvalidDstRegister() -{ - int i, j; - PPSH_INTERMEDIATE_FORMAT Cur; - PPSH_IMD_ARGUMENT CurArg; - - bool Result = false; - for (i = IntermediateCount - 1; i >= StartPos; --i) - { - Cur = &(Intermediate[i]); - // Skip non-arithmetic opcodes - if (!Cur->IsArithmetic()) - continue; - - // Loop over the output arguments : - for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) - { - CurArg = &(Cur->Output[j]); - - if (IsValidNativeOutputRegister(CurArg->Type, CurArg->Address)) - continue; - - int lastUsed = RegisterIsUsedFromIndexUntil(i + 1, CurArg->Type, CurArg->Address); - - PSH_ARGUMENT_TYPE dstType = PARAM_R; - int dstIndex = -1; - - if (IsValidNativeOutputRegister(PARAM_T)) - { - dstType = PARAM_T; - dstIndex = NextFreeRegisterFromIndexUntil(i + 1, PARAM_T, lastUsed); - } - - if (dstIndex == -1) - { - dstType = PARAM_R; - dstIndex = NextFreeRegisterFromIndexUntil(i + 1, PARAM_R, lastUsed); - } - - if (dstIndex != -1) - { - Result = true; - - if (Cur->ReadsFromRegister(CurArg->Type, CurArg->Address)) - { - if (lastUsed >= 0) ++lastUsed; - - PSH_INTERMEDIATE_FORMAT Ins = {}; - - Ins.Initialize(PO_MOV); - Ins.Output[0].SetRegister(dstType, dstIndex, 0); - Ins.Parameters[0].SetRegister(CurArg->Type, CurArg->Address, 0); - InsertIntermediate(&Ins, i); - ++Cur; - CurArg = &(Cur->Output[j]); - } - - ReplaceInputRegisterFromIndexOnwards(i + 1, CurArg->Type, CurArg->Address, dstType, dstIndex, lastUsed); - - CurArg->Type = dstType; - CurArg->Address = dstIndex; - } - } - } - return Result; -} - -// TODO: Refactor and optimize -bool PSH_XBOX_SHADER::FixOverusedRegisters() -{ - int i; - - bool Result = false; - - PSH_INTERMEDIATE_FORMAT Ins = {}; - Ins.Initialize(PO_MOV); - - // For all opcodes, try to put constant and discarded arguments in the rightmost slot, to ease following analysis : - i = IntermediateCount; - while (i > StartPos) - { - --i; - - int InsertPos = i; - - // Skip this operation on LRP instructions - // This prevents "error X5765: Dest register for LRP cannot be the same as first or third source register" in WWE RAW2 - if (Intermediate[i].Opcode == PO_LRP) { - continue; - } - - // Handle PARAM_C, PARAM_V and PARAM_T (in that order) : - for (int t = PARAM_C; t >= PARAM_T; t--) { - enum PSH_ARGUMENT_TYPE param_t = (enum PSH_ARGUMENT_TYPE)t; - int max_total = (t == PARAM_C) ? 2 : (t == PARAM_V) ? 999 : 1; - int addressCount = 0; - int total = 0; - while (Intermediate[i].ReadsFromRegister(param_t, -1, addressCount, total) && (addressCount > 1 || total > max_total)) - { - for (int p = 0; p < PSH_OPCODE_DEFS[Intermediate[i].Opcode]._In; ++p) - { - if (Intermediate[i].Parameters[p].Type == param_t) - { - int output = NextFreeRegisterFromIndexUntil(i, PARAM_R, i); - - // This inserts a MOV opcode that writes to R, reading from a C, V or T register - Ins.Output[0].SetRegister(PARAM_R, output, 0); - Ins.Parameters[0].SetRegister(Intermediate[i].Parameters[p].Type, Intermediate[i].Parameters[p].Address, 0); - InsertIntermediate(&Ins, InsertPos); - ++InsertPos; - - ReplaceInputRegisterFromIndexOnwards(InsertPos, Intermediate[InsertPos].Parameters[p].Type, Intermediate[InsertPos].Parameters[p].Address, PARAM_R, output, InsertPos); - Result = true; - break; - } - } - } - } - } - return Result; -} // FixOverusedRegisters - -// TODO : FocusBlur sample needs a zero in 'cnd' opcode - -/* RPSRegisterObject */ - -void RPSRegisterObject::Decode(uint8_t Value, bool aIsAlpha) -{ - IsAlpha = aIsAlpha; - Reg = (PS_REGISTER)(Value); -} - -std::string RPSRegisterObject::DecodedToString() -{ - assert((PS_REGISTER_DISCARD <= Reg) && (Reg <= PS_REGISTER_EF_PROD)); - - return PS_RegisterStr[Reg + 1]; -} - -/* RPSInputRegister */ - -void RPSInputRegister::Decode(uint8_t Value, bool aIsAlpha) -{ - RPSRegisterObject::Decode(Value & PS_NoChannelMask, aIsAlpha); - - Channel = (PS_CHANNEL)(Value & PS_CHANNEL_ALPHA); - InputMapping = (PS_INPUTMAPPING)(Value & 0xe0); - - // Remove the above flags from the register : - Reg = (PS_REGISTER)(Reg & 0xf); - - // Check if the input Register is ZERO, in which case we want to allow the extended registers : - if (Reg == PS_REGISTER_ZERO) - { - switch (InputMapping) { - case PS_REGISTER_ONE: case PS_REGISTER_NEGATIVE_ONE: case PS_REGISTER_ONE_HALF: case PS_REGISTER_NEGATIVE_ONE_HALF: - // These input mapping have their own register - keep these in 'Reg', so we can check for them : - Reg = (PS_REGISTER)(InputMapping); - break; - - case PS_INPUTMAPPING_EXPAND_NEGATE: - // This case has no separate PS_REGISTER define, but when applied to zero, also results in one : - Reg = PS_REGISTER_ONE; - break; - } - } -} - -std::string RPSInputRegister::DecodedToString() -{ - std::string Result; - std::string InputMappingStr = ""; - switch (Reg) { - case PS_REGISTER_ZERO: - { - Result = PS_RegisterStr[0]; - return Result; - } - case PS_REGISTER_ONE: - Result = PS_RegisterStr[0x11]; - break; - case PS_REGISTER_NEGATIVE_ONE: - Result = PS_RegisterStr[0x12]; - break; - case PS_REGISTER_ONE_HALF: - Result = PS_RegisterStr[0x13]; - break; - case PS_REGISTER_NEGATIVE_ONE_HALF: - Result = PS_RegisterStr[0x14]; - break; - default: - Result = RPSRegisterObject::DecodedToString(); - InputMappingStr = " | " + PS_InputMappingStr[(InputMapping >> 5) & 7]; - } - - // Render the channel as a string : - Result = Result + " | " + PS_ChannelStr[(Channel > 0) ? /*Alpha*/2 : (IsAlpha ? /*Blue*/1 : /*RGB*/0)] + InputMappingStr; - return Result; -} - -/* RPSCombinerOutput */ - -void RPSCombinerOutput::Decode(uint8_t Value, DWORD PSInputs, bool aIsAlpha) -{ - RPSRegisterObject::Decode(Value, aIsAlpha); - - // Decode PSAlphaInputs / PSRGBInputs : - Input1.Decode((PSInputs >> 8) & 0xFF, aIsAlpha); - Input2.Decode((PSInputs >> 0) & 0xFF, aIsAlpha); -} - -/* RPSCombinerStageChannel */ - -void RPSCombinerStageChannel::Decode(DWORD PSInputs, DWORD PSOutputs, bool aIsAlpha/* = false*/) -{ - // Get the combiner output flags : - CombinerOutputFlags = (PS_COMBINEROUTPUT)(PSOutputs >> 12); - - // Decompose the combiner output flags : - OutputSUM.OutputAB.DotProduct = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) > 0; // false=Multiply, true=DotProduct - OutputSUM.OutputCD.DotProduct = (CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0; // false=Multiply, true=DotProduct - - if (!aIsAlpha) - { - OutputSUM.OutputAB.BlueToAlpha = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) > 0; // false=Alpha-to-Alpha, true=Blue-to-Alpha - OutputSUM.OutputCD.BlueToAlpha = (CombinerOutputFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) > 0; // false=Alpha-to-Alpha, true=Blue-to-Alpha - } - - // Decode PSAlphaOutputs / PSRGBOutputs and PSAlphaInputs / PSRGBInputs : - OutputSUM.OutputAB.Decode((PSOutputs >> 4) & 0xF, (PSInputs >> 16) & 0xFFFF, aIsAlpha); - OutputSUM.OutputCD.Decode((PSOutputs >> 0) & 0xF, (PSInputs >> 0) & 0xFFFF, aIsAlpha); - OutputSUM.Decode((PSOutputs >> 8) & 0xF, aIsAlpha); - - AB_CD_SUM = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_CD_MUX) == 0; // true=AB+CD, false=MUX(AB,CD) based on R0.a -} - -// Note : On a hardware level, there are only 4 pixel shaders instructions present in the Nvidia NV2A GPU : -// - xdd (dot/dot/discard) > calculating AB=A.B and CD=C.D -// - xdm (dot/mul/discard) > calculating AB=A.B and CD=C*D -// - xmmc (mul/mul/mux) > calculating AB=A*B and CD=C*D and Mux=AB?CD -// - xmma (mul/mul/sum) > calculating AB=A*B and CD=C*D and Sum=AB+CD -// (One of the implications is, that once a dot-product is issued, no Sum or Mux operation is possible.) -// All other instructions (mov, add, sub, mul, lrp, dp3) are compiled into one of these 4 using varying arguments. -// All 4 instruction specify up to three output registers, all of which must be unique (or be discarded). -// -// Apart from the r0,r1 and t0-t3 registers, the NV2A allows writing to the v0,v1 (this conflicts with PS.1.3!) -// -// The precision of registers is also different; On the Xbox, all 4 color components (RGBA) for constant registers -// range from 0.0 to 1.0 (with 8 bits of precision), while all other registers (r, t and v) range from -1.0 to 1.0. -// -// This is different from native PS.1.3 in which constant registers suddenly have a range -1.0 to 1.0, but vertex -// registers (v0 and v1) range from 0.0 to 1.0 instead, and the temporary and texture registers have a range -// from negative 'MaxPixelShaderValue' to positive 'MaxPixelShaderValue', which value must at least be 1.0 -// (but depending on hardware capabilities can be higher). -// -// TODO : Correct emulation should correct these differences; The range of constant-registers must be converted -// from 0.0-1.0 to -1.0-1.0, and vertex-registers must be converted from -1.0..1.0 to 0.0..1.0 (if anything like -// that is at all possible!) -// -// register | Xbox range | Native range | Xbox | Native | -// C0..C8 | 0.0 - 1.0 ! -1.0 - 1.0 | readonly | readonly | -// R0..R1 | -1.0 - 1.0 | -1.0 - 1.0 | writeable | writeable | -// T0..T3 | -1.0 - 1.0 | -1.0 - 1.0 | writeable | writeable | -// V0..V1 | -1.0 - 1.0 ! 0.0 - 1.0 | writeable ! readonly | -// -// "-C0_bias_x2" shifts range from [ 0..1] to [-1..1] -// "-V0_bias_d2" shifts range from [-1..1] to [ 0..1] - -/* RPSFinalCombiner */ - -void RPSFinalCombiner::Decode(const DWORD PSFinalCombinerInputsABCD, const DWORD PSFinalCombinerInputsEFG, const DWORD PSFinalCombinerConstants) -{ - InputA.Decode((PSFinalCombinerInputsABCD >> 24) & 0xFF, /*aIsAlpha=*/false); - InputB.Decode((PSFinalCombinerInputsABCD >> 16) & 0xFF, /*aIsAlpha=*/false); - InputC.Decode((PSFinalCombinerInputsABCD >> 8) & 0xFF, /*aIsAlpha=*/false); - InputD.Decode((PSFinalCombinerInputsABCD >> 0) & 0xFF, /*aIsAlpha=*/false); - - InputE.Decode((PSFinalCombinerInputsEFG >> 24) & 0xFF, /*aIsAlpha=*/false); - InputF.Decode((PSFinalCombinerInputsEFG >> 16) & 0xFF, /*aIsAlpha=*/false); - InputG.Decode((PSFinalCombinerInputsEFG >> 8) & 0xFF, /*aIsAlpha=*/false); - FinalCombinerFlags = (PS_FINALCOMBINERSETTING)((PSFinalCombinerInputsEFG >> 0) & 0xFF); - - FinalCombinerC0Mapping = (PSFinalCombinerConstants >> 0) & 0xF; - FinalCombinerC1Mapping = (PSFinalCombinerConstants >> 4) & 0xF; - dwPS_GLOBALFLAGS = (PSFinalCombinerConstants >> 8) & 0x1; -} - -void XTL_DumpPixelShaderToFile(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ - static int PshNumber = 0; // Keep track of how many pixel shaders we've attempted to convert. - // Don't dump more than 100 shaders, to prevent cluttering the filesystem : - if (PshNumber >= 100) - return; - - char szPSDef[32]; - - sprintf(szPSDef, "PSDef%.03d.txt", PshNumber++); - FILE* out = fopen(szPSDef, "w"); - if (out) - { - fprintf(out, PSH_XBOX_SHADER::OriginalToString(pPSDef).c_str()); - fclose(out); - } -} - -PSH_RECOMPILED_SHADER XTL_EmuRecompilePshDef(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ - uint32_t PSVersion = D3DPS_VERSION(2, 0); // Use pixel shader model 2.0 by default - - extern D3DCAPS g_D3DCaps; - - if (g_D3DCaps.PixelShaderVersion > D3DPS_VERSION(3, 0)) { - // TODO : Test PSVersion = D3DPS_VERSION(3, 0); // g_D3DCaps.PixelShaderVersion; - // TODO : Make the pixel shader version configurable - } - - PSH_XBOX_SHADER PSH = {}; - PSH.SetPSVersion(PSVersion); - PSH.Decode(pPSDef); - return PSH.Convert(pPSDef); -} - -// From Dxbx uState.pas : - -PSH_RECOMPILED_SHADER DxbxRecompilePixelShader(XTL::X_D3DPIXELSHADERDEF *pPSDef) -{ -static const - char *szDiffusePixelShader = - "ps_2_x\n" - "dcl_2d s0\n" - "dcl t0.xy\n" - "texld r0, t0, s0\n" - "mov oC0, r0\n"; - std::string ConvertedPixelShaderStr; - DWORD hRet; - LPD3DXBUFFER pShader; - LPD3DXBUFFER pErrors; - DWORD *pFunction; - - // Attempt to recompile PixelShader - PSH_RECOMPILED_SHADER Result = XTL_EmuRecompilePshDef(pPSDef); - ConvertedPixelShaderStr = Result.NewShaderStr; - - // assemble the shader - pShader = nullptr; - pErrors = nullptr; - hRet = D3DXAssembleShader( - ConvertedPixelShaderStr.c_str(), - ConvertedPixelShaderStr.length(), - /*pDefines=*/nullptr, - /*pInclude=*/nullptr, - /*Flags=*/0, // D3DXASM_DEBUG, - /*ppCompiledShader=*/&pShader, - /*ppCompilationErrors*/&pErrors); - - if (hRet != D3D_OK) - { - EmuLog(LOG_LEVEL::WARNING, "Could not create pixel shader"); - EmuLog(LOG_LEVEL::WARNING, std::string((char*)pErrors->GetBufferPointer(), pErrors->GetBufferSize()).c_str()); - - printf(ConvertedPixelShaderStr.c_str()); - - hRet = D3DXAssembleShader( - szDiffusePixelShader, - strlen(szDiffusePixelShader), - /*pDefines=*/nullptr, - /*pInclude=*/nullptr, - /*Flags=*/0, // Was D3DXASM_SKIPVALIDATION, - /*ppCompiledShader=*/&pShader, - /*ppCompilationErrors*/&pErrors); - - if (hRet != D3D_OK) { - EmuLog(LOG_LEVEL::WARNING, "Could not create pixel shader"); - EmuLog(LOG_LEVEL::WARNING, std::string((char*)pErrors->GetBufferPointer(), pErrors->GetBufferSize()).c_str()); - CxbxKrnlCleanup("Cannot fall back to the most simple pixel shader!"); - } - - EmuLog(LOG_LEVEL::WARNING, "We're lying about the creation of a pixel shader!"); - } - - if (pShader) - { - pFunction = (DWORD*)(pShader->GetBufferPointer()); - if (hRet == D3D_OK) { - // redirect to windows d3d - hRet = g_pD3DDevice->CreatePixelShader - ( - pFunction, - (IDirect3DPixelShader**)(&(Result.ConvertedHandle)) //fixme - ); - - if (hRet != D3D_OK) { - printf(D3DErrorString(hRet)); - } - } - - // Dxbx note : We must release pShader here, else we would have a resource leak! - pShader->Release(); - pShader = nullptr; - } - - // Dxbx addition : We release pErrors here (or it would become a resource leak!) - if (pErrors) - { - pErrors->Release(); - pErrors = nullptr; - } - return Result; -} // DxbxRecompilePixelShader - -std::vector g_RecompiledPixelShaders; - -VOID DxbxUpdateActivePixelShader() // NOPATCH -{ - XTL::X_D3DPIXELSHADERDEF *pPSDef; - PPSH_RECOMPILED_SHADER RecompiledPixelShader; - DWORD ConvertedPixelShaderHandle; - DWORD CurrentPixelShader; - int i; - DWORD Register_; - D3DCOLOR dwColor; - D3DXCOLOR fColor; - - HRESULT Result = D3D_OK; - - // The first RenderState is PSAlpha, - // The pixel shader is stored in pDevice->m_pPixelShader - // For now, we still patch SetPixelShader and read from there... - - // Use the pixel shader stored in D3D__RenderState rather than the set handle - // This allows changes made via SetRenderState to actually take effect! - // NOTE: PSTextureModes is in a different location in the X_D3DPIXELSHADERFEF than in Render State mappings - // All other fields are the same. - // We cast D3D__RenderState to a pPSDef for these fields, but - // manually read from D3D__RenderState[X_D3DRS_PSTEXTUREMODES) for that one field. - // See D3DDevice_SetPixelShaderCommon which implements this - - pPSDef = g_pXbox_PixelShader != nullptr ? (XTL::X_D3DPIXELSHADERDEF*)(XboxRenderStates.GetPixelShaderRenderStatePointer()) : nullptr; - - if (pPSDef != nullptr) - { - RecompiledPixelShader = nullptr; - - // Now, see if we already have a shader compiled for this declaration : - for (auto it = g_RecompiledPixelShaders.begin(); it != g_RecompiledPixelShaders.end(); ++it) { - // Only compare parts that form a unique shader (ignore the constants and Direct3D8 run-time fields) : - if ((memcmp(&(it->PSDef.PSAlphaInputs[0]), &(pPSDef->PSAlphaInputs[0]), (8 + 2) * sizeof(DWORD)) == 0) - && (memcmp(&(it->PSDef.PSAlphaOutputs[0]), &(pPSDef->PSAlphaOutputs[0]), (8 + 8 + 3 + 8 + 4) * sizeof(DWORD)) == 0)) { - RecompiledPixelShader = &(*it); - break; - } - } - - // If none was found, recompile this shader and remember it : - if (RecompiledPixelShader == nullptr) { - // Recompile this pixel shader : - g_RecompiledPixelShaders.push_back(DxbxRecompilePixelShader(pPSDef)); - RecompiledPixelShader = &g_RecompiledPixelShaders.back(); - } - - // Switch to the converted pixel shader (if it's any different from our currently active - // pixel shader, to avoid many unnecessary state changes on the local side). - ConvertedPixelShaderHandle = RecompiledPixelShader->ConvertedHandle; - - g_pD3DDevice->GetPixelShader(/*out*/(IDirect3DPixelShader**)(&CurrentPixelShader)); - if (CurrentPixelShader != ConvertedPixelShaderHandle) - g_pD3DDevice->SetPixelShader((IDirect3DPixelShader*)ConvertedPixelShaderHandle); - - // Note : We set the constants /after/ setting the shader, so that any - // constants in the shader declaration can be overwritten (this will be - // needed for the final combiner constants at least)! - - // TODO: Figure out a method to forward the vertex-shader oFog output to the pixel shader FOG input register : - // We could use the unused oT4.x to output fog from the vertex shader, and read it with 'texcoord t4' in pixel shader! - // For now, we still disable native fog if pixel shader is said to handle it, this prevents black screen issues in titles using pixel shader fog. - // NOTE: Disabled: This breaks fog in XDK samples such as DolphinClassic. -#if-0 - if ((RecompiledPixelShader->PSDef.PSFinalCombinerInputsABCD > 0) || (RecompiledPixelShader->PSDef.PSFinalCombinerInputsEFG > 0)) { - g_pD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); - } -#endif - - //PS_TEXTUREMODES psTextureModes[XTL::X_D3DTS_STAGECOUNT]; - //PSH_XBOX_SHADER::GetPSTextureModes(pPSDef, psTextureModes); - // - //for (i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) - //{ - // switch (psTextureModes[i]) - // { - // case PS_TEXTUREMODES_BUMPENVMAP: - // g_pD3DDevice->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP); - // break; - // case PS_TEXTUREMODES_BUMPENVMAP_LUM: - // g_pD3DDevice->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE); - // break; - // default: - // break; - // } - //} - - // Set constants, not based on g_PixelShaderConstants, but based on - // the render state slots containing the pixel shader constants, - // as these could have been updated via SetRenderState or otherwise : - for (i = 0; i < PSH_XBOX_CONSTANT_MAX; i++) - { - if (RecompiledPixelShader->ConstInUse[i]) - { - // Read the color from the corresponding render state slot : - switch (i) { - case PSH_XBOX_CONSTANT_FOG: - // Note : FOG.RGB is correct like this, but FOG.a should be coming - // from the vertex shader (oFog) - however, D3D8 does not forward this... - fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_FOGCOLOR); - break; - case PSH_XBOX_CONSTANT_FC0: - fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSFINALCOMBINERCONSTANT0); - break; - case PSH_XBOX_CONSTANT_FC1: - fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSFINALCOMBINERCONSTANT1); - break; - case PSH_XBOX_CONSTANT_MUL0: - case PSH_XBOX_CONSTANT_MUL1: - continue; - case PSH_XBOX_CONSTANT_BEM + 0: - case PSH_XBOX_CONSTANT_BEM + 1: - case PSH_XBOX_CONSTANT_BEM + 2: - case PSH_XBOX_CONSTANT_BEM + 3: - { - int stage = i - PSH_XBOX_CONSTANT_BEM; - DWORD* value = (DWORD*)&fColor; - - g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT00, &value[0]); - g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT01, &value[1]); - g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT11, &value[2]); - g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT10, &value[3]); - break; - } - case PSH_XBOX_CONSTANT_LUM + 0: - case PSH_XBOX_CONSTANT_LUM + 1: - case PSH_XBOX_CONSTANT_LUM + 2: - case PSH_XBOX_CONSTANT_LUM + 3: - { - int stage = i - PSH_XBOX_CONSTANT_LUM; - DWORD* value = (DWORD*)&fColor; - - g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVLSCALE, &value[0]); - g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVLOFFSET, &value[1]); - value[2] = 0; - value[3] = 0; - break; - } - default: - fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSCONSTANT0_0 + i); - break; - } - - // Convert it back to 4 floats : - //fColor = dwColor; - // Read the register we can use on PC : - Register_ = RecompiledPixelShader->ConstMapping[i]; - // TODO : Avoid the following setter if it's no different from the previous update (this might speed things up) - // Set the value locally in this register : - g_pD3DDevice->SetPixelShaderConstantF - ( - Register_, - (PixelShaderConstantType*)(&fColor), - 1 - ); - } - } - } - else - { - ConvertedPixelShaderHandle = 0; - g_pD3DDevice->SetPixelShader((IDirect3DPixelShader*)ConvertedPixelShaderHandle); - } -} - -// End of Dxbx code - -#define REVEL8N_PIXEL_SHADER_CHANGES - -// help functions -char *pCodeBuffer=nullptr; - -void WriteCode(const char *str, ...) -{ - char szBuffer[256]; - va_list argp; - - va_start(argp, str); - vsprintf(szBuffer, str, argp); - va_end(argp); - - //printf("%s", szBuffer); - if(pCodeBuffer) - strcat(pCodeBuffer, szBuffer); -} - -void InsertString(char *szStr, int iOffset, char *szInsert, int iInsertLen, int iRemoveLen); - -inline void HandleInputOutput -( - DWORD dwInput, - DWORD dwOutput, - BOOL bAlpha, - int iCStage, - BOOL bUniqueC0, - BOOL bUniqueC1, - int *iPSC0, - int *iPSC1, - - BOOL bGlobalRGBA, - - BOOL bFinalCombiner -); - -inline void GetRegister -( - WORD wRegister, - char *szRegister, - BOOL bUniqueC0, - BOOL bUniqueC1, - int iCStage, - int *pPSC0, - int *pPSC1 -); - -inline void GetInputMapping(WORD wInputMapping, char *szInputMapping, char *szInputMappingAfter, char *szConst); -inline void GetChannel(WORD wInputChannel, char *szInput, BOOL bAlpha, BOOL bGlobalRGBA); - -inline void GetOutputFlags -( - WORD wOutputFlags, - char *szInstMod, - char *szABOp, - char *szCDOp, - char *szABCDOp, - - BOOL *bAB_BA, - BOOL *bCD_BA, - - BOOL *bShl1Bias, - BOOL *bBias -); - -//inline BOOL CheckOpForMov(char *szOp, char *szInputs1, char *szInput2, char *szRegInput); -inline BOOL OptimizeOperation -( - char *szOp, - char *szOp1, - - char *szOp2, - char *szMod, - - char *szInputAB1, - char *szInputAB2, - - char *szInputCD1, - char *szInputCD2, - - char *szConstRegAB1, - char *szConstRegAB2, - char *szConstRegCD1, - char *szConstRegCD2, - - char *szOutAB, - char *szOutCD, - char *szABCDOutput, - - char *szCommand -); - -inline void ClearConstRegVars(); -inline void CorrectConstToReg(char *szConst, int *pPSC0, int *pPSC1); - -int iPreRunLen=0; - -// This is set to true if an operation tries to read from r0 -// before r0 was written, in that case we do the same as the xbox -// we write the value of t0.a to r0 ;-) -BOOL bR0WAccess=FALSE; -BOOL bR0Written=FALSE; -BOOL bR0AWritten=FALSE; -/* -BOOL bR1WAccess=FALSE; -BOOL bR1AWAccess=FALSE; -BOOL bR1RGBWAccess=FALSE; - -BOOL bR1AWritten=FALSE; -BOOL bR1RGBWritten=FALSE; -BOOL bR1Written=FALSE; -*/ -BOOL bR0AlphaOutput = FALSE; - -BOOL bLastOpRGB = FALSE; - -BOOL bEFProduct = FALSE; -BOOL bV1R0Reg = FALSE; - -#define DEF_VAR_TABLE_LEN 7 -char szVar[][10] = -{ - "r0", - "r1", - "t0", - "t1", - "t2", - "t3", - "t4" -}; - -inline void HandleInputOutput -( - DWORD dwInput, - DWORD dwOutput, - BOOL bAlpha, - int iCStage, - BOOL bUniqueC0, - BOOL bUniqueC1, - int *iPSC0, - int *iPSC1, - - BOOL bGlobalRGBA, - - BOOL bFinalCombiner -) -{ - // INPUTS - if(bFinalCombiner) printf("\npPSD.PSFinalCombinerInputsABCD = PS_COMBINERINPUTS(\n"); - else if(bAlpha) printf("\npPSD.PSAlphaInputs[%d] = PS_COMBINERINPUTS(\n", iCStage); - else printf("\npPSD.PSRGBInputs[%d] = PS_COMBINERINPUTS(\n", iCStage); - - WORD wCombinerInputs[4]; // 0=a, 1=b, 2=c, 3=d - wCombinerInputs[0] = (WORD) ((dwInput>>24) & 0xFF); - wCombinerInputs[1] = (WORD) ((dwInput>>16) & 0xFF); - wCombinerInputs[2] = (WORD) ((dwInput>> 8) & 0xFF); - wCombinerInputs[3] = (WORD) ( dwInput & 0xFF); - - char szInput[4][20] = {0}; - char szConst[4][20] = {0}; - char szInputMapping[4][20] = {0}; - char szInputMappingAfter[4][20] = {0}; - char szChannels[4][5] = {0}; - - // Go through inputs - int i=0; - for(i=0; i<4; i++) - { - szInput[i][0]=0x00; // Fast way to zero a string ;-) - szConst[i][0]=0x00; - szInputMapping[i][0]=0x00; - szInputMappingAfter[i][0]=0x00; - szChannels[i][0]=0x00; - - GetRegister(wCombinerInputs[i] & 0xF, szInput[i], bUniqueC0, bUniqueC1, iCStage, iPSC0, iPSC1); - - if(strcmp(szInput[i], "r0")==0) - { - if(!bR0AWritten) - strcpy(szInput[i], "t0"); - - if(!bR0Written) { - strcpy(szInput[i], "t0"); - //bR0WAccess=TRUE; - } - } - - printf(" | "); - GetInputMapping(wCombinerInputs[i] & 0x1E0, szInputMapping[i], szInputMappingAfter[i], szConst[i]); - printf(" | "); - GetChannel(wCombinerInputs[i] & 0x10, szChannels[i], bAlpha, bGlobalRGBA); - printf(",\n"); - - if((wCombinerInputs[i] & 0xF)==0x00) - szInput[i][0]=0x00; - - // 6928: check this as I doubt whether it works really like that - /*if(strcmp(szInput[i], "r1")==0) - { - // EmuLog(LOG_LEVEL::DEBUG, "channel: %s", szChannels[i]); - // Sleep(3000); - - if((strcmp(szChannels[i], ".a")==0) && (!bR1AWritten)) { - bR1AWAccess=TRUE; - - strcpy(szInput[i], " t1"); - } else if((strcmp(szChannels[i], ".rgb")==0) && (!bR1RGBWritten)) { - bR1RGBWAccess=TRUE; - - strcpy(szInput[i], " t1"); - } else if(!bR1Written) { - bR1WAccess=TRUE; - - strcpy(szInput[i], " t1"); - } - - if(bR1AWAccess && bR1RGBWAccess) - bR1WAccess=TRUE; - - //if(bR1AWAccess || bR1RGBWAccess) - // strcpy(szInput[i], "t1"); - }*/ - - //printf("\n*** szInput[%d]: %s\n", i, szInput[i]); - } - - // Input stuff - BOOL bInput[4] = {0, 0, 0, 0}; - if(szInput[0][0]) bInput[0]=TRUE; - if(szInput[1][0]) bInput[1]=TRUE; - if(szInput[2][0]) bInput[2]=TRUE; - if(szInput[3][0]) bInput[3]=TRUE; - -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - // Correct param if a constant is used! - if(!bInput[0]) - CorrectConstToReg(szConst[0], iPSC0, iPSC1); - if(!bInput[1]) - CorrectConstToReg(szConst[1], iPSC0, iPSC1); - if(!bInput[2]) - CorrectConstToReg(szConst[2], iPSC0, iPSC1); - if(!bInput[3]) - CorrectConstToReg(szConst[3], iPSC0, iPSC1); - - bool bEmptyChannel = false; -#endif - - char szCompleteInput[4][20] = {0}; - for(i=0; i<4; i++) - { - strcpy(szCompleteInput[i], szInputMapping[i]); -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - if(bInput[i]) - { -#endif - strcat(szCompleteInput[i], szInput[i]); -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - bEmptyChannel = bEmptyChannel || (szChannels[i][0] == 0); - } - else - strcat(szCompleteInput[i], &szConst[i][4]); -#endif - strcat(szCompleteInput[i], szInputMappingAfter[i]); - strcat(szCompleteInput[i], szChannels[i]); - } - - printf(");\n"); - - if(!bFinalCombiner) - { - // OUTPUTS - if(bAlpha) printf("\npPSD.PSAlphaOutputs[%d] = PS_COMBINEROUTPUTS(\n", iCStage); - else printf("\npPSD.PSRGBOutputs[%d] = PS_COMBINEROUTPUTS(\n", iCStage); - - WORD wCombinerOutputs[3]; // 0=d0 (ab), 1=d1 (cd), 2=d2 (mux_sum) - wCombinerOutputs[0] = (WORD) ((dwOutput>> 4) & 0xF); - wCombinerOutputs[1] = (WORD) ( dwOutput & 0xF); - wCombinerOutputs[2] = (WORD) ((dwOutput>> 8) & 0xF); - WORD wCombinerOutputFlags = (WORD) ((dwOutput>>12) & 0xFF); - - char szOutput[3][10] = {0}; - char szOutputMod[10]="\0"; - - char szABOp[10]="\0"; - char szCDOp[10]="\0"; - char szABCDOp[10]="\0"; - - BOOL bAB_B2A; - BOOL bCD_B2A; - - BOOL bR0Now = FALSE; - BOOL bR0ANow = FALSE; - BOOL bVAccess[3] = {0,0,0}; - - BOOL bOpRGB_Current = FALSE; - BOOL bCurrOpRealAlpha = FALSE; - - // Go through outputs - for(i=0; i<3; i++) - { - szOutput[i][0]=0x00; // Fast way to zero a string ;-) - - GetRegister(wCombinerOutputs[i], szOutput[i], bUniqueC0, bUniqueC1, iCStage, iPSC0, iPSC1); - if(strcmp(szOutput[i], "r0")==0) - { - bR0Now=TRUE; - - // this checks for output to r0.a - if(bGlobalRGBA || (!bGlobalRGBA && bAlpha)) - bR0AlphaOutput=TRUE; - } - - if((strcmp(szOutput[i], "v0")==0) || (strcmp(szOutput[i], "v1")==0)) { bVAccess[i] = TRUE; } - - /*BOOL bR1_Written = FALSE; - if(strcmp(szOutput[i], "r1")==0) - bR1_Written=TRUE;*/ - - // check channel! - if(!bGlobalRGBA && bAlpha) - { - strcat(szOutput[i], ".a"); - bCurrOpRealAlpha = TRUE; - - if(bR0Now) - bR0ANow=TRUE; - - /*if(bR1_Written) - bR1AWritten=TRUE;*/ - } - else if(!bGlobalRGBA && !bAlpha -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - && !bEmptyChannel -#endif - ) - { - strcat(szOutput[i], ".rgb"); - - if(wCombinerOutputs[i]) - bOpRGB_Current = TRUE; - - /*if(bR1_Written) - bR1RGBWritten=TRUE;*/ - } - else - { - /*if(bR1_Written) - bR1Written=TRUE;*/ - - if(bR0Now) - bR0ANow=TRUE; - } - - printf(",\n"); - - if(wCombinerOutputs[i]==0x00) - szOutput[i][0]=0x00; - - //printf("\n*** szOutput[%d]: %s\n", i, szOutput[i]); - } - - BOOL bBias=FALSE; - BOOL bSh1Bias=FALSE; - - GetOutputFlags( - wCombinerOutputFlags, - szOutputMod, - - szABOp, - szCDOp, - szABCDOp, - - &bAB_B2A, - &bCD_B2A, - - &bSh1Bias, - &bBias); - - if(bR0Now) - bR0Written=TRUE; - - if(bR0ANow) - bR0AWritten=TRUE; - - printf(");\n"); - - // Find output for the operations - char szOut[10]="\0"; - char szOut1[10]="\0"; - - //printf("|****| %s |****|\n", szOutput[1]); - - if(szOutput[0][0]) - strcpy(szOut, szOutput[0]); - if(szOutput[1][0]) - strcpy(szOut1, szOutput[1]); - -#ifndef REVEL8N_PIXEL_SHADER_CHANGES - if(szOutput[2][0]) - { - /* - //EmuWarningMsg("THIS IS WRONG, FIX ME!"); - //if(!szOutput[1][0]) - // strcpy(szOut1, szOutput[2]); - EmuLog(LOG_LEVEL::DEBUG, "(!szOutput[0][0] || !szOutput[1][0]) && szOutput[2][0] = TRUE!"); - - BOOL bUsable=TRUE; - for(i=2; i<4; i++) - { - if((strcmp(szOutput[2], szInput[i])==0) || (strcmp(szOutput[2], szOut1)==0)) { - bUsable=FALSE; - } - } - if(bUsable && !szOutput[0][0]) - { - - strcpy(szOut, szOutput[2]); - - EmuLog(LOG_LEVEL::DEBUG, "BUsable = TRUE, new output: %s", szOut); - - } - else { - printf("!WARNING!: The operation uses the output register also as input!" - "Trying to find a free output register. It is possible that the pixel shader " - "will generate garbage because the new free one contains data used " - "in an other comming operation!\n\n"); - - for(int j=0; j> 24) & 0xFF); - wEFG[1] = (WORD) ((dwOutput >> 16) & 0xFF); - wEFG[2] = (WORD) ((dwOutput >> 8) & 0xFF); - - BOOL bInputEFG[3] = {0, 0, 0}; - char szCompleteInputEFG[3][10]; - - char szInputEFG[3][10]; - char szInputMappingEFG[3][10]; - char szInputMappingAfterEFG[3][10]; - char szConstEFG[3][10]; - - for(i=0; i<3; i++) - { - szInputEFG[i][0]=0x00; - szInputMappingEFG[i][0]=0x00; - szInputMappingAfterEFG[i][0]=0x00; - szConstEFG[i][0]=0x00; - - GetRegister(wEFG[i] & 0xF, szInputEFG[i], bUniqueC0, bUniqueC1, 0, iPSC0, iPSC1); - printf(" | "); - GetInputMapping(wEFG[i] & 0x1E0, szInputMappingEFG[i], szInputMappingAfterEFG[i], szConstEFG[i]); - printf(" | "); - GetChannel(wEFG[i] & 0x10, szInputEFG[i], bAlpha, FALSE); - printf(", \n"); - - strcpy(szCompleteInputEFG[i], szInputMappingEFG[i]); - strcat(szCompleteInputEFG[i], szInputEFG[i]); - strcat(szCompleteInputEFG[i], szInputMappingAfterEFG[i]); - - if(szInputEFG[i][0]) - bInputEFG[i]=TRUE; - else - { - // add that constant as a reg - CorrectConstToReg(szConstEFG[i], iPSC0, iPSC1); - } - } - - if(dwV1R0_EFProd_Flags & 0x20) - printf("PS_FINALCOMBINERSETTINGS_COMPLEMENT_R0"); - else if(dwV1R0_EFProd_Flags & 0x40) - printf("PS_FINALCOMBINERSETTINGS_COMPLEMENT_V1"); - else if(dwV1R0_EFProd_Flags & 0x80) - printf("PS_FINALCOMBINERSETTINGS_CLAMP_SUM"); - else - printf("0"); - - printf(");\n"); - - if (bV1R0Reg) - { - char sMod[10] = {0}; - char sV1[10] = {0}; - char sR0[10] = {0}; - if(dwV1R0_EFProd_Flags & 0x20) - strcpy(sR0, "1-"); - else if(dwV1R0_EFProd_Flags & 0x40) - strcpy(sV1, "1-"); - else if(dwV1R0_EFProd_Flags & 0x80) - strcpy(sMod, "_sat"); - - if (bEFProduct) - { - EmuLog(LOG_LEVEL::WARNING, "EF Product and V1R0 register used at the same time!"); - } - else - { - WriteCode("; (v1 + r0)\nadd%s r0, %sr0, %sv1\n\n", sMod, sR0, sV1); - } - } - - // only we we will use this later in final combiner stuff!! - // all inputs are known now, so check: - if(bEFProduct) { - - // r0 = E * F (E or F must be the r0 calculated before otherwise the stage results - // are lost, problem??? - if(! - ((!bInputEFG[0] && szConstEFG[0][0]=='0') && - (!bInputEFG[1] && szConstEFG[1][0]=='0'))) { - WriteCode(";E * F\nmul r0, %s, %s\n\n", bInputEFG[0] ? szCompleteInputEFG[0] : &szConstEFG[0][4], - bInputEFG[1] ? szCompleteInputEFG[1] : &szConstEFG[1][4]); - } - - } - - // Now the result: - - // What is done by the final combiner: - // final color = s0*s1 + (1-s0)*s2 + s3 - - // lrp r0, s0, s1, s2 - // add r0, r0, s3 - // s0 = szInput[0] - // s1 = szInput[1] - // s2 = szInput[2] - // s3 = szInput[3] - - // Check whether it is a mov r0, r0 - // for example: lrp r0, 1, r0, 0 - // r0 = 1*r0 + (1-1)*r0 + 0 - // --> r0 = r0 - - for(i=0; i<4; i++) - { - if(!bInput[i]) - CorrectConstToReg(szConst[i], iPSC0, iPSC1); - } - - if(!((!bInput[0]) && (szConst[0][0] == '1') && (strncmp(szCompleteInput[1], "r0", 2)==0))) - { - // cases for s2 - // s2 == 0 --> final color = s0*s1 + s3 - if((!bInput[2]) && (szConst[2][0] == '0')) - { - WriteCode("mul r0.rgb, %s, %s\n", - bInput[0] ? szCompleteInput[0] : &szConst[0][4], - bInput[1] ? szCompleteInput[1] : &szConst[1][4]); - } - // s0 == 0 --> final color = s2 + s3 - else if((!bInput[0]) && (szConst[0][0] == '0')) { - // Check whether s2 is r0!!! - if(!(bInput[2] && (strncmp(szCompleteInput[2], "r0", 2)==0))) - WriteCode("mov r0.rgb, %s\n", - bInput[2] ? szCompleteInput[2] : &szConst[2][4]); - } - // s0 == 1 --> final color = s1 + s3 - else if((!bInput[0]) && (szConst[0][0] == '1')) { - // Check whether s1 is r0!!! - if(!(bInput[1] && (strncmp(szCompleteInput[1], "r0", 2)==0))) - WriteCode("mov r0.rgb, %s\n", - bInput[1] ? szCompleteInput[1] : &szConst[1][4]); - } - // no special cases - else if(bInput[2] || bInput[0]) - { - WriteCode("lrp r0.rgb, %s, %s, %s\n", - bInput[0] ? szCompleteInput[0] : &szConst[0][4], - bInput[1] ? szCompleteInput[1] : &szConst[1][4], - bInput[2] ? szCompleteInput[2] : &szConst[2][4]); - } - } - // case for s3 - if(bInput[3] || (szConst[3][0] != '0')) - WriteCode("add r0.rgb, r0, %s\n", bInput[3] ? szCompleteInput[3] : &szConst[3][4]); - - // Alpha ouput (G) - if(bInputEFG[2] && (strncmp(szInputEFG[2], "r0", 2)!=0)) - { - bR0AlphaOutput=TRUE; - - WriteCode("mov r0.a, %s\n", - bInputEFG[2] ? szCompleteInputEFG[2] : &szConstEFG[2][4]); - } - - //else - // WriteCode("mov r0.a, v0.a\n"); - //*/ - //Sleep(3000); - } -} - -inline void GetRegister(WORD wRegister, char *szRegister, BOOL bUniqueC0, BOOL bUniqueC1, int iCStage, int *iPSC0, int *iPSC1) -{ - // Determine register - switch(wRegister) - { - case 0x00: - printf("PS_REGISTER_ZERO"); - break; - case 0x01: // read - printf("PS_REGISTER_C0"); - if(bUniqueC0) - sprintf(szRegister, "c%d", iPSC0[iCStage]); - else - strcpy(szRegister, "c0"); - break; - case 0x02: // read - printf("PS_REGISTER_C1"); - if(bUniqueC0) - sprintf(szRegister, "c%d", iPSC1[iCStage]); - else - strcpy(szRegister, "c1"); - break; - case 0x03: // read - { - printf("PS_REGISTER_FOG"); - - char szOneHalf[40] = "0.5\0"; - CorrectConstToReg(szOneHalf, iPSC0, iPSC1); - - strcpy(szRegister, &szOneHalf[4]); // Unsupported - break; - } - case 0x04: // read/(write ???) - printf("PS_REGISTER_V0"); - strcpy(szRegister, "v0"); - break; - case 0x05: // read/(write ???) - printf("PS_REGISTER_V1"); - strcpy(szRegister, "v1"); - break; - case 0x08: // read/write - printf("PS_REGISTER_T0"); - strcpy(szRegister, "t0"); - //strcpy(szRegister, "r2"); - break; - case 0x09: // read/write - printf("PS_REGISTER_T1"); - strcpy(szRegister, "t1"); - //strcpy(szRegister, "r3"); - break; - case 0x0A: // read/write - printf("PS_REGISTER_T2"); - strcpy(szRegister, "t2"); - //strcpy(szRegister, "r4"); - break; - case 0x0B: // read/write - printf("PS_REGISTER_T3"); - strcpy(szRegister, "t3"); - //strcpy(szRegister, "r5"); - break; - case 0x0C: // read/write - printf("PS_REGISTER_R0"); - strcpy(szRegister, "r0"); - break; - case 0x0D: // read/write - printf("PS_REGISTER_R1"); - strcpy(szRegister, "r1"); - break; - case 0x0E: // read - printf("PS_REGISTER_V1R0_SUM"); - - bV1R0Reg = TRUE; - strcpy(szRegister, "r0"); //"V1R0");//(v1+r0)"); - break; - case 0x0F: - printf("PS_REGISTER_EF_PROD"); - - // we save it in r0 - bEFProduct = TRUE; - strcpy(szRegister, "r0");/* e * f --> combiner input */ - break; - default: - printf("/*Unknown register %d*/", wRegister); - break; - } -} - -inline void GetInputMapping(WORD wInputMapping, char *szInputMapping, char *szInputMappingAfter, char *szConst) -{ - strcpy(szConst, "0"); - switch(wInputMapping) - { - case 0x00: // max(0,x) [ok for final combiner] - printf("PS_INPUTMAPPING_UNSIGNED_IDENTITY"); - break; - case 0x20: // 1 - max(0,x) [ok for final combiner] - printf("PS_INPUTMAPPING_UNSIGNED_INVERT"); - strcpy(szInputMapping, "1-"); - strcpy(szConst, "1"); - break; - case 0x40: // 2*max(0,x) - 1 [invalid for final combiner] - printf("PS_INPUTMAPPING_EXPAND_NORMAL"); - strcpy(szInputMappingAfter, "_bx2"); // right??? - strcpy(szConst, "-1"); - break; - case 0x60: // 1 - 2*max(0,x) [invalid for final combiner] - printf("PS_INPUTMAPPING_EXPAND_NEGATE"); - - strcpy(szInputMapping, "-"); - strcpy(szInputMappingAfter, "_bx2"); - strcpy(szConst, "1"); - break; - case 0x80: // max(0,x) - 1/2 [invalid for final combiner] - printf("PS_INPUTMAPPING_HALFBIAS_NORMAL"); - strcpy(szInputMappingAfter, "_bias"); - - strcpy(szConst, "-0.5"); - break; - case 0xA0: // 1/2 - max(0,x) [invalid for final combiner] - printf("PS_INPUTMAPPING_HALFBIAS_NEGATE"); - - strcpy(szConst, "0.5"); - - // Negate is run last if combined with bias - strcpy(szInputMapping, "-"); - strcpy(szInputMappingAfter, "_bias"); - break; - case 0xC0: // x [invalid for final combiner] - printf("PS_INPUTMAPPING_SIGNED_IDENTITY"); - break; - case 0xE0: // -x [invalid for final combiner] - printf("PS_INPUTMAPPING_SIGNED_NEGATE"); - strcpy(szInputMapping, "-"); - break; - default: - printf("/*Unknown input mapping %d!*/", wInputMapping); - break; - } -} - -inline void GetChannel(WORD wInputChannel, char *szInput, BOOL bAlpha, BOOL bGlobalRGBA) -{ - switch(wInputChannel) - { - case 0x00: - if(bAlpha) { - printf("PS_CHANNEL_BLUE"); - strcat(szInput, ".b"); - } else { - printf("PS_CHANNEL_RGB"); - - //if (!bGlobalRGBA) - // strcat(szInput, ".rgb"); - } - break; - case 0x10: - printf("PS_CHANNEL_ALPHA"); - - // TODO: check this || !bAlpha, it should mean that alpha channel - // is detected in a RGB register, then it must be set also - // if both commands the same are (in that case it has to be RGB!) - if (!bGlobalRGBA || !bAlpha) - strcat(szInput, ".a"); - break; - default: - printf("/*Unknown channel %d!*/", wInputChannel); - break; - } -} - -inline void GetOutputFlags -( - WORD wOutputFlags, - char *szInstMod, - char *szABOp, - char *szCDOp, - char *szABCDOp, - - BOOL *bAB_BA, - BOOL *bCD_BA, - - BOOL *bShl1Bias, - BOOL *bBias -) -{ - // Output mapping - switch (wOutputFlags & 0x38) { - case PS_COMBINEROUTPUT_BIAS: - { - printf("PS_COMBINEROUTPUT_BIAS"); // y = x - 0.5 - //strcpy(szInstMod, "_bias"); - - // Only over this: - // mov y, y_bias - (*bBias)=TRUE; - break; - } - case PS_COMBINEROUTPUT_SHIFTLEFT_1: // 0x10L - { - printf("PS_COMBINEROUTPUT_SHIFTLEFT_1"); // y = x*2 - strcpy(szInstMod, "_x2"); - break; - } - case PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS: // 0x18L - { - LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS"); - printf("PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS"); // y = (x - 0.5)*2 - - //strcpy(szInstMod, "_x2"); - // what is missing is a subtraction of 1 - // --> 2 * (x - 0.5) = 2x - 1 - - // But this won't work because we would have to do 2 movs - // to subtract 1 - // Let's do this: mov_x2 y, y_bias - (*bShl1Bias)=TRUE; - break; - } - case PS_COMBINEROUTPUT_SHIFTLEFT_2: // 0x20L - { - LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTLEFT_2"); - printf("PS_COMBINEROUTPUT_SHIFTLEFT_2"); // y = x*4 - strcpy(szInstMod, "_x4"); - break; - } - // case PS_COMBINEROUTPUT_SHIFTLEFT_2_BIAS: // 0x28L, // y = (x - 0.5)*4 - case PS_COMBINEROUTPUT_SHIFTRIGHT_1: // 0x30L - { - LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTRIGHT_1"); - printf("PS_COMBINEROUTPUT_SHIFTRIGHT_1"); // y = x/2 - strcpy(szInstMod, "_d2"); - break; - } - // case PS_COMBINEROUTPUT_SHIFTRIGHT_1_BIAS: // 0x38L, // y = (x - 0.5)/2 - default: - printf("PS_COMBINEROUTPUT_IDENTITY"); - } - - printf(" | "); - - // MUX operation - if(wOutputFlags & 0x04) { - printf("PS_COMBINEROUTPUT_AB_CD_MUX"); - strcpy(szABCDOp, "cnd"); - - if((!bR0Written) || (!bR0AWritten)) - bR0WAccess=TRUE; - } - else - { - printf("PS_COMBINEROUTPUT_AB_CD_SUM"); // 3rd output is AB+CD - strcpy(szABCDOp, "add"); - } - - printf(" | "); - - // Function for ab side - if(wOutputFlags & 0x02) - { - printf("PS_COMBINEROUTPUT_AB_DOT_PRODUCT"); // RGB only - strcpy(szABOp, "dp3"); - } else { - printf("PS_COMBINEROUTPUT_AB_MULTIPLY"); - strcpy(szABOp, "mul"); - } - - printf(" | "); - - // Functiomn for cd side - if(wOutputFlags & 0x01) - { - printf("!!!PS_COMBINEROUTPUT_CD_DOT_PRODUCT!!!"); // RGB only - strcpy(szCDOp, "dp3"); - } else { - printf("PS_COMBINEROUTPUT_CD_MULTIPLY"); - strcpy(szCDOp, "mul"); - } - - // Blue to alpha for ab side - if(wOutputFlags & 0x80) { - printf(" | PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA"); // RGB only - (*bAB_BA)=TRUE; - } else (*bAB_BA)=FALSE; - - // Blue to alpha for cd side - if(wOutputFlags & 0x40) { - printf(" | PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA"); // RGB only - (*bCD_BA)=TRUE; - } else (*bCD_BA)=FALSE; -} - -enum OpType -{ - OPTYPE_NOP = -1, - OPTYPE_MOV = 0, - OPTYPE_ADD, - OPTYPE_MUL, - OPTYPE_DP3, - OPTYPE_CND, -}; - -inline BOOL OptimizeOperation -( - char *szOp, - char *szOp1, - - char *szOp2, - char *szMod, - - char *szInputAB1, - char *szInputAB2, - - char *szInputCD1, - char *szInputCD2, - - char *szConstRegAB1, - char *szConstRegAB2, - char *szConstRegCD1, - char *szConstRegCD2, - - char *szOutAB, - char *szOutCD, - char *szABCDOutput, - - char *szCommand) -{ - printf("----------\nszOp: |%s|\nszOp1: |%s|\nszOp2: |%s|\nszMod: |%s|\n" - "szInputAB1: |%s|\nszInputAB2: |%s|\nszInputCD1: |%s|\nszInputCD2: |%s|\n" - "szOutAB: |%s|\nszOutCD: |%s|\nszABCDOutput: |%s|\n", - szOp, szOp1, szOp2, szMod, szInputAB1, szInputAB2, szInputCD1, szInputCD2, - szOutAB, szOutCD, szABCDOutput); - - char szABCDInput[2][10]; - szABCDInput[0][0]=0x00; - szABCDInput[1][0]=0x00; - - szCommand[0]=0x00; - - char *szOps[3]; - szOps[0] = szOp; - szOps[1] = szOp1; - szOps[2] = szOp2; - - char *szInputs[4]; - szInputs[0] = szInputAB1; - szInputs[1] = szInputAB2; - szInputs[2] = szInputCD1; - szInputs[3] = szInputCD2; - - char *szRealInputs[4]; - szRealInputs[0] = szConstRegAB1; - szRealInputs[1] = szConstRegAB2; - szRealInputs[2] = szConstRegCD1; - szRealInputs[3] = szConstRegCD2; - -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - char *szOutputs[3]; - szOutputs[0] = szOutAB; - szOutputs[1] = szOutCD; - szOutputs[2] = szABCDOutput; -#endif - - // TODO: check mov: other operations like lrp - // are ignored because of a shitty mul with 1 - BOOL bMov[3]={0, 0, 0}; - - int i=0; - for(i=0; i<2; i++) - { - //printf("szOps[i]: %s\n", szOps[i]); - //printf("szInputs[i*2+1]: %s\n", szInputs[i*2+1]); - if(strcmp(szOps[i], "mul")==0) - { - // If it is a mul, it can also be only a mov - if(strcmp(szInputs[i*2], "1")==0) { - //strcpy(szABCDInput[i], szInputs[i*2+1]); -#ifndef REVEL8N_PIXEL_SHADER_CHANGES - strcpy(szABCDInput[i], szRealInputs[i*2+1]); -#endif - - strcpy(szOps[i], "mov"); - - strcpy(szInputs[i*2], szInputs[i*2+1]); - strcpy(szRealInputs[i*2], szRealInputs[i*2+1]); - - strcpy(szInputs[i*2+1], ""); - strcpy(szRealInputs[i*2+1], ""); - - bMov[i]=TRUE; - - } else if(strcmp(szInputs[i*2+1], "1")==0) { - //strcpy(szABCDInput[i], szInputs[i*2]); -#ifndef REVEL8N_PIXEL_SHADER_CHANGES - strcpy(szABCDInput[i], szRealInputs[i*2]); -#endif - - strcpy(szOps[i], "mov"); - - strcpy(szInputs[i*2+1], ""); - strcpy(szRealInputs[i*2+1], ""); - - bMov[i]=TRUE; - } - } - } - -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - OpType eOpTypes[3] = {OPTYPE_NOP, OPTYPE_NOP, OPTYPE_NOP}; - for (i = 0; i < 3; ++i) - { - if (strcmp(szOps[i], "mov") == 0) - eOpTypes[i] = OPTYPE_MOV; - else if (strcmp(szOps[i], "add") == 0) - eOpTypes[i] = OPTYPE_ADD; - else if (strcmp(szOps[i], "mul") == 0) - eOpTypes[i] = OPTYPE_MUL; - else if (strcmp(szOps[i], "dp3") == 0) - eOpTypes[i] = OPTYPE_DP3; - else if (strcmp(szOps[i], "cnd") == 0) - eOpTypes[i] = OPTYPE_CND; - else - eOpTypes[i] = OPTYPE_NOP; - } - - bool bHandled = false; - int iOffset = 0; - int iOpCount = 0; - if (szOps[2][0] && szOutputs[2][0] && szOutputs[2][0] != 'v') - { - if (!szOutputs[0][0] && - !szOutputs[1][0]) - { - if (szMod[0]) - { - EmuLog(LOG_LEVEL::WARNING, "Destination modifier present!"); - } - switch (eOpTypes[2]) - { - case OPTYPE_ADD: - { - if (eOpTypes[0] == OPTYPE_MOV && - eOpTypes[1] == OPTYPE_MOV) - { - iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, %s\n", - szMod, szOutputs[2], szRealInputs[0], szRealInputs[2]); - ++iOpCount; - bHandled = true; - } - else if (eOpTypes[0] == OPTYPE_MOV && - eOpTypes[1] == OPTYPE_MUL) - { - iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", - szMod, szOutputs[2], szRealInputs[2], szRealInputs[3], szRealInputs[0]); - bHandled = true; - ++iOpCount; - } - else if (eOpTypes[0] == OPTYPE_MUL && - eOpTypes[1] == OPTYPE_MOV) - { - iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", - szMod, szOutputs[2], szRealInputs[0], szRealInputs[1], szRealInputs[2]); - bHandled = true; - ++iOpCount; - } - else if (eOpTypes[0] == OPTYPE_MUL && - eOpTypes[1] == OPTYPE_MUL) - { - // nice, mul, mul, add can be converted to lrp - // lrp r0, t0, t1, c2 - // --> r0 = t0 * t1 + (1-t0) * c2 - // or r0 = c2 + t0 * (t1 - c2), but that would mean we have to mul in the ABCD op - // and that is not possible - - for(i=0; i<2; i++) - { - // To match the first option, the first input of the AB/CD op must inverted - BOOL bInvert[2] = {0, 0}; - if((szRealInputs[2*i][0] == '1') && (szRealInputs[2*i][1] == '-')) - //if((szInputs[2*i][0] == '1') && (szInputs[2*i][1] == '-')) - bInvert[0]=TRUE; - - if((szRealInputs[2*i+1][0] == '1') && (szRealInputs[2*i+1][1] == '-')) - //if((szInputs[2*i+1][0] == '1') && (szInputs[2*i+1][1] == '-')) - bInvert[1]=TRUE; - - //printf("szInputs[2*i]: %s\nszInputs[2*i+1]: %s\n", szInputs[2*i], szInputs[2*i+1]); - //printf("bInvert[0]: %d\nbInvert[1]: %d\n", bInvert[0], bInvert[1]); - - if((bInvert[0] || bInvert[1]) && (!(bInvert[0] && bInvert[1]))) - { - char szParam[3][10] = {0}; - char szRealParam0[10] = {0}; - if(bInvert[0]) - { - // copy over the not inverted param - strcpy(szParam[i+1], /*szInputs*/szRealInputs[2*i+1]); - - // and the inverted - strcpy(szParam[0], &szInputs[2*i][2]); - strcpy(szRealParam0, &szRealInputs[2*i][2]); - } - else if(bInvert[1]) - { - // copy over the not inverted param - strcpy(szParam[i+1], /*szInputs*/szRealInputs[2*i]); - - // and the inverted - strcpy(szParam[0], &szInputs[2*i+1][2]); - strcpy(szRealParam0, &szRealInputs[2*i+1][2]); - } - int iOtherOp = i == 0 ? 1 : 0; - - bHandled = true; - if (strcmp(szRealInputs[2*iOtherOp], szRealParam0/*szParam[0]*/)==0) - strcpy(szParam[iOtherOp+1], /*szInputs*/szRealInputs[2*iOtherOp+1]); - else if (strcmp(szRealInputs[2*iOtherOp+1], szRealParam0/*szParam[0]*/)==0) - strcpy(szParam[iOtherOp+1], /*szInputs*/szRealInputs[2*iOtherOp]); - else - bHandled = false; - if (bHandled) - { - // ok, we have it - iOffset += sprintf(szCommand, "lrp%s %s, %s, %s, %s\n", - szMod, szABCDOutput, szRealParam0/*szParam[0]*/, szParam[1], szParam[2]); - ++iOpCount; - break; - } - } - } - - if (!bHandled) - { - iOffset += sprintf(szCommand + iOffset, "mul r1, %s, %s\n", - szRealInputs[0], szRealInputs[1]); - ++iOpCount; - iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, r1\n", - szMod, szOutputs[2], szRealInputs[2], szRealInputs[3]); - ++iOpCount; - - bHandled = true; - } - } - } - break; - case OPTYPE_CND: - { - if (eOpTypes[0] == OPTYPE_MOV && - eOpTypes[1] == OPTYPE_MOV) - { - iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, %s\n", - szMod, szOutputs[2], szRealInputs[2], szRealInputs[0]); - ++iOpCount; - bHandled = true; - } - else if (eOpTypes[0] == OPTYPE_MUL && - eOpTypes[1] == OPTYPE_MUL) - { - if (szOutputs[2][0] != 'r') - { - EmuLog(LOG_LEVEL::WARNING, "Destination not temporary register!"); - } - // ab input - iOffset += sprintf(szCommand + iOffset, "mul%s r1, %s, %s\n", - szMod, szRealInputs[0], szRealInputs[1]); - ++iOpCount; - // cd input - iOffset += sprintf(szCommand + iOffset, "mul%s r0, %s, %s\n", - szMod, szRealInputs[2], szRealInputs[3]); - ++iOpCount; - // abcd output - iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, r0, r1\n", - szMod, szOutputs[2]); - ++iOpCount; - bHandled = true; - } - } - break; - } - if (!bHandled && strcmp(szOps[2], "add") == 0) - { - if ((strcmp(szOps[0], "mov")==0)) - { - if ((strcmp(szOps[1], "mul")==0)) - { - char szParam[10]="\0"; - - if(strcmp(szInputCD1, "-1")==0) - strcpy(szParam, szInputCD2); - else if(strcmp(szInputCD2, "-1")==0) - strcpy(szParam, szInputCD1); - - if(szParam[0] && szConstRegAB1[0] && szABCDOutput[0]) - { - iOffset += sprintf(szCommand, "sub%s %s, %s, %s\n", - szMod, szABCDOutput, szConstRegAB1, szParam); - bHandled = true; - ++iOpCount; - } -// else -// { -// iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", -// szMod, szOutputs[2], szRealInputs[2], szRealInputs[3], szRealInputs[0]); -// bHandled = true; -// ++iOpCount; -// } - } - } -// else if ((strcmp(szOps[0], "mul")==0)) -// { -// if ((strcmp(szOps[1], "mov")==0)) -// { -// iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", -// szMod, szOutputs[2], szRealInputs[0], szRealInputs[1], szRealInputs[2]); -// bHandled = true; -// ++iOpCount; -// } -// else if ((strcmp(szOps[1], "mul")==0)) -// { -// // nice, mul, mul, add can be converted to lrp -// // lrp r0, t0, t1, c2 -// // --> r0 = t0 * t1 + (1-t0) * c2 -// // or r0 = c2 + t0 * (t1 - c2), but that would mean we have to mul in the ABCD op -// // and that is not possible -// -// for(i=0; i<2; i++) -// { -// // To match the first option, the first input of the AB/CD op must inverted -// BOOL bInvert[2] = {0, 0}; -// if((szRealInputs[2*i][0] == '1') && (szRealInputs[2*i][1] == '-')) -// //if((szInputs[2*i][0] == '1') && (szInputs[2*i][1] == '-')) -// bInvert[0]=TRUE; -// -// if((szRealInputs[2*i+1][0] == '1') && (szRealInputs[2*i+1][1] == '-')) -// //if((szInputs[2*i+1][0] == '1') && (szInputs[2*i+1][1] == '-')) -// bInvert[1]=TRUE; -// -// //printf("szInputs[2*i]: %s\nszInputs[2*i+1]: %s\n", szInputs[2*i], szInputs[2*i+1]); -// //printf("bInvert[0]: %d\nbInvert[1]: %d\n", bInvert[0], bInvert[1]); -// -// if((bInvert[0] || bInvert[1]) && (!(bInvert[0] && bInvert[1]))) -// { -// char szParam[3][10]; -// char szRealParam0[10]; -// if(bInvert[0]) -// { -// // copy over the not inverted param -// strcpy(szParam[2], /*szInputs*/szRealInputs[2*i+1]); -// -// // and the inverted -// strcpy(szParam[0], &szInputs[2*i][2]); -// strcpy(szRealParam0, &szRealInputs[2*i][2]); -// } -// else if(bInvert[1]) -// { -// // copy over the not inverted param -// strcpy(szParam[2], /*szInputs*/szRealInputs[2*i]); -// -// // and the inverted -// strcpy(szParam[0], &szInputs[2*i+1][2]); -// strcpy(szRealParam0, &szRealInputs[2*i+1][2]); -// } -// int iOtherOp = i == 0 ? 1 : 0; -// -// bHandled = true; -// if (strcmp(szRealInputs[2*iOtherOp], szRealParam0/*szParam[0]*/)==0) -// strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp+1]); -// else if (strcmp(szRealInputs[2*iOtherOp+1], szRealParam0/*szParam[0]*/)==0) -// strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp]); -// else -// bHandled = false; -// if (bHandled) -// { -// // ok, we have it -// iOffset += sprintf(szCommand, "lrp%s %s, %s, %s, %s\n", -// szMod, szABCDOutput, szRealParam0/*szParam[0]*/, szParam[1], szParam[2]); -// ++iOpCount; -// break; -// } -// } -// } -// -// if (!bHandled) -// { -// iOffset += sprintf(szCommand + iOffset, "mul r1, %s, %s\n", -// szRealInputs[0], szRealInputs[1]); -// ++iOpCount; -// iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, r1\n", -// szMod, szOutputs[2], szRealInputs[2], szRealInputs[3]); -// ++iOpCount; -// -// bHandled = true; -// } -// } -// } - } - } - } - - if (!bHandled) - { - for (i = 0; i < 2; ++i) - { - if (szOps[i][0] && szOutputs[i][0] && szOutputs[i][0] != 'v') - { - ++iOpCount; - // copy output value to final input - strcpy(szABCDInput[i], szOutputs[i]); - // insert command - iOffset += sprintf(szCommand + iOffset, "%s%s %s, %s\n", szOps[i], szMod, szOutputs[i], szRealInputs[i * 2 + 0]); - - // if there are more parameters... - if (szRealInputs[i * 2 + 1][0]) - { - // backspace of the newline character - --iOffset; - // insert remaining parameters - iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[i * 2 + 1]); - } - bHandled = true; - } - } - -// if (szOutputs[2][0]) -// { -// if(!szOutputs[1][0]) -// strcpy(szOutputs[1], "r0"); -// if(!szOutputs[0][0]) -// strcpy(szOutputs[0], "r1"); -// } - - if (szOps[2][0] && szOutputs[2][0] && szOutputs[2][0] != 'v') - { - switch (eOpTypes[2]) - { - case OPTYPE_ADD: - { - if (szABCDInput[0][0] && - szABCDInput[1][0]) - { - iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, %s\n", - szMod, szOutputs[2], szABCDInput[0], szABCDInput[1]); - ++iOpCount; - bHandled = true; - } - else if (szABCDInput[0][0] && - !szABCDInput[1][0]) - { - switch (eOpTypes[1]) - { - case OPTYPE_MUL: - { - iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", - szMod, szOutputs[2], szRealInputs[2], szRealInputs[3], szABCDInput[0]); - ++iOpCount; - bHandled = true; - } - break; - case OPTYPE_DP3: - { - { - ++iOpCount; - // insert command - iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[1], szMod, szRealInputs[2]); - - // if there are more parameters... - if (szRealInputs[3][0]) - { - // backspace of the newline character - --iOffset; - // insert remaining parameters - iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[3]); - } - } - { - iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, r1\n", - szMod, szOutputs[2], szABCDInput[0]); - ++iOpCount; - bHandled = true; - } - } - break; - default: - break; - } - } - else if (!szABCDInput[0][0] && - szABCDInput[1][0]) - { - switch (eOpTypes[0]) - { - case OPTYPE_MUL: - { - iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", - szMod, szOutputs[2], szRealInputs[0], szRealInputs[1], szABCDInput[1]); - ++iOpCount; - bHandled = true; - } - break; - case OPTYPE_DP3: - { - { - ++iOpCount; - // insert command - iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[0], szMod, szRealInputs[0]); - - // if there are more parameters... - if (szRealInputs[1][0]) - { - // backspace of the newline character - --iOffset; - // insert remaining parameters - iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[1]); - } - } - { - iOffset += sprintf(szCommand + iOffset, "add%s %s, r1, %s\n", - szMod, szOutputs[2], szABCDInput[1]); - ++iOpCount; - bHandled = true; - } - } - break; - default: - break; - } - } - } - break; - case OPTYPE_CND: - { - if (szABCDInput[0][0] && - szABCDInput[1][0]) - { - iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, %s\n", - szMod, szOutputs[2], szABCDInput[1], szABCDInput[0]); - ++iOpCount; - bHandled = true; - } - else if (szABCDInput[0][0] && - !szABCDInput[1][0]) - { - { - ++iOpCount; - // insert command - iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[1], szMod, szRealInputs[2]); - - // if there are more parameters... - if (szRealInputs[3][0]) - { - // backspace of the newline character - --iOffset; - // insert remaining parameters - iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[3]); - } - } - { - iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, r1, %s\n", - szMod, szOutputs[2], szABCDInput[0]); - ++iOpCount; - bHandled = true; - } - } - else if (!szABCDInput[0][0] && - szABCDInput[1][0]) - { - { - ++iOpCount; - // insert command - iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[0], szMod, szRealInputs[0]); - - // if there are more parameters... - if (szRealInputs[1][0]) - { - // backspace of the newline character - --iOffset; - // insert remaining parameters - iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[1]); - } - } - { - iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, r1\n", - szMod, szOutputs[2], szABCDInput[1]); - ++iOpCount; - bHandled = true; - } - } - } - break; - } - if (!bHandled) - { - EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); - } -// if (strcmp(szOps[2], "add") == 0) -// { -// if (szABCDInput[0][0] && -// szABCDInput[1][0]) -// { -// iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, %s\n", -// szMod, szOutputs[2], szABCDInput[1], szABCDInput[0]); -// ++iOpCount; -// bHandled = true; -// } -// else -// { -// EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); -// } -// } -// else if (strcmp(szOps[2], "cnd") == 0) -// { -// if (szABCDInput[0][0] && -// szABCDInput[1][0]) -// { -// iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, %s\n", -// szMod, szOutputs[2], szABCDInput[1], szABCDInput[0]); -// ++iOpCount; -// bHandled = true; -// } -// else -// { -// EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); -// } -// } -// else -// { -// EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); -// } - } - } - - if(szCommand[0]) - printf("new command:\n%s\n", szCommand); - return (bHandled && (iOpCount == 1)) ? (TRUE) : (FALSE); -#endif - - if( - (strcmp(szOp, "mul")==0) && - (strcmp(szOp1, "mov")==0) && //bMov[1] && - (strcmp(szOp2, "add")==0) && - szABCDOutput[0]) - { - sprintf(szCommand, "mad%s %s, %s, %s, %s\n", - szMod, szABCDOutput, - /*szInput*/szConstRegAB1, - /*szInput*/szConstRegAB2, - /*szInput*/szConstRegCD1 /*because it's a mov now*/); - } - else if( - (strcmp(szOp, "mul")==0) && - (strcmp(szOp1, "mul")==0) && - (strcmp(szOp2, "add")==0) && - szABCDOutput[0]) // TODO: check that strange lrp/ABCDOutput[0]=0 case - { - // nice, mul, mul, add can be converted to lrp - // lrp r0, t0, t1, c2 - // --> r0 = t0 * t1 + (1-t0) * c2 - // or r0 = c2 + t0 * (t1 - c2), but that would mean we have to mul in the ABCD op - // and that is not possible - - for(i=0; i<2; i++) - { - // To match the first option, the first input of the AB/CD op must inverted - BOOL bInvert[2] = {0, 0}; - if((szInputs[2*i][0] == '1') && (szInputs[2*i][1] == '-')) - bInvert[0]=TRUE; - - if((szInputs[2*i+1][0] == '1') && (szInputs[2*i+1][1] == '-')) - bInvert[1]=TRUE; - - //printf("szInputs[2*i]: %s\nszInputs[2*i+1]: %s\n", szInputs[2*i], szInputs[2*i+1]); - //printf("bInvert[0]: %d\nbInvert[1]: %d\n", bInvert[0], bInvert[1]); - - if((bInvert[0] || bInvert[1]) && (!(bInvert[0] && bInvert[1]))) - { - char szParam[3][10]; - char szRealParam0[10]; - if(bInvert[0]) - { - // copy over the not inverted param - strcpy(szParam[2], /*szInputs*/szRealInputs[2*i+1]); - - // and the inverted - strcpy(szParam[0], &szInputs[2*i][2]); - strcpy(szRealParam0, &szRealInputs[2*i][2]); - } - else if(bInvert[1]) - { - // copy over the not inverted param - strcpy(szParam[2], /*szInputs*/szRealInputs[2*i]); - - // and the inverted - strcpy(szParam[0], &szInputs[2*i+1][2]); - strcpy(szRealParam0, &szRealInputs[2*i+1][2]); - } - int iOtherOp = i == 0 ? 1 : 0; - - if(strcmp(szInputs[2*iOtherOp], szParam[0])==0) - strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp+1]); - else - strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp]); - // ok, we have it - sprintf(szCommand, "lrp%s %s, %s, %s, %s\n", - szMod, szABCDOutput, szRealParam0/*szParam[0]*/, szParam[1], szParam[2]); - - break; - } - } - } else if(strcmp(szOp2, "cnd")==0) { -#ifdef REVEL8N_PIXEL_SHADER_CHANGES - iOffset = 0; - i = 0; - for (i = 0; i < 2; ++i) - { - if (strcmp(szOps[i], "mul")==0) - { - strcpy(szABCDInput[i], szOutputs[i]); - iOffset += sprintf(szCommand + iOffset, "mul %s, %s, %s\n", szOutputs[i], szRealInputs[i * 2 + 0], szRealInputs[i * 2 + 1]); - } - } - sprintf(szCommand + iOffset, "cnd%s %s, %s, %s, %s\n", - szMod, szABCDOutput, "r0.a", szABCDInput[1], szABCDInput[0]); -#else - sprintf(szCommand, "cnd%s %s, %s, %s, %s\n", - szMod, szABCDOutput, "r0.a", szABCDInput[1], szABCDInput[0]); -#endif - - bMov[1]=0; - bMov[0]=0; - } else if( - (strcmp(szOp, "mov")==0) && - (strcmp(szOp1, "mul")==0) && - (strcmp(szOp2, "add")==0)) - { - char szParam[10]="\0"; - - if(strcmp(szInputCD1, "-1")==0) - strcpy(szParam, szInputCD2); - else if(strcmp(szInputCD2, "-1")==0) - strcpy(szParam, szInputCD1); - - if(szParam[0] && szConstRegAB1[0] && szABCDOutput[0]) - { - sprintf(szCommand, "sub%s %s, %s, %s\n", - szMod, szABCDOutput, szConstRegAB1, szParam); - } - - } -//do_operation_with_new_input: - - if(bMov[0] && bMov[1] && szABCDOutput[0]) { - sprintf(szCommand, "%s%s %s, %s, %s\n", szOp2, szMod, szABCDOutput, szABCDInput[0], szABCDInput[1]); - } - - if(szCommand[0]) - printf("new command: %s", szCommand); - return TRUE; -} - -float fConstants[20] = {0.0f}; -int iConstants[20] = {0}; -int iConstCount=0; - -inline void ClearConstRegVars() -{ - iConstCount=0; - memset(fConstants, 0x00, 20*sizeof(float)); - memset(iConstants, 0x00, 20*sizeof(int)); -} - -inline void CorrectConstToReg(char *szConst, int *pPSC0, int *pPSC1) -{ - printf("Looking for %s\n", szConst); - float fConst = (float)atof(szConst); - - // check whether we already saved it - int i=0; - for(i=0; iPSAlphaInputs[0], pPSDef->PSAlphaInputs[1], pPSDef->PSAlphaInputs[2], pPSDef->PSAlphaInputs[3], - pPSDef->PSAlphaInputs[4], pPSDef->PSAlphaInputs[5], pPSDef->PSAlphaInputs[6], pPSDef->PSAlphaInputs[7], - pPSDef->PSFinalCombinerInputsABCD, - pPSDef->PSFinalCombinerInputsEFG, - pPSDef->PSConstant0[0], pPSDef->PSConstant0[1], pPSDef->PSConstant0[2], pPSDef->PSConstant0[3], - pPSDef->PSConstant0[4], pPSDef->PSConstant0[5], pPSDef->PSConstant0[6], pPSDef->PSConstant0[7], - pPSDef->PSConstant1[0], pPSDef->PSConstant1[1], pPSDef->PSConstant1[2], pPSDef->PSConstant1[3], - pPSDef->PSConstant1[4], pPSDef->PSConstant1[5], pPSDef->PSConstant1[6], pPSDef->PSConstant1[7], - pPSDef->PSAlphaOutputs[0], pPSDef->PSAlphaOutputs[1], pPSDef->PSAlphaOutputs[2], pPSDef->PSAlphaOutputs[3], - pPSDef->PSAlphaOutputs[4], pPSDef->PSAlphaOutputs[5], pPSDef->PSAlphaOutputs[6], pPSDef->PSAlphaOutputs[7], - pPSDef->PSRGBInputs[0], pPSDef->PSRGBInputs[1], pPSDef->PSRGBInputs[2], pPSDef->PSRGBInputs[3], - pPSDef->PSRGBInputs[4], pPSDef->PSRGBInputs[5], pPSDef->PSRGBInputs[6], pPSDef->PSRGBInputs[7], - pPSDef->PSCompareMode, - pPSDef->PSFinalCombinerConstant0, - pPSDef->PSFinalCombinerConstant1, - pPSDef->PSRGBOutputs[0], pPSDef->PSRGBOutputs[1], pPSDef->PSRGBOutputs[2], pPSDef->PSRGBOutputs[3], - pPSDef->PSRGBOutputs[4], pPSDef->PSRGBOutputs[5], pPSDef->PSRGBOutputs[6], pPSDef->PSRGBOutputs[7], - pPSDef->PSCombinerCount, - XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES), /* pPSDef->PSTextureModes is stored in a different place than pPSDef*/ - pPSDef->PSDotMapping, - pPSDef->PSInputTexture, - pPSDef->PSC0Mapping, - pPSDef->PSC1Mapping, - pPSDef->PSFinalCombinerConstants ); - if (pszCode) - { - fprintf(out, "\n\n%s\n", pszCode); - } - - fclose( out ); - } -} - -// print relevant contents to the debug console -void PrintPixelShaderDefContents(XTL::X_D3DPIXELSHADERDEF* pPSDef ) -{ - // Show the contents to the user - if( pPSDef ) - { - DbgPshPrintf( "\n-----PixelShader Def Contents-----\n" ); - - if(XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES)) - { - DWORD dwPSTexMode0 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 0 ) & 0x1F; - DWORD dwPSTexMode1 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 5 ) & 0x1F; - DWORD dwPSTexMode2 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 10 ) & 0x1F; - DWORD dwPSTexMode3 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 15 ) & 0x1F; - - DbgPshPrintf( "PSTextureModes ->\n" ); - DbgPshPrintf( "Stage 0: %s\n", PS_TextureModesStr[dwPSTexMode0] ); - DbgPshPrintf( "Stage 1: %s\n", PS_TextureModesStr[dwPSTexMode1] ); - DbgPshPrintf( "Stage 2: %s\n", PS_TextureModesStr[dwPSTexMode2] ); - DbgPshPrintf( "Stage 3: %s\n", PS_TextureModesStr[dwPSTexMode3] ); - } - - if( pPSDef->PSDotMapping ) - { - DWORD dwPSDMStage1 = ( pPSDef->PSDotMapping >> 0 ) & 0x7; - DWORD dwPSDMStage2 = ( pPSDef->PSDotMapping >> 4 ) & 0x7; - DWORD dwPSDMStage3 = ( pPSDef->PSDotMapping >> 8 ) & 0x7; - - DbgPshPrintf( "PSDotMapping ->\n" ); - DbgPshPrintf( "Stage 1: %s\n", PS_DotMappingStr[dwPSDMStage1] ); - DbgPshPrintf( "Stage 2: %s\n", PS_DotMappingStr[dwPSDMStage2] ); - DbgPshPrintf( "Stage 3: %s\n", PS_DotMappingStr[dwPSDMStage3] ); - } - - if( pPSDef->PSCompareMode ) - { - DWORD dwPSCMStage0 = ( pPSDef->PSCompareMode >> 0 ) & 0xF; - DWORD dwPSCMStage1 = ( pPSDef->PSCompareMode >> 4 ) & 0xF; - DWORD dwPSCMStage2 = ( pPSDef->PSCompareMode >> 8 ) & 0xF; - DWORD dwPSCMStage3 = ( pPSDef->PSCompareMode >> 12 ) & 0xF; - - DbgPshPrintf( "PSCompareMode ->\n" ); - DbgPshPrintf( "Stage 0: %s\n", PS_TextureModesStr[dwPSCMStage0 == 0 ? 0 : 1] ); - DbgPshPrintf( "Stage 1: %s\n", PS_TextureModesStr[dwPSCMStage1 == 0 ? 2 : 3] ); - DbgPshPrintf( "Stage 2: %s\n", PS_TextureModesStr[dwPSCMStage2 == 0 ? 4 : 5] ); - DbgPshPrintf( "Stage 3: %s\n", PS_TextureModesStr[dwPSCMStage3 == 0 ? 6 : 7] ); - } - - if( pPSDef->PSInputTexture ) - { - DWORD dwPSITStage2 = ( pPSDef->PSInputTexture >> 16 ) & 0x1; - DWORD dwPSITStage3 = ( pPSDef->PSInputTexture >> 20 ) & 0x3; - - DbgPshPrintf( "PSInputTexture ->\n" ); - DbgPshPrintf( "Stage 2: %s\n", PS_TextureModesStr[dwPSITStage2] ); - DbgPshPrintf( "Stage 3: %s\n", PS_TextureModesStr[dwPSITStage3] ); - } - - if( pPSDef->PSCombinerCount ) - { - DWORD dwPSCCNumCombiners = ( pPSDef->PSCombinerCount >> 0 ) & 0xF; - DWORD dwPSCCMux = ( pPSDef->PSCombinerCount >> 8 ) & 0x1; - DWORD dwPSCCC0 = ( pPSDef->PSCombinerCount >> 12 ) & 0x1; - DWORD dwPSCCC1 = ( pPSDef->PSCombinerCount >> 16 ) & 0x1; - - DbgPshPrintf( "PSCombinerCount ->\n" ); - DbgPshPrintf( "Combiners: %d\n", dwPSCCNumCombiners ); - DbgPshPrintf( "Mux: %s\n", PS_CombinerCountFlagsStr[dwPSCCMux] ); - DbgPshPrintf( "C0: %s\n", PS_CombinerCountFlagsStr[dwPSCCC0 == 0 ? 2 : 3] ); - DbgPshPrintf( "C1: %s\n", PS_CombinerCountFlagsStr[dwPSCCC1 == 0 ? 4 : 5] ); - } - - /*for( int i = 0; i > 7; i++ ) - { - if( pPSDef->PSRGBInputs[i] ) - {*/ - } -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 kingofc +// * +// * All rights reserved +// * +// ****************************************************************** + +/* + + This is a parser for the Xbox Pixel Shader Format. + + With the help of this parser it is possible to generate + Direct3D pixel shader assembly code. + + TODO: + - fix BumpDemo + (after second recompilation the shader does not work, + can also be something in CxbxKrnl because it looks like no + textures are set. Check cubemap loading from resourcesd!!!) + => seems to work now, the problem is that I don't know + how it must look on a real xbox + + - add reference counting constants which were added as c variables + if they are compiled away (optimization of the command, etc.) + decrement the reference count and when it reaches 0 remove + the constant (to save the num of vars) + + - add _sat feature + * Support as instruction modifier, + if necessary as mov_sat x, y + + - When porting to DirectX 9, expand this to pixel shader model 2.0 or up + - Alternatively, translate to HLSL and let D3DXCompileShader/D3DCompile figure it out +*/ + +#define LOG_PREFIX CXBXR_MODULE::PXSH + +#include "core\kernel\support\Emu.h" +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice, g_pXbox_PixelShader +#include "core\hle\D3D8\XbPixelShader.h" +#include "core\hle\D3D8\XbD3D8Logging.h" // For D3DErrorString() + +#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup() + +#include // assert() +#include +#include + +#include "Direct3D9\RenderStates.h" +extern XboxRenderStateConverter XboxRenderStates; + +#define DbgPshPrintf \ + LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \ + if(g_bPrintfOn) printf + + +/*---------------------------------------------------------------------------*/ +/* Texture configuration - The following members of the D3DPixelShaderDef */ +/* structure define the addressing modes of each of the four texture stages:*/ +/* PSTextureModes */ +/* PSDotMapping */ +/* PSInputTexture */ +/* PSCompareMode */ +/*---------------------------------------------------------------------------*/ + +// ========================================================================================================= +// PSTextureModes +// --------.--------.--------.---xxxxx stage0 +// --------.--------.------xx.xxx----- stage1 +// --------.--------.-xxxxx--.-------- stage2 +// --------.----xxxx.x-------.-------- stage3 + +#define PS_TEXTUREMODES(t0,t1,t2,t3) (((t3)<<15)|((t2)<<10)|((t1)<<5)|(t0)) + +/* +Texture modes: +NONE :stage inactive +PROJECT2D :argb = texture(s/q, t/q) +PROJECT3D :argb = texture(s/q, t/q, r/q) +CUBEMAP :argb = cubemap(s,t,r) +PASSTHRU :argb = s,t,r,q +CLIPPLANE :pixel not drawn if s,t,r, or q < 0. PSCompareMode affects comparison +BUMPENVMAP :argb=texture(s+mat00*src.r+mat01*src.g, + t+mat10*src.r+mat11*src.g) + mat00 set via D3DTSS_BUMPENVMAT00, etc. +BUMPENVMAP_LUM :argb=texture(s+mat00*src.r+mat01*src.g, + t+mat10*src.r+mat11*src.g); + rgb *= (lum_scale*src.b + lum_bias); (a is not affected) + lum_scale set by D3DTSS_BUMPENVLSCALE + lum_bias set by D3DTSS_BUMPENVLOFFSET + mat00 set via D3DTSS_BUMPENVMAT00, etc. +BRDF :argb = texture(eyeSigma, lightSigma, dPhi) + eyeSigma = Sigma of eye vector in spherical coordinates + lightSigma = Sigma of light vector in spherical coordinates + dPhi = Phi of eye - Phi of light +DOT_ST :argb = texture(, (s,t,r).(src.r,src.g,src.b)) +DOT_ZW :frag depth = (/((s,t,r).(src.r,src.g,src.b)) +DOT_RFLCT_DIFF :n = (,(s,t,r).(src.r,src.g,src.b),) + argb = cubemap(n) +DOT_RFLCT_SPEC :n = (,,(s,t,r).(src.r,src.g,src.b)) + r = 2*n*(n.e)/(n.n) - e where e is eye vector built from q coord of each stage + argb = cubemap(r) +DOT_STR_3D :argb=texture((,,(s,t,r).(src.r,src.g,src.b))) +DOT_STR_CUBE :argb=cubemap((,,(s,t,r).(src.r,src.g,src.b))) +DEPENDENT_AR :argb = texture(src.a, src.r) +DEPENDENT_GB :argb = texture(src.g, src.b) +DOTPRODUCT :argb = (s,t,r).(src.r,src.g,src.b) +DOT_RFLCT_SPEC_CONST :n = (,,(s,t,r).(src.r,src.g,src.b)) + r = 2*n*(n.e)/(n.n) - e where e is eye vector set via SetEyeVector() + argb = cubemap(r) +*/ + +enum PS_TEXTUREMODES +{ // valid in stage 0 1 2 3 + PS_TEXTUREMODES_NONE= 0x00L, // * * * * + PS_TEXTUREMODES_PROJECT2D= 0x01L, // * * * * + PS_TEXTUREMODES_PROJECT3D= 0x02L, // * * * * + PS_TEXTUREMODES_CUBEMAP= 0x03L, // * * * * + PS_TEXTUREMODES_PASSTHRU= 0x04L, // * * * * + PS_TEXTUREMODES_CLIPPLANE= 0x05L, // * * * * + PS_TEXTUREMODES_BUMPENVMAP= 0x06L, // - * * * + PS_TEXTUREMODES_BUMPENVMAP_LUM= 0x07L, // - * * * + PS_TEXTUREMODES_BRDF= 0x08L, // - - * * + PS_TEXTUREMODES_DOT_ST= 0x09L, // - - * * + PS_TEXTUREMODES_DOT_ZW= 0x0aL, // - - * * + PS_TEXTUREMODES_DOT_RFLCT_DIFF= 0x0bL, // - - * - + PS_TEXTUREMODES_DOT_RFLCT_SPEC= 0x0cL, // - - - * + PS_TEXTUREMODES_DOT_STR_3D= 0x0dL, // - - - * + PS_TEXTUREMODES_DOT_STR_CUBE= 0x0eL, // - - - * + PS_TEXTUREMODES_DPNDNT_AR= 0x0fL, // - * * * + PS_TEXTUREMODES_DPNDNT_GB= 0x10L, // - * * * + PS_TEXTUREMODES_DOTPRODUCT= 0x11L, // - * * - + PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST= 0x12L, // - - - * + // 0x13-0x1f reserved +}; + +// ========================================================================================================= +// PSDotMapping +// --------.--------.--------.-----xxx // stage1 +// --------.--------.--------.-xxx---- // stage2 +// --------.--------.-----xxx.-------- // stage3 + +#define PS_DOTMAPPING(t0,t1,t2,t3) (((t3)<<8)|((t2)<<4)|(t1)) + +// Mappings: +// ZERO_TO_ONE :rgb->(r,g,b): 0x0=>0.0, 0xff=>1.0 +// MINUS1_TO_1_D3D :rgb->(r,g,b): 0x0=>-128/127, 0x01=>-1.0, 0x80=>0.0, 0xff=>1.0 +// MINUS1_TO_1_GL :rgb->(r,g,b): 0x80=>-1.0, 0x0=>0.0, 0x7f=>1.0 +// MINUS1_TO_1 :rgb->(r,g,b): 0x80=>-128/127, 0x81=>-1.0, 0x0=>0.0, 0x7f=>1.0 +// HILO_1 :HL->(H,L,1.0): 0x0000=>0.0, 0xffff=>1.0 +// HILO_HEMISPHERE :HL->(H,L,sqrt(1-H*H-L*L)): 0x8001=>-1.0, 0x0=>0.0, 0x7fff=>1.0, 0x8000=>-32768/32767 + +enum PS_DOTMAPPING +{ // valid in stage 0 1 2 3 + PS_DOTMAPPING_ZERO_TO_ONE= 0x00L, // - * * * + PS_DOTMAPPING_MINUS1_TO_1_D3D= 0x01L, // - * * * + PS_DOTMAPPING_MINUS1_TO_1_GL= 0x02L, // - * * * + PS_DOTMAPPING_MINUS1_TO_1= 0x03L, // - * * * + PS_DOTMAPPING_HILO_1= 0x04L, // - * * * + // ? 0x05L ? + // ? 0x06L ? + PS_DOTMAPPING_HILO_HEMISPHERE= 0x07L, // - * * * +}; + +// ========================================================================================================= +// PSCompareMode +// --------.--------.--------.----xxxx // stage0 +// --------.--------.--------.xxxx---- // stage1 +// --------.--------.----xxxx.-------- // stage2 +// --------.--------.xxxx----.-------- // stage3 + +#define PS_COMPAREMODE(t0,t1,t2,t3) (((t3)<<12)|((t2)<<8)|((t1)<<4)|(t0)) + +enum PS_COMPAREMODE +{ + PS_COMPAREMODE_S_LT= 0x00L, + PS_COMPAREMODE_S_GE= 0x01L, + + PS_COMPAREMODE_T_LT= 0x00L, + PS_COMPAREMODE_T_GE= 0x02L, + + PS_COMPAREMODE_R_LT= 0x00L, + PS_COMPAREMODE_R_GE= 0x04L, + + PS_COMPAREMODE_Q_LT= 0x00L, + PS_COMPAREMODE_Q_GE= 0x08L, +}; + +// ========================================================================================================= +// PSInputTexture +// --------.-------x.--------.-------- // stage2 +// --------.--xx----.--------.-------- // stage3 +// +// Selects the other texture to use as an input in the following texture modes: +// DOT_ST, DOT_STR_3D, DOT_STR_CUBE, DOT_ZW, DOT_RFLCT_SPEC, +// DOT_RFLCT_DIFF, DPNDNT_AR, DPNDNT_GB, BUMPENVMAP, +// BUMPENVMAP_LUM, DOT_PRODUCT + +#define PS_INPUTTEXTURE(t0,t1,t2,t3) (((t3)<<20)|((t2)<<16)) + + +/*---------------------------------------------------------------------------------*/ +/* Color combiners - The following members of the D3DPixelShaderDef structure */ +/* define the state for the eight stages of color combiners: */ +/* PSCombinerCount - Number of stages */ +/* PSAlphaInputs[8] - Inputs for alpha portion of each stage */ +/* PSRGBInputs[8] - Inputs for RGB portion of each stage */ +/* PSConstant0[8] - Constant 0 for each stage */ +/* PSConstant1[8] - Constant 1 for each stage */ +/* PSFinalCombinerConstant0 - Constant 0 for final combiner */ +/* PSFinalCombinerConstant1 - Constant 1 for final combiner */ +/* PSAlphaOutputs[8] - Outputs for alpha portion of each stage */ +/* PSRGBOutputs[8] - Outputs for RGB portion of each stage */ +/*---------------------------------------------------------------------------------*/ + + +// ========================================================================================================= +// PSCombinerCount +// --------.--------.--------.----xxxx // number of combiners (1-8) +// --------.--------.-------x.-------- // mux bit (0= LSB, 1= MSB) +// --------.--------.---x----.-------- // separate C0 +// --------.-------x.--------.-------- // separate C1 + +#define PS_COMBINERCOUNT(count, flags) (((flags)<<8)|(count)) +// count is 1-8, flags contains one or more values from PS_COMBINERCOUNTFLAGS + +enum PS_COMBINERCOUNTFLAGS +{ + PS_COMBINERCOUNT_MUX_LSB= 0x0000L, // mux on r0.a lsb + PS_COMBINERCOUNT_MUX_MSB= 0x0001L, // mux on r0.a msb + + PS_COMBINERCOUNT_SAME_C0= 0x0000L, // c0 same in each stage + PS_COMBINERCOUNT_UNIQUE_C0= 0x0010L, // c0 unique in each stage + + PS_COMBINERCOUNT_SAME_C1= 0x0000L, // c1 same in each stage + PS_COMBINERCOUNT_UNIQUE_C1= 0x0100L // c1 unique in each stage +}; + +// ========================================================================================================= +// PSRGBInputs[0-7] +// PSAlphaInputs[0-7] +// PSFinalCombinerInputsABCD +// PSFinalCombinerInputsEFG +// --------.--------.--------.----xxxx // D register +// --------.--------.--------.---x---- // D channel (0= RGB/BLUE, 1= ALPHA) +// --------.--------.--------.xxx----- // D input mapping +// --------.--------.----xxxx.-------- // C register +// --------.--------.---x----.-------- // C channel (0= RGB/BLUE, 1= ALPHA) +// --------.--------.xxx-----.-------- // C input mapping +// --------.----xxxx.--------.-------- // B register +// --------.---x----.--------.-------- // B channel (0= RGB/BLUE, 1= ALPHA) +// --------.xxx-----.--------.-------- // B input mapping +// ----xxxx.--------.--------.-------- // A register +// ---x----.--------.--------.-------- // A channel (0= RGB/BLUE, 1= ALPHA) +// xxx-----.--------.--------.-------- // A input mapping + +// examples: +// +// shader.PSRGBInputs[3]= PS_COMBINERINPUTS( +// PS_REGISTER_T0 | PS_INPUTMAPPING_EXPAND_NORMAL | PS_CHANNEL_RGB, +// PS_REGISTER_C0 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_ALPHA, +// PS_REGISTER_ZERO, +// PS_REGISTER_ZERO); +// +// shader.PSFinalCombinerInputsABCD= PS_COMBINERINPUTS( +// PS_REGISTER_T0 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_ALPHA, +// PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL | PS_CHANNEL_RGB, +// PS_REGISTER_EFPROD | PS_INPUTMAPPING_UNSIGNED_INVERT | PS_CHANNEL_RGB, +// PS_REGISTER_ZERO); +// +// PS_FINALCOMBINERSETTING is set in 4th field of PSFinalCombinerInputsEFG with PS_COMBINERINPUTS +// example: +// +// shader.PSFinalCombinerInputsEFG= PS_COMBINERINPUTS( +// PS_REGISTER_R0 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_RGB, +// PS_REGISTER_R1 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_RGB, +// PS_REGISTER_R1 | PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_CHANNEL_BLUE, +// PS_FINALCOMBINERSETTING_CLAMP_SUM | PS_FINALCOMBINERSETTING_COMPLEMENT_R0); + +#define PS_COMBINERINPUTS(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) +// For PSFinalCombinerInputsEFG, +// a,b,c contain a value from PS_REGISTER, PS_CHANNEL, and PS_INPUTMAPPING for input E,F, and G +// d contains values from PS_FINALCOMBINERSETTING +// For all other inputs, +// a,b,c,d each contain a value from PS_REGISTER, PS_CHANNEL, and PS_INPUTMAPPING + +// The input can have the following mappings applied : +// +// PS_INPUTMAPPING_UNSIGNED_IDENTITY : y = max(0,x) = 1*max(0,x) + 0.0 +// PS_INPUTMAPPING_UNSIGNED_INVERT : y = 1 - max(0,x) = -1*max(0,x) + 1.0 +// PS_INPUTMAPPING_EXPAND_NORMAL : y = 2*max(0,x) - 1 = 2*max(0,x) - 1.0 +// PS_INPUTMAPPING_EXPAND_NEGATE : y = 1 - 2*max(0,x) = -2*max(0,x) + 1.0 +// PS_INPUTMAPPING_HALFBIAS_NORMAL : y = max(0,x) - 1/2 = 1*max(0,x) - 0.5 +// PS_INPUTMAPPING_HALFBIAS_NEGATE : y = 1/2 - max(0,x) = -1*max(0,x) + 0.5 +// PS_INPUTMAPPING_SIGNED_IDENTITY : y = x = 1* x + 0.0 +// PS_INPUTMAPPING_SIGNED_NEGATE : y = -x = -1* x + 0.0 +// +// (Note : I don't know for sure if the max() operation mentioned above is indeed what happens, +// as there's no further documentation available on this. Native Direct3D can clamp with the +// '_sat' instruction modifier, but that's not really the same as these Xbox1 input mappings.) +// +// When the input register is PS_ZERO, the above mappings result in the following constants: +// +// PS_REGISTER_NEGATIVE_ONE (PS_INPUTMAPPING_EXPAND_NORMAL on zero) : y = -1.0 +// PS_REGISTER_NEGATIVE_ONE_HALF (PS_INPUTMAPPING_HALFBIAS_NORMAL on zero) : y = -0.5 +// PS_REGISTER_ZERO itself : y = 0.0 +// PS_REGISTER_ONE_HALF (PS_INPUTMAPPING_HALFBIAS_NEGATE on zero) : y = 0.5 +// PS_REGISTER_ONE (PS_INPUTMAPPING_UNSIGNED_INVERT on zero) : y = 1.0 +// (Note : It has no define, but PS_INPUTMAPPING_EXPAND_NEGATE on zero results in ONE too!) + +enum PS_INPUTMAPPING +{ + PS_INPUTMAPPING_UNSIGNED_IDENTITY= 0x00L, // max(0,x) OK for final combiner: y = abs(x) + PS_INPUTMAPPING_UNSIGNED_INVERT= 0x20L, // 1 - max(0,x) OK for final combiner: y = 1 - x + PS_INPUTMAPPING_EXPAND_NORMAL= 0x40L, // 2*max(0,x) - 1 invalid for final combiner + PS_INPUTMAPPING_EXPAND_NEGATE= 0x60L, // 1 - 2*max(0,x) invalid for final combiner + PS_INPUTMAPPING_HALFBIAS_NORMAL= 0x80L, // max(0,x) - 1/2 invalid for final combiner + PS_INPUTMAPPING_HALFBIAS_NEGATE= 0xa0L, // 1/2 - max(0,x) invalid for final combiner + PS_INPUTMAPPING_SIGNED_IDENTITY= 0xc0L, // x invalid for final combiner + PS_INPUTMAPPING_SIGNED_NEGATE= 0xe0L, // -x invalid for final combiner +}; + +enum PS_REGISTER +{ + PS_REGISTER_ZERO= 0x00L, // r + PS_REGISTER_DISCARD= 0x00L, // w + PS_REGISTER_C0= 0x01L, // r + PS_REGISTER_C1= 0x02L, // r + PS_REGISTER_FOG= 0x03L, // r + PS_REGISTER_V0= 0x04L, // r/w + PS_REGISTER_V1= 0x05L, // r/w + PS_REGISTER_T0= 0x08L, // r/w + PS_REGISTER_T1= 0x09L, // r/w + PS_REGISTER_T2= 0x0aL, // r/w + PS_REGISTER_T3= 0x0bL, // r/w + PS_REGISTER_R0= 0x0cL, // r/w + PS_REGISTER_R1= 0x0dL, // r/w + PS_REGISTER_V1R0_SUM= 0x0eL, // r + PS_REGISTER_EF_PROD= 0x0fL, // r + + PS_REGISTER_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // 0x20 OK for final combiner + PS_REGISTER_NEGATIVE_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL, // 0x40 invalid for final combiner + PS_REGISTER_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NEGATE, // 0xa0 invalid for final combiner + PS_REGISTER_NEGATIVE_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NORMAL, // 0x80 invalid for final combiner + + PS_REGISTER_CXBX_PROD = PS_REGISTER_ZERO | PS_INPUTMAPPING_SIGNED_IDENTITY, // Cxbx internal use +}; + +// FOG ALPHA is only available in final combiner +// V1R0_SUM and EF_PROD are only available in final combiner (A,B,C,D inputs only) +// V1R0_SUM_ALPHA and EF_PROD_ALPHA are not available +// R0_ALPHA is initialized to T0_ALPHA in stage0 + +enum PS_CHANNEL +{ + PS_CHANNEL_RGB= 0x00, // used as RGB source + PS_CHANNEL_BLUE= 0x00, // used as ALPHA source + PS_CHANNEL_ALPHA= 0x10, // used as RGB or ALPHA source +}; + +constexpr DWORD PS_ChannelMask = (DWORD)PS_CHANNEL_ALPHA; +constexpr DWORD PS_NoChannelMask = (DWORD)(~PS_ChannelMask); +constexpr DWORD PS_AlphaChannelsMask = (DWORD)(PS_ChannelMask | (PS_ChannelMask << 8) | (PS_ChannelMask << 16) | (PS_ChannelMask << 24)); +constexpr DWORD PS_NoChannelsMask = (DWORD)(~PS_AlphaChannelsMask); + +enum PS_FINALCOMBINERSETTING +{ + PS_FINALCOMBINERSETTING_CLAMP_SUM= 0x80, // V1+R0 sum clamped to [0,1] + PS_FINALCOMBINERSETTING_COMPLEMENT_V1= 0x40, // unsigned invert mapping (1 - v1) is used as an input to the sum rather than v1 + PS_FINALCOMBINERSETTING_COMPLEMENT_R0= 0x20, // unsigned invert mapping (1 - r0) is used as an input to the sum rather than r0 +}; + +// ========================================================================================================= +// PSRGBOutputs[0-7] +// PSAlphaOutputs[0-7] +// --------.--------.--------.----xxxx // CD register +// --------.--------.--------.xxxx---- // AB register +// --------.--------.----xxxx.-------- // SUM register +// --------.--------.---x----.-------- // CD output (0= multiply, 1= dot product) +// --------.--------.--x-----.-------- // AB output (0= multiply, 1= dot product) +// --------.--------.-x------.-------- // AB_CD mux/sum select (0= sum, 1= mux) +// --------.------xx.x-------.-------- // Output mapping +// --------.-----x--.--------.-------- // CD blue to alpha +// --------.----x---.--------.-------- // AB blue to alpha + +#define PS_COMBINEROUTPUTS(ab,cd,mux_sum,flags) (((flags)<<12)|((mux_sum)<<8)|((ab)<<4)|(cd)) +// ab,cd,mux_sum contain a value from PS_REGISTER +// flags contains values from PS_COMBINEROUTPUT + +enum PS_COMBINEROUTPUT +{ + PS_COMBINEROUTPUT_IDENTITY= 0x00L, // y = x + PS_COMBINEROUTPUT_BIAS= 0x08L, // y = x - 0.5 + PS_COMBINEROUTPUT_SHIFTLEFT_1= 0x10L, // y = x*2 + PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS= 0x18L, // y = (x - 0.5)*2 + PS_COMBINEROUTPUT_SHIFTLEFT_2= 0x20L, // y = x*4 + // PS_COMBINEROUTPUT_SHIFTLEFT_2_BIAS= 0x28L, // y = (x - 0.5)*4 + PS_COMBINEROUTPUT_SHIFTRIGHT_1= 0x30L, // y = x/2 + // PS_COMBINEROUTPUT_SHIFTRIGHT_1_BIAS= 0x38L, // y = (x - 0.5)/2 + + PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA= 0x80L, // RGB only + + PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA= 0x40L, // RGB only + + PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L, + PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only + + PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L, + PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only + + PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD + PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a +}; + +// AB_CD register output must be DISCARD if either AB_DOT_PRODUCT or CD_DOT_PRODUCT are set + +// ========================================================================================================= +// PSC0Mapping +// PSC1Mapping +// --------.--------.--------.----xxxx // offset of D3D constant for stage 0 +// --------.--------.--------.xxxx---- // offset of D3D constant for stage 1 +// --------.--------.----xxxx.-------- // offset of D3D constant for stage 2 +// --------.--------.xxxx----.-------- // offset of D3D constant for stage 3 +// --------.----xxxx.--------.-------- // offset of D3D constant for stage 4 +// --------.xxxx----.--------.-------- // offset of D3D constant for stage 5 +// ----xxxx.--------.--------.-------- // offset of D3D constant for stage 6 +// xxxx----.--------.--------.-------- // offset of D3D constant for stage 7 + +#define PS_CONSTANTMAPPING(s0,s1,s2,s3,s4,s5,s6,s7) \ + (((DWORD)(s0)&0xf)<< 0) | (((DWORD)(s1)&0xf)<< 4) | \ + (((DWORD)(s2)&0xf)<< 8) | (((DWORD)(s3)&0xf)<<12) | \ + (((DWORD)(s4)&0xf)<<16) | (((DWORD)(s5)&0xf)<<20) | \ + (((DWORD)(s6)&0xf)<<24) | (((DWORD)(s7)&0xf)<<28) +// s0-s7 contain the offset of the D3D constant that corresponds to the +// c0 or c1 constant in stages 0 through 7. These mappings are only used in +// SetPixelShaderConstant(). + +// ========================================================================================================= +// PSFinalCombinerConstants +// --------.--------.--------.----xxxx // offset of D3D constant for C0 +// --------.--------.--------.xxxx---- // offset of D3D constant for C1 +// --------.--------.-------x.-------- // Adjust texture flag + +#define PS_FINALCOMBINERCONSTANTS(c0,c1,flags) (((DWORD)(flags) << 8) | ((DWORD)(c0)&0xf)<< 0) | (((DWORD)(c1)&0xf)<< 4) +// c0 and c1 contain the offset of the D3D constant that corresponds to the +// constants in the final combiner. These mappings are only used in +// SetPixelShaderConstant(). Flags contains values from PS_GLOBALFLAGS + +enum PS_GLOBALFLAGS +{ + // if this flag is set, the texture mode for each texture stage is adjusted as follows: + // if set texture is a cubemap, + // change PS_TEXTUREMODES_PROJECT2D to PS_TEXTUREMODES_CUBEMAP + // change PS_TEXTUREMODES_PROJECT3D to PS_TEXTUREMODES_CUBEMAP + // change PS_TEXTUREMODES_DOT_STR_3D to PS_TEXTUREMODES_DOT_STR_CUBE + // if set texture is a volume texture, + // change PS_TEXTUREMODES_PROJECT2D to PS_TEXTUREMODES_PROJECT3D + // change PS_TEXTUREMODES_CUBEMAP to PS_TEXTUREMODES_PROJECT3D + // change PS_TEXTUREMODES_DOT_STR_CUBE to PS_TEXTUREMODES_DOT_STR_3D + // if set texture is neither cubemap or volume texture, + // change PS_TEXTUREMODES_PROJECT3D to PS_TEXTUREMODES_PROJECT2D + // change PS_TEXTUREMODES_CUBEMAP to PS_TEXTUREMODES_PROJECT2D + + PS_GLOBALFLAGS_NO_TEXMODE_ADJUST= 0x0000L, // don"t adjust texture modes + PS_GLOBALFLAGS_TEXMODE_ADJUST= 0x0001L, // adjust texture modes according to set texture +}; + +enum PSH_OPCODE +{ + PO_COMMENT, + PO_PS, + PO_DEF, + PO_DCL, // Note : ps.2.0 and up only + PO_DCL_2D, // Note : ps.2.0 and up only + PO_DCL_CUBE, // Note : ps.2.0 and up only + PO_DCL_VOLUME, // Note : ps.2.0 and up only + PO_TEX, + PO_TEXLD, // Note : ps.1.4 only + PO_TEXLD2, // Note : ps.2.0 and up only + PO_TEXBEM, + PO_TEXBEML, + PO_TEXBRDF, // Xbox ext. + PO_TEXCOORD, + PO_TEXCRD, // Note: ps.1.4 only + PO_TEXKILL, + PO_TEXREG2AR, + PO_TEXREG2GB, + PO_TEXDP3, // Note : ps.1.3 only + PO_TEXDP3TEX, // Note : ps.1.3 only + PO_TEXM3X2TEX, + PO_TEXM3X2DEPTH, // Note : requires ps.1.3 and a preceding texm3x2pad + PO_TEXM3X3DIFF, // Xbox ext. + PO_TEXM3X3VSPEC, + PO_TEXM3X3TEX, // Note : Uses a cube texture + PO_TEXM3X2PAD, // Note : Must be combined with texm3x2tex or texm3x2depth + PO_TEXM3X3PAD, + PO_TEXM3X3SPEC, // NOTE : NEEDS 3 ARGUMENTS! + // Direct3D8 arithmetic instructions : + PO_ADD, + PO_CMP, + PO_CND, + PO_DP3, // dp3 d, s1,s2 : d=s0 dot s1 (replicated to all channels, .rgb=color only, .a=color+alpha) + PO_DP4, // dp3 d, s1,s2 : d.r=d.g=d.b=d.a=(s1.r*s2.r)+(s1.g*s2.g)+(s1.b*s2.b)+(s1.a*s2.a) + PO_LRP, + PO_MAD, + PO_MOV, + PO_MUL, + PO_NOP, + PO_SUB, + PO_RCP, // Note: ps.2.0 and up only + // Xbox1 opcodes : + PO_XMMA, + PO_XMMC, + PO_XDM, + PO_XDD, + PO_XFC, + PO_XPS, +}; + +const struct { char *mn; int _Out; int _In; char *note; } PSH_OPCODE_DEFS[/*PSH_OPCODE*/] = { + // Pixel shader header opcodes (must be specified in this order) : + {/* PO_COMMENT */ /*mn:*/";", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, // + {/* PO_PS */ /*mn:*/"ps", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, // Must occur once + {/* PO_DEF */ /*mn:*/"def", /*_Out: */ 1, /*_In: */ 4, /*note:*/"" }, // Output must be a PARAM_C, arguments must be 4 floats [0.00f .. 1.00f] + {/* PO_DCL */ /*mn:*/"dcl", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only + {/* PO_DCL_2D */ /*mn:*/"dcl_2d", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only + {/* PO_DCL_CUBE */ /*mn:*/"dcl_cube", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only + {/* PO_DCL_VOLUME */ /*mn:*/"dcl_volume", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, // Note : ps.2.0 and up only + {/* PO_TEX */ /*mn:*/"tex", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, + {/* PO_TEXLD */ /*mn:*/"texld", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // Note : ps.1.4 and up only + {/* PO_TEXLD2 */ /*mn:*/"texld", /*_Out: */ 1, /*_In: */ 2, /*note:*/"" }, // Note : ps.1.4 and up only + {/* PO_TEXBEM */ /*mn:*/"texbem", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXBEML */ /*mn:*/"texbeml", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXBRDF */ /*mn:*/"texbrdf", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ Not supported by Direct3D8 ? + {/* PO_TEXCOORD */ /*mn:*/"texcoord", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, + {/* PO_TEXCRD */ /*mn:*/"texcrd", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // Note: ps.1.4 only + {/* PO_TEXKILL */ /*mn:*/"texkill", /*_Out: */ 1, /*_In: */ 0, /*note:*/"" }, + {/* PO_TEXDP3 */ /*mn:*/"texdp3", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXDP3TEX */ /*mn:*/"texdp3tex", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXM3X2TEX */ /*mn:*/"texm3x2tex", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXM3X2DEPTH */ /*mn:*/"texm3x2depth", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ requires ps.1.3 and a preceding texm3x2pad + {/* PO_TEXM3X3DIFF */ /*mn:*/"texm3x3diff", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ Not supported by Direct3D8 ? + {/* PO_TEXM3X3VSPEC */ /*mn:*/"texm3x3vspec", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXM3X3TEX */ /*mn:*/"texm3x3tex", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, // /*note: */ Uses a cube texture + {/* PO_TEXREG2AR */ /*mn:*/"texreg2ar", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXREG2GB */ /*mn:*/"texreg2gb", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXM3X2PAD */ /*mn:*/"texm3x2pad", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXM3X3PAD */ /*mn:*/"texm3x3pad", /*_Out: */ 1, /*_In: */ 1, /*note:*/"" }, + {/* PO_TEXM3X3SPEC */ /*mn:*/"texm3x3spec", /*_Out: */ 1, /*_In: */ 2, /*note:*/"" }, + // Arithmetic opcodes : + {/* PO_ADD */ /*mn:*/"add", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0+s1" }, + {/* PO_CMP */ /*mn:*/"cmp", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0={s0>=0?s1:s2}" }, + {/* PO_CND */ /*mn:*/"cnd", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0={s0.a>0.5?s1:s2}" }, // 1st input must be "r0.a" + {/* PO_DP3 */ /*mn:*/"dp3", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0 dot3 s1" }, + {/* PO_DP4 */ /*mn:*/"dp4", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0 dot4 s1" }, + {/* PO_LRP */ /*mn:*/"lrp", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0=s0*s1+{1-s0}*s2=s0*{s1-s2}+s2" }, + {/* PO_MAD */ /*mn:*/"mad", /*_Out: */ 1, /*_In: */ 3, /*note:*/"d0=s0*s1+s2" }, + {/* PO_MOV */ /*mn:*/"mov", /*_Out: */ 1, /*_In: */ 1, /*note:*/"d0=s0" }, + {/* PO_MUL */ /*mn:*/"mul", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0*s1" }, + {/* PO_NOP */ /*mn:*/"nop", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, + {/* PO_SUB */ /*mn:*/"sub", /*_Out: */ 1, /*_In: */ 2, /*note:*/"d0=s0-s1" }, + {/* PO_RCP */ /*mn:*/"rcp", /*_Out: */ 1, /*_In: */ 1, /*note:*/"d0=1/s0" }, // Note: ps.2.0 and up only + // Xbox-only {NV2A} opcodes : + {/* PO_XMMA */ /*mn:*/"xmma", /*_Out: */ 3, /*_In: */ 4, /*note:*/"d0=s0*s1, d1=s2*s3, d2={s0*s1}+{s2*s3}" }, + {/* PO_XMMC */ /*mn:*/"xmmc", /*_Out: */ 3, /*_In: */ 4, /*note:*/"d0=s0*s1, d1=s2*s3, d2={r0.a>0.5}?{s0*s1}:{s2*s3}" }, + {/* PO_XDM */ /*mn:*/"xdm", /*_Out: */ 2, /*_In: */ 4, /*note:*/"d0=s0 dot s1, d1=s2*s3" }, + {/* PO_XDD */ /*mn:*/"xdd", /*_Out: */ 2, /*_In: */ 4, /*note:*/"d0=s0 dot s1, d1=s2 dot s3" }, + {/* PO_XFC */ /*mn:*/"xfc", /*_Out: */ 0, /*_In: */ 7, /*note:*/"r0.rgb=s0*s1+{1-s0}*s2+s3, r0.a=s6.a, prod=s4*s5, sum=r0+v1" }, + {/* PO_XPS */ /*mn:*/"xps", /*_Out: */ 0, /*_In: */ 0, /*note:*/"" }, // Must occur once +}; + +enum PSH_ARGUMENT_TYPE +{ + PARAM_VALUE, // Xbox only; Numberic constants used in Xbox-only opcodes + PARAM_DISCARD, // Xbox only; + PARAM_FOG, // Final combiner only; Read-only register fog register + PARAM_V1R0_SUM, // Final combiner only; Read-only register that contains the result of V1+R0 + PARAM_EF_PROD, // Final combiner only; Read-only register that contains the result of final combiner parameters E * F + PARAM_oDepth, // Output depth register + PARAM_R, // Temporary registers (unassigned except r0.a, which on NV2A is initially set to t0.a) + PARAM_T, // Textures + PARAM_V, // Vertex colors + PARAM_C, // Constant registers, set by def opcodes or SetPixelShaderConstant + PARAM_S, // Sampler registers + PARAM_oC, // Output color registers +}; + +const char *PSH_ARGUMENT_TYPE_Str[/*PSH_ARGUMENT_TYPE*/] = { +// Prefix # r/w Input? Output? Note + "", // * r No No Used for numeric constants like -1, 0, 1 + "discard", // * w No Yes Only for xbox opcodes (native opcodes have single output - discards must be removed) + "fog", // 1 r Yes No Only for final combiner parameter + "sum", // 1 r Yes No Only for final combiner parameter + "prod", // 1 r Yes No Only for final combiner parameter + "oDepth", // + "r", // 2 r/w Yes Yes We fake a few extra registers and resolve them in FixupPixelShader + "t", // 4 r/w Yes Yes D3D9 cannot write to these! + "v", // 2 r Yes Yes + "c", // 16 r Yes No Xbox has 8*c0,c1=16, while PC D3D8 has only 8, we try to reduce that in FixupPixelShader + "s", // 16 - No Yes + "oC", // +}; + +constexpr int XFC_COMBINERSTAGENR = XTL::X_PSH_COMBINECOUNT; // Always call XFC 'stage 9', 1 after the 8th combiner + +constexpr int PSH_XBOX_MAX_C_REGISTER_COUNT = 16; +constexpr int PSH_XBOX_MAX_R_REGISTER_COUNT = 2; +constexpr int PSH_XBOX_MAX_T_REGISTER_COUNT = 4; +constexpr int PSH_XBOX_MAX_V_REGISTER_COUNT = 2; +// Extra constants to support features not present in Native D3D : +constexpr int PSH_XBOX_CONSTANT_FOG = PSH_XBOX_MAX_C_REGISTER_COUNT; // = 16 +constexpr int PSH_XBOX_CONSTANT_FC0 = PSH_XBOX_CONSTANT_FOG + 1; // = 17 +constexpr int PSH_XBOX_CONSTANT_FC1 = PSH_XBOX_CONSTANT_FC0 + 1; // = 18 +constexpr int PSH_XBOX_CONSTANT_MUL0 = PSH_XBOX_CONSTANT_FC1 + 1; // = 19 +constexpr int PSH_XBOX_CONSTANT_MUL1 = PSH_XBOX_CONSTANT_MUL0 + 1; // = 20 +constexpr int PSH_XBOX_CONSTANT_BEM = PSH_XBOX_CONSTANT_MUL1 + 1; // = 21 +constexpr int PSH_XBOX_CONSTANT_LUM = PSH_XBOX_CONSTANT_BEM + 4; // = 25 +constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_LUM + 4; // = 29 + +constexpr int FakeRegNr_Sum = PSH_XBOX_MAX_T_REGISTER_COUNT + 0; +constexpr int FakeRegNr_Prod = PSH_XBOX_MAX_T_REGISTER_COUNT + 1; +constexpr int FakeRegNr_Xmm1 = PSH_XBOX_MAX_T_REGISTER_COUNT + 2; +constexpr int FakeRegNr_Xmm2 = PSH_XBOX_MAX_T_REGISTER_COUNT + 3; + +enum PSH_INST_MODIFIER { + INSMOD_NONE, // y = x + INSMOD_BIAS, // y = x - 0.5 // Xbox only : TODO : Fixup occurrances! + INSMOD_X2, // y = x * 2 + INSMOD_BX2, // y = (x - 0.5) * 2 // Xbox only : TODO : Fixup occurrances! + INSMOD_X4, // y = x * 4 + INSMOD_D2, // y = x * 0.5 + INSMOD_SAT, // Xbox doesn"t support this, but has ARGMOD_SATURATE instead + INSMOD_X8, // y = x * 8 // ps 1.4 only + INSMOD_D4, // y = x * 0.25 // ps 1.4 only + INSMOD_D8, // y = x * 0.125 // ps 1.4 only +}; + +const char *PSH_INST_MODIFIER_Str[/*PSH_INST_MODIFIER*/] = { + "", + "_bias", + "_x2", + "_bx2", + "_x4", + "_d2", + "_sat", + "_x8", + "_d4", + "_d8", +}; + +// Four argument modifiers (applied in this order) : +// 1: Inversion (invert or negate : "1-" or "-") +// 2: Apply bias ("_bias") +// 3: Apply scale ("_x2", "_bx2", "_x4", or "_d2") +// 4: Apply clamp ("_sat") +enum PSH_ARG_MODIFIER { + ARGMOD_IDENTITY, // y = x + + ARGMOD_INVERT, // y = 1-x -> 0..1 > 1..0 + ARGMOD_NEGATE, // y = -x -> 0..1 > 0..-1 + + ARGMOD_BIAS, // y = x-0.5 -> 0..1 > -0.5..0.5 + + ARGMOD_SCALE_X2, // y = x*2 -> 0..1 > 0..2 + ARGMOD_SCALE_BX2, // y = (x*2)-1 -> 0..1 > -1..1 + ARGMOD_SCALE_X4, // y = x*4 -> 0..1 > 0..4 + ARGMOD_SCALE_D2, // y = x/2 -> 0..1 > 0..0.5 + + ARGMOD_SATURATE, // Xbox - not available in PS1.3 (can be done on output instead) + + ARGMOD_ALPHA_REPLICATE, + ARGMOD_BLUE_REPLICATE // PS1.1-PS1.3 only allow this if destination writemask = .a +}; + +typedef DWORD PSH_ARG_MODIFIERs; // = set of PSH_ARG_MODIFIER; + +const char *PSH_ARG_MODIFIER_Str[/*PSH_ARG_MODIFIER*/] = { + "%s", + + "1-%s", + "-%s", + + "%s_bias", + + "%s_x2", + "%s_bx2", + "%s_x4", + "%s_d2", + + "%s_sat", + + "%s", // .a is added via Mask + "%s" // .b idem +}; + +struct RPSRegisterObject { + bool IsAlpha; + PS_REGISTER Reg; + + void Decode(uint8_t Value, bool aIsAlpha); + std::string DecodedToString(); +}; + +struct RPSInputRegister : RPSRegisterObject { + PS_CHANNEL Channel; + PS_INPUTMAPPING InputMapping; + + void Decode(uint8_t Value, bool aIsAlpha); + std::string DecodedToString(); +}; + +struct RPSCombinerOutput : RPSRegisterObject { + RPSInputRegister Input1; // Called InputA or InputC (depending if it's inside the AB or CD combiner) + RPSInputRegister Input2; // Called InputC or InputD (depending if it's inside the AB or CD combiner) + bool DotProduct; // False=Multiply, True=DotProduct + bool BlueToAlpha; // False=Alpha-to-Alpha, True=Blue-to-Alpha + + void Decode(uint8_t Value, DWORD PSInputs, bool aIsAlpha); +}; + +struct RPSCombinerOutputMuxSum : RPSRegisterObject { + RPSCombinerOutput OutputAB; // Contains InputA and InputB (as Input1 and Input2) + RPSCombinerOutput OutputCD; // Contains InputC and InputD (as Input1 and Input2) +}; + +struct RPSCombinerStageChannel { + RPSCombinerOutputMuxSum OutputSUM; // Contains OutputAB, OutputCD + PS_COMBINEROUTPUT CombinerOutputFlags; + bool AB_CD_SUM; // True=AB+CD, False=MUX(AB;CD) based on R0.a + + void Decode(DWORD PSInputs, DWORD PSOutputs, bool aIsAlpha = false); +}; + +struct RPSCombinerStage { + RPSCombinerStageChannel RGB; + RPSCombinerStageChannel Alpha; +}; + +struct RPSFinalCombiner { + RPSInputRegister InputA; + RPSInputRegister InputB; + RPSInputRegister InputC; + RPSInputRegister InputD; + RPSInputRegister InputE; + RPSInputRegister InputF; + RPSInputRegister InputG; + + PS_FINALCOMBINERSETTING FinalCombinerFlags; + + uint8_t FinalCombinerC0Mapping; + uint8_t FinalCombinerC1Mapping; + + DWORD dwPS_GLOBALFLAGS; + + void Decode(const DWORD PSFinalCombinerInputsABCD, const DWORD PSFinalCombinerInputsEFG, const DWORD PSFinalCombinerConstants); +}; + +constexpr DWORD MASK_NONE = 0x000; +constexpr DWORD MASK_R = 0x001; +constexpr DWORD MASK_G = 0x002; +constexpr DWORD MASK_B = 0x004; +constexpr DWORD MASK_A = 0x008; +constexpr DWORD MASK_RGB = MASK_R | MASK_G | MASK_B; +constexpr DWORD MASK_RGBA = MASK_R | MASK_G | MASK_B | MASK_A; + +enum + TArgumentType { + atInput, atOutput, atFinalCombiner +}; + +typedef struct _PSH_RECOMPILED_SHADER { + XTL::X_D3DPIXELSHADERDEF PSDef; + std::string NewShaderStr; + DWORD ConvertedHandle; + bool ConstInUse[PSH_XBOX_CONSTANT_MAX]; + int ConstMapping[PSH_XBOX_CONSTANT_MAX]; +} PSH_RECOMPILED_SHADER, +*PPSH_RECOMPILED_SHADER; + +typedef struct _PSH_IMD_ARGUMENT { + PSH_ARGUMENT_TYPE Type; // For parameters: R, T, V or C For output : Discard, R, T or V + int16_t Address; // Register address + DWORD Mask; + PSH_ARG_MODIFIERs Modifiers; + float Multiplier; + + void SetConstValue(float Value); + float GetConstValue(); + bool UsesRegister(); + bool IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); // overload; + bool IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask); // overload; + void SetRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask); + bool HasModifier(PSH_ARG_MODIFIER modifier); + bool SetScaleConstRegister(float factor, const PSH_RECOMPILED_SHADER& pRecompiled); + bool SetScaleBemLumRegister(D3DTEXTURESTAGESTATETYPE factor, int stage, const PSH_RECOMPILED_SHADER& pRecompiled); + std::string ToString(); + bool Decode(const DWORD Value, DWORD aMask, TArgumentType ArgumentType); + void Invert(); + void Negate(); +} PSH_IMD_ARGUMENT, +*PPSH_IMD_ARGUMENT; + +//TPSH_IMD_ARGUMENTArray = array[0..(MaxInt div SizeOf(PSH_IMD_ARGUMENT)) - 1] of PSH_IMD_ARGUMENT; +//PPSH_IMD_ARGUMENTs = ^TPSH_IMD_ARGUMENTArray; + +typedef struct _PSH_INTERMEDIATE_FORMAT { + int CombinerStageNr; + bool IsCombined; + PSH_OPCODE Opcode; + std::string CommentString; + PSH_INST_MODIFIER Modifier; + PSH_IMD_ARGUMENT Output[3]; // 3 = xmm* output count + PSH_IMD_ARGUMENT Parameters[7]; // 7 = xfc parameter count + + _PSH_INTERMEDIATE_FORMAT *Initialize(const PSH_OPCODE aOpcode); + std::string ToString(); + bool IsArithmetic(); + void ScaleOutput(float aFactor); + bool ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress); // overload; + bool ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask); // overload; + bool ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, int& addressCount, int& total); // overload; + bool WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress); // overload; + bool WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask); // overload; + void SwapParameter(const int Index1, const int Index2); + void XSwapOutput(); + bool MoveRemovableParametersRight(const int Index1, const int Index2); + bool XMoveNonRegisterOutputsRight(); + void XCopySecondOpcodeToFirst(const PSH_OPCODE aOpcode); + bool Decode(DWORD aCombinerStageNr, DWORD PSInputs, DWORD PSOutputs, DWORD aMask); + bool DecodeFinalCombiner(DWORD aPSFinalCombinerInputsABCD, DWORD aPSFinalCombinerInputsEFG); +} PSH_INTERMEDIATE_FORMAT, +*PPSH_INTERMEDIATE_FORMAT; + +struct PSH_XBOX_SHADER { + uint32_t m_PSVersion; // see D3DPS_VERSION - https://msdn.microsoft.com/en-us/library/windows/desktop/bb172592(v=vs.85).aspx + int MaxConstantFloatRegisters; + int MaxTemporaryRegisters; + int MaxSamplerRegisters; // Sampler (Direct3D 9 asm-ps) + int MaxTextureCoordinateRegisters; + int MaxInputColorRegisters; + int PSH_PC_MAX_REGISTER_COUNT; + + // Reserve enough slots for all shaders, so we need space for 2 constants, 5 lines per texture addressing codes and 10 lines per opcode : : + PSH_INTERMEDIATE_FORMAT Intermediate[2 + (XTL::X_D3DTS_STAGECOUNT * 5) + (XTL::X_PSH_COMBINECOUNT * 10) + 1]; + int IntermediateCount; + + PS_TEXTUREMODES PSTextureModes[XTL::X_D3DTS_STAGECOUNT]; + PS_DOTMAPPING PSDotMapping[XTL::X_D3DTS_STAGECOUNT]; + DWORD PSCompareMode[XTL::X_D3DTS_STAGECOUNT]; + int PSInputTexture[XTL::X_D3DTS_STAGECOUNT]; + + PS_FINALCOMBINERSETTING FinalCombinerFlags; + // Note : The following constants are only needed for PSH_XBOX_SHADER::DecodedToString, + // they are not involved in the actual pixel shader recompilation anymore : + RPSFinalCombiner FinalCombiner; + RPSCombinerStage Combiners[XTL::X_PSH_COMBINECOUNT]; + int NumberOfCombiners; + DWORD CombinerCountFlags; // For PS_COMBINERCOUNTFLAGS + // Read from CombinerCountFlags : + bool CombinerMuxesOnMsb; + bool CombinerHasUniqueC0; + bool CombinerHasUniqueC1; + + int StartPos; + + PSH_RECOMPILED_SHADER Recompiled = {}; + + void SetPSVersion(const uint32_t PSVersion); + + std::string ToString(); + void Log(const char *PhaseStr); + PPSH_INTERMEDIATE_FORMAT NewIntermediate(); + void InsertIntermediate(PPSH_INTERMEDIATE_FORMAT pIntermediate, int Index); + void DeleteIntermediate(int Index); + void DeleteLastIntermediate(); + std::string static OriginalToString(XTL::X_D3DPIXELSHADERDEF *pPSDef); + void Decode(XTL::X_D3DPIXELSHADERDEF *pPSDef); + PSH_RECOMPILED_SHADER Convert(XTL::X_D3DPIXELSHADERDEF *pPSDef); + std::string DecodedToString(XTL::X_D3DPIXELSHADERDEF *pPSDef); + bool _NextIs2D(int Stage); + bool DecodeTextureModes(XTL::X_D3DPIXELSHADERDEF *pPSDef); + int GetTextureStageModifiers(int Stage); + void InsertTex3x2Instructions(int Stage, int inputStage, std::vector& InsertIns); + void InsertTex3x3Instructions(int Stage, int inputStage, std::vector& InsertIns); + bool InsertTextureModeInstruction(XTL::X_D3DPIXELSHADERDEF *pPSDef, int Stage, PSH_OPCODE opcode, std::vector& InsertIns, int& InsertPos); + bool MoveRemovableParametersRight(); + void ConvertXboxOpcodesToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef); + void _SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLOR ConstColor); + void _SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLORVALUE ConstColor); + int _MapConstant(int ConstNr, bool *NativeConstInUse); + int _HandleConst(int XboxConst, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled, bool *NativeConstInUse, bool *EmittedNewConstant); + bool ConvertConstantsToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled); + bool RemoveUselessWrites(); + int MaxRegisterCount(PSH_ARGUMENT_TYPE aRegType); + bool IsValidNativeOutputRegister(PSH_ARGUMENT_TYPE aRegType, int index = -1); + int RegisterIsFreeFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); + int RegisterIsUsedFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); + int NextFreeRegisterFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int bIndex = -1, int startAddress = 0, int excludeAddress = -1); + bool IsRegisterFreeFromIndexOnwards(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress); + void ReplaceInputRegisterFromIndexOnwards(int aIndex, + PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, + PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex = -1); + void ReplaceOutputRegisterFromIndexOnwards(int aIndex, + PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, + PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex = -1); + void ReplaceRegisterFromIndexOnwards(int aIndex, + PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, + PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex = -1, bool replaceInput = true, bool replaceOutput = true); + bool ConvertXMMToNative_Except3RdOutput(int i); + void ConvertXPSToNative(int i); + void ConvertXMMAToNative(int i); + void ConvertXMMCToNative(int i); + void ConvertXDMToNative(int i); + void ConvertXDDToNative(int i); + void ConvertXFCToNative(int i); + bool FixArgumentModifiers(); + bool CombineInstructions(); + bool RemoveNops(); + bool SimplifyMOV(PPSH_INTERMEDIATE_FORMAT Cur); + bool SimplifyADD(PPSH_INTERMEDIATE_FORMAT Cur); + bool SimplifyMAD(PPSH_INTERMEDIATE_FORMAT Cur, int index); + bool SimplifySUB(PPSH_INTERMEDIATE_FORMAT Cur); + bool SimplifyMUL(PPSH_INTERMEDIATE_FORMAT Cur); + bool SimplifyLRP(PPSH_INTERMEDIATE_FORMAT Cur, int index); + bool FixupCND(PPSH_INTERMEDIATE_FORMAT Cur, int index); + bool FixupPixelShader(); + bool FixInvalidSrcSwizzle(); + bool FixMissingR0a(); + bool FixMissingR1a(); + bool FixCoIssuedOpcodes(); + bool FixInvalidDstRegister(); + bool FixConstantParameters(); + bool FixInstructionModifiers(); + bool FixUninitializedReads(); + bool FixOverusedRegisters(); + bool FinalizeShader(); + + static void GetPSTextureModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_TEXTUREMODES psTextureModes[XTL::X_D3DTS_STAGECOUNT]); + static void GetPSDotMapping(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_DOTMAPPING psDotMapping[XTL::X_D3DTS_STAGECOUNT]); + static void GetPSCompareModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, DWORD psCompareModes[XTL::X_D3DTS_STAGECOUNT]); + static void GetPSInputTexture(XTL::X_D3DPIXELSHADERDEF* pPSDef, int psInputTexture[XTL::X_D3DTS_STAGECOUNT]); +}; + +/* +* Blueshogun's code (useful for debugging the PixelShader binary format) +*/ + +// PS Texture Modes +char* PS_TextureModesStr[/*PS_TEXTUREMODES*/] = +{ + "PS_TEXTUREMODES_NONE", // 0x00 + "PS_TEXTUREMODES_PROJECT2D", // 0x01 + "PS_TEXTUREMODES_PROJECT3D", // 0x02 + "PS_TEXTUREMODES_CUBEMAP", // 0x03 + "PS_TEXTUREMODES_PASSTHRU", // 0x04 + "PS_TEXTUREMODES_CLIPPLANE", // 0x05 + "PS_TEXTUREMODES_BUMPENVMAP", // 0x06 + "PS_TEXTUREMODES_BUMPENVMAP_LUM", // 0x07 + "PS_TEXTUREMODES_BRDF", // 0x08 + "PS_TEXTUREMODES_DOT_ST", // 0x09 + "PS_TEXTUREMODES_DOT_ZW", // 0x0A + "PS_TEXTUREMODES_DOT_RFLCT_DIFF", // 0x0B + "PS_TEXTUREMODES_DOT_RFLCT_SPEC", // 0x0C + "PS_TEXTUREMODES_DOT_STR_3D", // 0x0D + "PS_TEXTUREMODES_DOT_STR_CUBE", // 0x0E + "PS_TEXTUREMODES_DPNDNT_AR", // 0x0F + "PS_TEXTUREMODES_DPNDNT_GB", // 0x10 + "PS_TEXTUREMODES_DOTPRODUCT", // 0x11 + "PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST", // 0x12 + "???", // 0x13 + "???", // 0x14 + "???", // 0x15 + "???", // 0x16 + "???", // 0x17 + "???", // 0x18 + "???", // 0x19 + "???", // 0x1A + "???", // 0x1B + "???", // 0x1C + "???", // 0x1D + "???", // 0x1E + "???", // 0x1F +}; + +// PS DotMapping +char* PS_DotMappingStr[/*PS_DOTMAPPING*/] = +{ + "PS_DOTMAPPING_ZERO_TO_ONE", // 0x00 + "PS_DOTMAPPING_MINUS1_TO_1_D3D", // 0x01 + "PS_DOTMAPPING_MINUS1_TO_1_GL", // 0x02 + "PS_DOTMAPPING_MINUS1_TO_1", // 0x03 + "PS_DOTMAPPING_HILO_1", // 0x04 + "???", // 0x05 + "???", // 0x06 + "PS_DOTMAPPING_HILO_HEMISPHERE", // 0x07 +}; + +#if 1 // array unusable for bitflags +// PS CompareMode +char* PS_CompareModeStr[/*PS_COMPAREMODE*/] = +{ + "PS_COMPAREMODE_S_LT", // 0x00L + "PS_COMPAREMODE_S_GE", // 0x01L + + "PS_COMPAREMODE_T_LT", // 0x00L + "PS_COMPAREMODE_T_GE", // 0x02L + + "???", + + "PS_COMPAREMODE_R_LT", // 0x00L + "PS_COMPAREMODE_R_GE", // 0x04L + + "???", + "???", + "???", + + "PS_COMPAREMODE_Q_LT", // 0x00L + "PS_COMPAREMODE_Q_GE", // 0x08L +}; +#endif + +#if 1 // array unfit for bitflags +// PS CombinerCountFlags +char* PS_CombinerCountFlagsStr[/*PS_COMBINERCOUNTFLAGS*/] = +{ + "PS_COMBINERCOUNT_MUX_LSB", // 0x0000L, // mux on r0.a lsb + "PS_COMBINERCOUNT_MUX_MSB", // 0x0001L, // mux on r0.a msb + + "PS_COMBINERCOUNT_SAME_C0", // 0x0000L, // c0 same in each stage + "PS_COMBINERCOUNT_UNIQUE_C0", // 0x0010L, // c0 unique in each stage + + "PS_COMBINERCOUNT_SAME_C1", // 0x0000L, // c1 same in each stage + "PS_COMBINERCOUNT_UNIQUE_C1", // 0x0100L // c1 unique in each stage +}; +#endif + +// PS InputMapping +std::string PS_InputMappingStr[/*PS_INPUTMAPPING*/] = +{ + "PS_INPUTMAPPING_UNSIGNED_IDENTITY", // 0x00L, // max(0,x) OK for final combiner: y = abs(x) + "PS_INPUTMAPPING_UNSIGNED_INVERT", // 0x20L, // 1 - max(0,x) OK for final combiner: y = 1 - x + "PS_INPUTMAPPING_EXPAND_NORMAL", // 0x40L, // 2*max(0,x) - 1 invalid for final combiner + "PS_INPUTMAPPING_EXPAND_NEGATE", // 0x60L, // 1 - 2*max(0,x) invalid for final combiner + "PS_INPUTMAPPING_HALFBIAS_NORMAL", // 0x80L, // max(0,x) - 1/2 invalid for final combiner + "PS_INPUTMAPPING_HALFBIAS_NEGATE", // 0xa0L, // 1/2 - max(0,x) invalid for final combiner + "PS_INPUTMAPPING_SIGNED_IDENTITY", // 0xc0L, // x invalid for final combiner + "PS_INPUTMAPPING_SIGNED_NEGATE", // 0xe0L, // -x invalid for final combiner +}; + +// PS Register (note, a few have one space, to line up the output a little) +std::string PS_RegisterStr[/*PS_REGISTER*/] = +{ + "PS_REGISTER_ZERO", // 0x00L, // r + "PS_REGISTER_DISCARD", // 0x00L, // w + "PS_REGISTER_C0 ", // 0x01L, // r + "PS_REGISTER_C1 ", // 0x02L, // r + "PS_REGISTER_FOG", // 0x03L, // r + "PS_REGISTER_V0 ", // 0x04L, // r/w + "PS_REGISTER_V1 ", // 0x05L, // r/w + "??", // 0x06 + "??", // 0x07 + "PS_REGISTER_T0 ", // 0x08L, // r/w + "PS_REGISTER_T1 ", // 0x09L, // r/w + "PS_REGISTER_T2 ", // 0x0aL, // r/w + "PS_REGISTER_T3 ", // 0x0bL, // r/w + "PS_REGISTER_R0 ", // 0x0cL, // r/w + "PS_REGISTER_R1 ", // 0x0dL, // r/w + "PS_REGISTER_V1R0_SUM", // 0x0eL, // r + "PS_REGISTER_EF_PROD", // 0x0fL, // r + + "PS_REGISTER_ONE", // PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // OK for final combiner + "PS_REGISTER_NEGATIVE_ONE", // PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL, // invalid for final combiner + "PS_REGISTER_ONE_HALF", // PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NEGATE, // invalid for final combiner + "PS_REGISTER_NEGATIVE_ONE_HALF" // PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NORMAL, // invalid for final combiner +}; + +// PS Channel +char* PS_ChannelStr[/*PS_CHANNEL*/] = +{ + "PS_CHANNEL_RGB", // 0x00, // used as RGB source + "PS_CHANNEL_BLUE", // 0x00, // used as ALPHA source + "PS_CHANNEL_ALPHA", // 0x10, // used as RGB or ALPHA source +}; + +// PS FinalCombinerSetting +char* PS_FinalCombinerSettingStr[/*PS_FINALCOMBINERSETTING*/] = +{ + "PS_FINALCOMBINERSETTING_CLAMP_SUM", // 0x80, // V1+R0 sum clamped to [0,1] + "PS_FINALCOMBINERSETTING_COMPLEMENT_V1", // 0x40, // unsigned invert mapping + "PS_FINALCOMBINERSETTING_COMPLEMENT_R0", // 0x20, // unsigned invert mapping +}; + +// PS CombineOutput +char* PS_CombineOutputStr[/*PS_COMBINEROUTPUT*/] = +{ + "PS_COMBINEROUTPUT_IDENTITY", // 0x00L, // y = x + "PS_COMBINEROUTPUT_BIAS", // 0x08L, // y = x - 0.5 + "PS_COMBINEROUTPUT_SHIFTLEFT_1", // 0x10L, // y = x*2 + "PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS", // 0x18L, // y = (x - 0.5)*2 = x*2 - 1.0 + "PS_COMBINEROUTPUT_SHIFTLEFT_2", // 0x20L, // y = x*4 + "PS_COMBINEROUTPUT_SHIFTRIGHT_1", // 0x30L, // y = x/2 = x*0.5 + + "PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA", // 0x80L, // RGB only + + "PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA", // 0x40L, // RGB only + + "PS_COMBINEROUTPUT_AB_MULTIPLY", // 0x00L, + "PS_COMBINEROUTPUT_AB_DOT_PRODUCT", // 0x02L, // RGB only + + "PS_COMBINEROUTPUT_CD_MULTIPLY", // 0x00L, + "PS_COMBINEROUTPUT_CD_DOT_PRODUCT", // 0x01L, // RGB only + + "PS_COMBINEROUTPUT_AB_CD_SUM", // 0x00L, // 3rd output is AB+CD + "PS_COMBINEROUTPUT_AB_CD_MUX", // 0x04L, // 3rd output is MUX(AB,CD) based on R0.a +}; + +// PS GlobalFlags +char* PS_GlobalFlagsStr[/*PS_GLOBALFLAGS*/] = +{ + "PS_GLOBALFLAGS_NO_TEXMODE_ADJUST", // 0x0000L, // don't adjust texture modes + "PS_GLOBALFLAGS_TEXMODE_ADJUST", // 0x0001L, // adjust texture modes according to set texture +}; + +const int CONST_NEG_ONE = -2; +const int CONST_NEG_HALF = -1; +const int CONST_ZERO = 0; +const int CONST_POS_HALF = 1; // Note : Instead of 0.5 we use 1 (so we can keep using integers) +const int CONST_POS_ONE = 2; + +/// + +std::string PSCombinerOutputFlagsToStr(const DWORD dwFlags, bool aIsAlpha = false) +{ + std::string Result = PS_CombineOutputStr[0 + ((dwFlags & 0x38) >> 3)]; + Result = Result + " | " + PS_CombineOutputStr[8 + ((dwFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) >> 1)]; + Result = Result + " | " + PS_CombineOutputStr[10 + ((dwFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) >> 0)]; + Result = Result + " | " + PS_CombineOutputStr[12 + ((dwFlags & PS_COMBINEROUTPUT_AB_CD_MUX) >> 2)]; + + if (!aIsAlpha) { + if (dwFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) + Result = Result + " | " + PS_CombineOutputStr[6]; + + if (dwFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) + Result = Result + " | " + PS_CombineOutputStr[7]; + } + + return Result; +} + +std::string PSFinalCombinerSettingToStr(const DWORD dwPS_FINALCOMBINERSETTING) +{ + std::string Result = ""; + if (dwPS_FINALCOMBINERSETTING & PS_FINALCOMBINERSETTING_CLAMP_SUM) + Result = Result + " | " + PS_FinalCombinerSettingStr[0]; + + if (dwPS_FINALCOMBINERSETTING & PS_FINALCOMBINERSETTING_COMPLEMENT_V1) + Result = Result + " | " + PS_FinalCombinerSettingStr[1]; + + if (dwPS_FINALCOMBINERSETTING & PS_FINALCOMBINERSETTING_COMPLEMENT_R0) + Result = Result + " | " + PS_FinalCombinerSettingStr[2]; + + if (!Result.empty()) + Result.erase(0, 3); + + return Result; +} + +/* PSH_IMD_ARGUMENT */ + +void PSH_IMD_ARGUMENT::SetConstValue(float Value) +{ + Type = PARAM_VALUE; + Address = CONST_ZERO; + Multiplier = Value; + Modifiers = 0; +} + +float PSH_IMD_ARGUMENT::GetConstValue() +{ + if (Type != PARAM_VALUE) { + // Anything other than a value-parameter returns a value never checked for : + return INFINITY; + } + + float Result = Multiplier; + + // y = 1-x -> 0..1 > 1..0 + if (HasModifier(ARGMOD_INVERT)) Result = 1.0f-Result; + + // y = -x -> 0..1 > 0..-1 + if (HasModifier(ARGMOD_NEGATE)) Result = -Result; + + // y = x-0.5 -> 0..1 > -0.5..0.5 + if (HasModifier(ARGMOD_BIAS)) Result = Result-0.5f; + + // y = x*2 -> 0..1 > 0..2 + if (HasModifier(ARGMOD_SCALE_X2)) Result = Result*2.0f; + + // y = (x*2)-1 -> 0..1 > -1..1 + if (HasModifier(ARGMOD_SCALE_BX2)) Result = (Result*2.0f)-1.0f; + + // y = x*4 -> 0..1 > 0..4 + if (HasModifier(ARGMOD_SCALE_X4)) Result = Result*4.0f; + + // y = x/2 -> 0..1 > 0..0.5 + if (HasModifier(ARGMOD_SCALE_D2)) Result = Result/2.0f; + + return Result; +} // GetConstValue + +bool PSH_IMD_ARGUMENT::UsesRegister() +{ + return (Type > PARAM_DISCARD); +} + +bool PSH_IMD_ARGUMENT::IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) +{ + return (Type == aRegType) + && (Address == aAddress || aAddress == -1); +} + +bool PSH_IMD_ARGUMENT::IsRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask) +{ + return IsRegister(aRegType, aAddress) + // Check the mask itself, but also 'mask-less' : + && (((Mask & aMask) == aMask) || (Mask == 0)); +} + +void PSH_IMD_ARGUMENT::SetRegister(PSH_ARGUMENT_TYPE aRegType, int16_t aAddress, DWORD aMask) +{ + Type = aRegType; + Address = aAddress; + Mask = aMask; +} + +bool PSH_IMD_ARGUMENT::HasModifier(PSH_ARG_MODIFIER modifier) +{ + return (Modifiers & (1 << modifier)) != 0; +} + +bool PSH_IMD_ARGUMENT::SetScaleConstRegister(float factor, const PSH_RECOMPILED_SHADER& pRecompiled) +{ + bool result = false; + + PSH_ARG_MODIFIERs modifiers = 0; + DWORD mask = Mask; + int address = Address; + + const int mappedConstant0 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_MUL0]; + const int mappedConstant1 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_MUL1]; + + if (factor < 0.0f) + { + factor = -factor; + modifiers = (1 << ARGMOD_NEGATE); + } + + if (factor == 1.0f) + { + address = mappedConstant0; + mask = MASK_R; + result = true; + } + + else if (factor == 2.0f) + { + address = mappedConstant0; + mask = MASK_G; + result = true; + } + + else if (factor == 4.0f) + { + address = mappedConstant0; + mask = MASK_B; + result = true; + } + + else if (factor == 8.0f) + { + address = mappedConstant0; + mask = MASK_A; + result = true; + } + + else if (factor == 0.0f) + { + address = mappedConstant1; + mask = MASK_R; + result = true; + } + + else if (factor == 1.0f / 2.0f) + { + address = mappedConstant1; + mask = MASK_G; + result = true; + } + + else if (factor == 1.0f / 4.0f) + { + address = mappedConstant1; + mask = MASK_B; + result = true; + } + + else if (factor == 1.0f / 8.0f) + { + address = mappedConstant1; + mask = MASK_A; + result = true; + } + + if (result) + { + Type = PARAM_C; + Address = address; + Mask = mask; + Modifiers = modifiers; + Multiplier = 1.0f; + } + + return result; +} + +bool PSH_IMD_ARGUMENT::SetScaleBemLumRegister(D3DTEXTURESTAGESTATETYPE factor, int stage, const PSH_RECOMPILED_SHADER& pRecompiled) +{ + bool result = false; + + PSH_ARG_MODIFIERs modifiers = 0; + DWORD mask = Mask; + int address = Address; + + const int mappedConstant0 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_BEM + stage]; + const int mappedConstant1 = pRecompiled.ConstMapping[PSH_XBOX_CONSTANT_LUM + stage]; + + switch (factor) + { + case D3DTSS_BUMPENVMAT00: + { + address = mappedConstant0; + mask = MASK_R; + result = true; + break; + } + case D3DTSS_BUMPENVMAT01: + { + address = mappedConstant0; + mask = MASK_G; + result = true; + break; + } + case D3DTSS_BUMPENVMAT11: + { + address = mappedConstant0; + mask = MASK_B; + result = true; + break; + } + case D3DTSS_BUMPENVMAT10: + { + address = mappedConstant0; + mask = MASK_A; + result = true; + break; + } + case D3DTSS_BUMPENVLSCALE: + { + address = mappedConstant1; + mask = MASK_R; + result = true; + break; + } + case D3DTSS_BUMPENVLOFFSET: + { + address = mappedConstant1; + mask = MASK_G; + result = true; + break; + } + } + + if (result) + { + Type = PARAM_C; + Address = address; + Mask = mask; + Modifiers = modifiers; + Multiplier = 1.0f; + } + + return result; +} + +std::string PSH_IMD_ARGUMENT::ToString() +{ + std::string Result; + + if (Type == PARAM_VALUE) + { + Result = std::to_string(GetConstValue()); + if (Result.find(".") > 0) + Result = Result + 'f'; + + return Result; + } + + Result = PSH_ARGUMENT_TYPE_Str[Type]; + + if (Type >= PARAM_R) + Result = Result + std::to_string(Address); + + if (UsesRegister()) + { + for (DWORD Modifier = ARGMOD_IDENTITY; Modifier < ARGMOD_BLUE_REPLICATE; Modifier++) + if (HasModifier((PSH_ARG_MODIFIER)Modifier)) { + char buffer[256]; + Result = std::string(buffer, sprintf(buffer, PSH_ARG_MODIFIER_Str[Modifier], Result.c_str())); + } + + if ((Mask > 0) && (Mask != MASK_RGBA)) + { + Result = Result + '.'; + if ((Mask & MASK_R) > 0) Result = Result + 'r'; + if ((Mask & MASK_G) > 0) Result = Result + 'g'; + if ((Mask & MASK_B) > 0) Result = Result + 'b'; + if ((Mask & MASK_A) > 0) Result = Result + 'a'; + } + } + return Result; +} // ToString + +bool PSH_IMD_ARGUMENT::Decode(const DWORD Value, DWORD aMask, TArgumentType ArgumentType) +{ + PS_REGISTER Reg; + PS_INPUTMAPPING InputMapping; + PS_CHANNEL Channel; + + bool Result = true; + Address = 0; + Mask = aMask; + Modifiers = (1 << ARGMOD_IDENTITY); + Multiplier = 1.0; + + // Determine PS_REGISTER for this argument type : + { + Reg = (PS_REGISTER)(Value & 0xF); + if (ArgumentType == atOutput) + { + // Output arguments may not write to C0 or C1, prevent that : + if ((Reg == PS_REGISTER_C0) || (Reg == PS_REGISTER_C1)) + Reg = PS_REGISTER_CXBX_PROD; // unhandled case - will reach "invalid" else-block + } + else + { + // Input arguments (normal or final combiners) can use the extended PS_REGISTER values : + if (Reg == PS_REGISTER_ZERO) + Reg = (PS_REGISTER)(Value & 0xE0); + + // 'Signed Identity' flag on PS_REGISTER_ZERO has no meaning, treat as zero : + if (Reg == PS_REGISTER_CXBX_PROD) + Reg = PS_REGISTER_ZERO; + + // Prevent decoding final combiner registers outside that mode : + if (ArgumentType != atFinalCombiner) + if ((Reg == PS_REGISTER_FOG) || (Reg == PS_REGISTER_V1R0_SUM) || (Reg == PS_REGISTER_EF_PROD)) + Reg = PS_REGISTER_CXBX_PROD; // unhandled case - will reach "invalid" else-block + } + } + + switch (Reg) { + case PS_REGISTER_ZERO: + { + if (ArgumentType == atOutput) + { + // Mark output arguments as 'discard' and return that fact : + Type = PARAM_DISCARD; + Result = false; + } + else + Type = PARAM_VALUE; + + Address = CONST_ZERO; + Multiplier = 0.0f; + break; + } + case PS_REGISTER_C0: + Type = PARAM_C; + break; + case PS_REGISTER_C1: + { + Type = PARAM_C; + Address = 1; + break; + } + case PS_REGISTER_V0: + Type = PARAM_V; + break; + case PS_REGISTER_V1: + { + Type = PARAM_V; + Address = 1; + break; + } + case PS_REGISTER_T0: + Type = PARAM_T; + break; + case PS_REGISTER_T1: + { + Type = PARAM_T; + Address = 1; + break; + } + case PS_REGISTER_T2: + { + Type = PARAM_T; + Address = 2; + break; + } + case PS_REGISTER_T3: + { + Type = PARAM_T; + Address = 3; + break; + } + case PS_REGISTER_R0: + Type = PARAM_R; + break; + case PS_REGISTER_R1: + { + Type = PARAM_R; + Address = 1; + break; + } + // Registers only available when ArgumentType != atOutput (Reg is capped otherwise) : + case PS_REGISTER_ONE: + { + Type = PARAM_VALUE; + Address = CONST_POS_ONE; + Multiplier = 1.0f; + break; + } + case PS_REGISTER_NEGATIVE_ONE: + { + Type = PARAM_VALUE; + Address = CONST_NEG_ONE; + Multiplier = -1.0f; + break; + } + case PS_REGISTER_ONE_HALF: + { + Type = PARAM_VALUE; + Address = CONST_POS_HALF; + Multiplier = 0.5f; + break; + } + case PS_REGISTER_NEGATIVE_ONE_HALF: + { + Type = PARAM_VALUE; + Address = CONST_NEG_HALF; + Multiplier = -0.5f; + break; + } + // Registers only available when ArgumentType == atFinalCombiner (Reg is capped otherwise) : + case PS_REGISTER_FOG: + Type = PARAM_FOG; + break; + case PS_REGISTER_V1R0_SUM: + Type = PARAM_V1R0_SUM; + break; + case PS_REGISTER_EF_PROD: + Type = PARAM_EF_PROD; + break; + default : + EmuLog(LOG_LEVEL::DEBUG, "INVALID ARGUMENT!"); + + Result = false; + } + + // We're done if this decoding is meant for output parameters, + // or when the input is a value-parameter (already read above) : + if ((ArgumentType == atOutput) + || (Type == PARAM_VALUE) ) + return Result; + + // Handle the Channel Designator : + { + Channel = (PS_CHANNEL)(Value & PS_CHANNEL_ALPHA); + if (Channel == PS_CHANNEL_ALPHA) + // Input comes from alpha portion of input register (valid for both RGB and alpha portions) : + Mask = MASK_A; + else // = PS_CHANNEL_BLUE (for Alpha step) = PS_CHANNEL_BLUE (for RGB step) : + if (aMask == MASK_A) + // Input comes from b portion of input register (valid for alpha portion only) : + Mask = MASK_B; // Note : This is not the same as ARGMOD_BLUE_REPLICATE! + else + // Input comes from the RGB portion of the input register (valid for RGB portion only) : + Mask = aMask; // Note : Is already put here, but makes this code clearer + } + + InputMapping = (PS_INPUTMAPPING)(Value & 0xe0); + +// ARGMOD_BIAS, +// +// ARGMOD_SCALE_X2, ARGMOD_SCALE_BX2, ARGMOD_SCALE_X4, ARGMOD_SCALE_D2, +// +// ARGMOD_SATURATE, +// +// ARGMOD_ALPHA_REPLICATE, ARGMOD_BLUE_REPLICATE]; + + switch (InputMapping) { + case PS_INPUTMAPPING_UNSIGNED_IDENTITY: + Modifiers = (1 << ARGMOD_IDENTITY); + break; + case PS_INPUTMAPPING_UNSIGNED_INVERT: + Modifiers = (1 << ARGMOD_INVERT); + break; + case PS_INPUTMAPPING_EXPAND_NORMAL: + { + Modifiers = (1 << ARGMOD_SCALE_BX2); + Multiplier = 2.0f * Multiplier; + break; + } + case PS_INPUTMAPPING_EXPAND_NEGATE: + { + Modifiers = (1 << ARGMOD_NEGATE); + Multiplier = -Multiplier; + break; + } + case PS_INPUTMAPPING_HALFBIAS_NORMAL: + Modifiers = (1 << ARGMOD_BIAS); + break; +// case PS_INPUTMAPPING_HALFBIAS_NEGATE: +// Modifiers = (1 << ARGMOD_IDENTITY); ??? +// break; + case PS_INPUTMAPPING_SIGNED_IDENTITY: + Modifiers = (1 << ARGMOD_IDENTITY); + break; + case PS_INPUTMAPPING_SIGNED_NEGATE: + { + Modifiers = (1 << ARGMOD_NEGATE); + Multiplier = -Multiplier; + break; + } + } + return Result; +} // Decode + +void PSH_IMD_ARGUMENT::Invert() +{ + if (!HasModifier(ARGMOD_INVERT)) + Modifiers = Modifiers | (1 << ARGMOD_INVERT); + else + Modifiers = Modifiers & ~(1 << ARGMOD_INVERT); +} + +void PSH_IMD_ARGUMENT::Negate() +{ + if (!HasModifier(ARGMOD_NEGATE)) + Modifiers = Modifiers | (1 << ARGMOD_NEGATE); + else + Modifiers = Modifiers & ~(1 << ARGMOD_NEGATE); +} + +/* PSH_INTERMEDIATE_FORMAT */ + +_PSH_INTERMEDIATE_FORMAT *PSH_INTERMEDIATE_FORMAT::Initialize(const PSH_OPCODE aOpcode) +{ + int i; + + Opcode = aOpcode; + Modifier = INSMOD_NONE; + for (i = 0; i < 3; i++) + { + Output[i] = {}; + Output[i].Multiplier = 1.0f; + } + for (i = 0; i < 7; i++) + { + Parameters[i] = {}; + Parameters[i].Multiplier = 1.0f; + } + + return this; +} + +std::string PSH_INTERMEDIATE_FORMAT::ToString() +{ + std::string Result = {}; + int i; + char SeparatorChar; + + switch (Opcode) { + case PO_COMMENT: + { + Result = "; " + CommentString; + return Result; + } + case PO_PS: { + // 1.1 allows reading from 2 textures (which we use in 'cnd') and reading from the .b (blue) channel + // 1.3 allows the use of texm3x2depth (which can occur sometimes) + // 2.0 allows up to r12, c32, t8 and s16 (requires Direct3D9) + // 3.0 allows up to r32, c224, v10 (instead of t via dcl), s16 and vFace (which can do two-sided lighting) + + // Use supplied pixel shader version (if any is given) + DWORD PSVersion = Parameters[6].Mask; + + Result = "ps_" + std::to_string(D3DSHADER_VERSION_MAJOR(PSVersion)) + + "_" + std::to_string(D3DSHADER_VERSION_MINOR(PSVersion)); + return Result; + } + case PO_XPS: { + Result = "xps.1.1"; + return Result; + } + } + + if (IsCombined) + Result = "+"; + else + Result = ""; + + Result = Result + PSH_OPCODE_DEFS[Opcode].mn + PSH_INST_MODIFIER_Str[Modifier]; + + // Output a comma-separated list of output registers : + SeparatorChar = ' '; + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._Out; i++) + { + Result = Result + SeparatorChar + Output[i].ToString(); + SeparatorChar = ','; + } + + // If this opcode has both output and input, put a space between them : + if ((PSH_OPCODE_DEFS[Opcode]._Out > 0) && (PSH_OPCODE_DEFS[Opcode]._In > 0)) + { + Result = Result + ","; + SeparatorChar = ' '; + } + + // Output a comma-separated list of parameters : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) + { + Result = Result + SeparatorChar + Parameters[i].ToString(); + SeparatorChar = ','; + } + + if ((!CommentString.empty()) + || (PSH_OPCODE_DEFS[Opcode].note != "")) + Result = Result + " ; " + PSH_OPCODE_DEFS[Opcode].note + " " + CommentString; + + return Result; +} // ToString + +bool PSH_INTERMEDIATE_FORMAT::IsArithmetic() +{ + return (Opcode >= PO_ADD); +} + +void PSH_INTERMEDIATE_FORMAT::ScaleOutput(float aFactor) +{ + assert(aFactor > 0.0f); + + if (aFactor == 1.0f) + return; + + if (aFactor == 0.5f) + { + // Half the output modifier : + switch (Modifier) { + case INSMOD_X8: + Modifier = INSMOD_X4; + break; + case INSMOD_X4: + Modifier = INSMOD_X2; + break; + case INSMOD_X2: + Modifier = INSMOD_NONE; + break; + case INSMOD_NONE: + Modifier = INSMOD_D2; + break; + case INSMOD_D2: + Modifier = INSMOD_D4; + break; + case INSMOD_D4: + Modifier = INSMOD_D8; + break; + } + + return; + } + + if (aFactor == 2.0f) + { + // Double the output modifier : + switch (Modifier) { + case INSMOD_D8: + Modifier = INSMOD_D4; + break; + case INSMOD_D4: + Modifier = INSMOD_D2; + break; + case INSMOD_D2: + Modifier = INSMOD_NONE; + break; + case INSMOD_NONE: + Modifier = INSMOD_X2; + break; + case INSMOD_X2: + Modifier = INSMOD_X4; + break; + case INSMOD_X4: + Modifier = INSMOD_X8; + break; + } + + return; + } +} + +bool PSH_INTERMEDIATE_FORMAT::ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress) // overload; +{ + int i; + bool Result; + + // Check all parameters : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) + { + // Check if one of them reads from the given register : + Result = Parameters[i].IsRegister(aRegType, aAddress); + if (Result) + return true; + } + + return false; +} + +bool PSH_INTERMEDIATE_FORMAT::ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask) // overload; +{ + int i; + bool Result; + + // Check all parameters : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) + { + // Check if one of them reads from the given register : + Result = Parameters[i].IsRegister(aRegType, aAddress, aMask); + if (Result) + return true; + } + + return false; +} + +// Used to determine the number of accesses to a register type within an instruction +// For use when determining register access limitations on certain instructions +// addressCount = the number of different registers read of the specified type +// total = the number of accesses to the spcified register type +bool PSH_INTERMEDIATE_FORMAT::ReadsFromRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, int& addressCount, int& total) // overload; +{ + int i; + bool Result; + bool RegisterUsage[256] = { false }; + + addressCount = 0; + total = 0; + + // Check all parameters : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) + { + // Check if one of them reads from the given register : + Result = Parameters[i].IsRegister(aRegType, aAddress, 0); + if (Result) + { + ++total; + if (!RegisterUsage[Parameters[i].Address]) + { + RegisterUsage[Parameters[i].Address] = true; + ++addressCount; + } + } + } + + return total > 0; +} + +bool PSH_INTERMEDIATE_FORMAT::WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress) // overload; +{ + int i; + bool Result; + + // Check the output : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._Out; i++) + { + // Check if one of them writes to the given register : + Result = Output[i].IsRegister(aRegType, aAddress); + if (Result) + return true; + } + + return false; +} + +bool PSH_INTERMEDIATE_FORMAT::WritesToRegister(PSH_ARGUMENT_TYPE aRegType, int aAddress, DWORD aMask) // overload; +{ + int i; + bool Result; + + // Check the output : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._Out; i++) + { + // Check if one of them writes to the given register : + Result = Output[i].IsRegister(aRegType, aAddress, aMask); + if (Result) + return true; + } + + return false; +} + +void PSH_INTERMEDIATE_FORMAT::SwapParameter(const int Index1, const int Index2) +// Swaps two parameters. +{ + PSH_IMD_ARGUMENT TmpParameters; + + TmpParameters = Parameters[Index1]; + Parameters[Index1] = Parameters[Index2]; + Parameters[Index2] = TmpParameters; +} + +void PSH_INTERMEDIATE_FORMAT::XSwapOutput() +// Swaps the two outputs, along with their arguments. Applies only to Xbox opcodes. +{ + PSH_IMD_ARGUMENT TmpOutput; + + // Swap output 0 with 1 : + TmpOutput = Output[0]; + Output[0] = Output[1]; + Output[1] = TmpOutput; + + // Swap parameters 0 with 2 and 1 with 3 : + SwapParameter(0, 2); + SwapParameter(1, 3); +} + +bool PSH_INTERMEDIATE_FORMAT::MoveRemovableParametersRight(const int Index1, const int Index2) +// Swaps discarded (and const) parameters to the right position, to ease later conversions. +{ + bool Result = false; + + if ( (!Parameters[Index1].UsesRegister()) + && (Parameters[Index2].UsesRegister())) + { + SwapParameter(Index1, Index2); + Result = true; + } + return Result; +} + +bool PSH_INTERMEDIATE_FORMAT::XMoveNonRegisterOutputsRight() +// Swap discards and constants to the right position, to ease later conversions. Applies only to Xbox opcodes. +{ + bool Result = false; + + // First, check if the left output is discarded, while the second isn't : + if ( (!Output[0].UsesRegister()) + && (Output[1].UsesRegister())) + { + // Swap the outputs, so the discarded version is positioned rightmost : + XSwapOutput(); + Result = true; + } + + // Also try to swap the parameters to the first operation : + if (MoveRemovableParametersRight(0, 1)) + Result = true; + + // Idem for the parameters to second operation : + if (MoveRemovableParametersRight(2, 3)) + Result = true; + return Result; +} + +void PSH_INTERMEDIATE_FORMAT::XCopySecondOpcodeToFirst(const PSH_OPCODE aOpcode) +// Copies second opcode to first position, changing the opcode type on the fly. +{ + Opcode = aOpcode; + Output[0] = Output[1]; + Parameters[0] = Parameters[2]; + Parameters[1] = Parameters[3]; +} + +bool PSH_INTERMEDIATE_FORMAT::Decode(DWORD aCombinerStageNr, DWORD PSInputs, DWORD PSOutputs, DWORD aMask) +{ + DWORD CombinerOutputFlags; + int i; + + bool Result = false; + CombinerStageNr = aCombinerStageNr; + IsCombined = aMask == MASK_A; + + // Decode first two outputs : + if (Output[0].Decode((PSOutputs >> 4) & 0xF, aMask, atOutput)) + Result = true; + if (Output[1].Decode((PSOutputs >> 0) & 0xF, aMask, atOutput)) + Result = true; + + // Get the combiner output flags : + CombinerOutputFlags = (PS_COMBINEROUTPUT)(PSOutputs >> 12); + + // Use that to choose between the four possible operations : + // - xdd (dot/dot/discard) > calculating AB=A.B and CD=C.D + // - xdm (dot/mul/discard) > calculating AB=A.B and CD=C*D + // - xmmc (mul/mul/mux) > calculating AB=A*B and CD=C*D and Mux=AB?CD + // - xmma (mul/mul/sum) > calculating AB=A*B and CD=C*D and Sum=AB+CD + if ((CombinerOutputFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) > 0) // false=Multiply, true=DotProduct + { + if ((CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0) // false=Multiply, true=DotProduct + Opcode = PO_XDD; + else + Opcode = PO_XDM; + + // Note : All arguments are already in-place for these two opcodes. + + // No 3rd output; Assert that (PSOutputs >> 8) & 0xF == PS_REGISTER_DISCARD ? + } + else + if ((CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0) // false=Multiply, true=DotProduct + { + // The first operation is a multiply, but the second is a dot-product; + // There's no opcode for that, but we can reverse the two and still use XDM : + Opcode = PO_XDM; + XSwapOutput(); + + // No 3rd output; Assert that (PSOutputs >> 8) & 0xF == PS_REGISTER_DISCARD ? + } + else + { + if (/*AB_CD_SUM=*/(CombinerOutputFlags & PS_COMBINEROUTPUT_AB_CD_MUX) == 0) // true=AB+CD, false=MUX(AB,CD) based on R0.a + Opcode = PO_XMMA; + else + Opcode = PO_XMMC; + + // This has a 3rd output, set that already : + if (Output[2].Decode((PSOutputs >> 8) & 0xF, aMask, atOutput)) + Result = true; + } + + if (Result) + { + // Handle the Output Mapping : + switch (CombinerOutputFlags & 0x38) { + case PS_COMBINEROUTPUT_BIAS: Modifier = INSMOD_BIAS; break; // TODO : Fixup occurrances! + case PS_COMBINEROUTPUT_SHIFTLEFT_1: Modifier = INSMOD_X2; break; + case PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS: Modifier = INSMOD_BX2; break; // TODO : Fixup occurrances! + case PS_COMBINEROUTPUT_SHIFTLEFT_2: Modifier = INSMOD_X4; break; + case PS_COMBINEROUTPUT_SHIFTRIGHT_1: Modifier = INSMOD_D2; break; + default /*PS_COMBINEROUTPUT_IDENTITY*/: Modifier = INSMOD_NONE; break; + } + + if ((CombinerOutputFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) > 0) // false=Alpha-to-Alpha, true=Blue-to-Alpha + { + // Note : The effect of this flag is not entirely clear - blue to alpha itself is an easy to understand operation, + // but on what output does it operate? AB? or the mux_sum destination register (which doesn't occur when a dot + // operation is executed)? What if AB is discarded, but AB+CD is registered? Also, what happens to the other + // color channels (R,G and A) in that register? The docs seem to imply that AB itself is not changed (as they + // state that the alpha portion is not necessarily discarded), which would mean that only the mux_sum output + // is influenced, but that would imply that this flag has no effect for dot-products (XDD or XDM)... + // And if this is true, how do the blue-to-alpha flags behave if present on both AB and CD? + + // TODO : Rayman does this in some shaders, requires a fixup (as output.b is incorrect and not allowed) + Output[0].Modifiers = Output[0].Modifiers | (1 << ARGMOD_BLUE_REPLICATE); + Output[0].Mask = MASK_B; + // TODO Handle blue-to-alpha flag (only valid for RGB) + // Note : We can't use the '+ ' prefix, as the blue channel is not determined yet! + // Note 2: Pixel shader 1.1-1.3 'blue replicate' on source, uses an alpha destination write mask. + } + + if ((CombinerOutputFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) > 0) // false=Alpha-to-Alpha, true=Blue-to-Alpha + { + Output[1].Modifiers = Output[1].Modifiers | (1 << ARGMOD_BLUE_REPLICATE); + Output[1].Mask = MASK_B; + } + + // Decode all four inputs : + for (i = 0; i < PSH_OPCODE_DEFS[Opcode]._In; i++) + Parameters[i].Decode((PSInputs >> ((3-i) * 8)) & 0xFF, aMask, atInput); + } + return Result; +} // Decode + +bool PSH_INTERMEDIATE_FORMAT::DecodeFinalCombiner(DWORD aPSFinalCombinerInputsABCD, DWORD aPSFinalCombinerInputsEFG) +{ + int i; +// Note : The sign bit is lost upon input to the final combiner! + +// The final combiner performs the following operations : +// +// prod register = E*F // PS_REGISTER_EF_PROD, useable in A,B,C,D,G +// +// rgbout = A*B + (1-A)*C + D // lrp tmp.rgb, A, B, C // Note : tmp can be r0 if [A,B,C,D] * r0 = [] +// // add r0.rgb, tmp.rgb, D.rgb // Otherwise use a writable register from A;B or C +// +// alphaout = G.a // mov r0.a, G.a // Not necessary if G = r0 +// +// (also the final combiner can read PS_REGISTER_V1R0_SUM, which is equal to v1 + r0) +// Normal optimizations apply, like when A = PS_REGISTER_ZERO, all we have left is C + D (add r0.rgb, C.rgb, D.rgb) +// Also, if D = PS_REGISTER_ZERO, the add can be changed into a mov (if the result isn't already in r0.rgb) + + // Note : Previously, XSokoban lost it's font rendering when the final combiner was emitted, + // when disabled, the font reappeared (in various colors). This was because constants where + // not properly set locally. + + Opcode = PO_XFC; + CombinerStageNr = XFC_COMBINERSTAGENR; + + // Decode A,B,C and D : + for (i = 0; i < 4; i++) + Parameters[i].Decode((aPSFinalCombinerInputsABCD >> ((3-i) * 8)) & 0xFF, MASK_RGB/*?*/, atFinalCombiner); + + // Decode E,F and G : + for (i = 0; i < 3; i++) + Parameters[4+i].Decode((aPSFinalCombinerInputsEFG >> ((3-i) * 8)) & 0xFF, MASK_RGB/*?*/, atFinalCombiner); + + return true; +} + +/* PSH_XBOX_SHADER */ + +void PSH_XBOX_SHADER::SetPSVersion(const uint32_t PSVersion) +{ + m_PSVersion = PSVersion; + + // Source : https://en.wikipedia.org/wiki/High-Level_Shading_Language#Pixel_shader_comparison + if (m_PSVersion >= D3DPS_VERSION(4, 0)) { + MaxInputColorRegisters = 32; + MaxTemporaryRegisters = 4096; + MaxConstantFloatRegisters = 16*4096; + MaxSamplerRegisters = 16; + MaxTextureCoordinateRegisters = 0; // In shader model 4 and up, Dependent texture limit (T) is unlimited + // Note : Input Registers (v#) are now fully floating point and the Texture Coordinate Registers (t#) have been consolidated into it. + + PSH_PC_MAX_REGISTER_COUNT = 16 * 4096; + } + else if (m_PSVersion >= D3DPS_VERSION(3, 0)) { + // Source https://msdn.microsoft.com/en-us/library/windows/desktop/bb172920(v=vs.85).aspx + MaxInputColorRegisters = 10; + MaxTemporaryRegisters = 32; + MaxConstantFloatRegisters = 224; + MaxSamplerRegisters = 16; + MaxTextureCoordinateRegisters = 0; // In shader model 3 and up, Dependent texture limit (T) is unlimited + + PSH_PC_MAX_REGISTER_COUNT = 224; + } + else if (m_PSVersion >= D3DPS_VERSION(2, 0)) { + // Source https://msdn.microsoft.com/en-us/library/windows/desktop/bb172918(v=vs.85).aspx + MaxInputColorRegisters = 2; + MaxTemporaryRegisters = 12; // 12 min/32 max: The number of r# registers is determined by D3DCAPS9.D3DPSHADERCAPS2_0.NumTemps (which ranges from 12 to 32). + MaxConstantFloatRegisters = 32; + MaxSamplerRegisters = 16; + MaxTextureCoordinateRegisters = 8; + + PSH_PC_MAX_REGISTER_COUNT = 32; + } + else + assert(false); // We no longer support less than Direct3D 9 + /* For documentation purposes, keep the below information around : + else if (m_PSVersion >= D3DPS_VERSION(1, 4)) { + // Source https://msdn.microsoft.com/en-us/library/windows/desktop/bb172917(v=vs.85).aspx + MaxConstantFloatRegisters = 8; + MaxTemporaryRegisters = 6; + MaxTextureCoordinateRegisters = 4; + MaxInputColorRegisters = 2; // 2 in phase 2 + MaxSamplerRegisters = 0; // Not yet in shader model 1 + + PSH_PC_MAX_REGISTER_COUNT = 8; + } + else if (m_PSVersion >= D3DPS_VERSION(1, 3)) { + MaxConstantFloatRegisters = 8; + MaxTemporaryRegisters = 2; + MaxTextureCoordinateRegisters = 4; + MaxInputColorRegisters = 2; + MaxSamplerRegisters = 0; // Not yet in shader model 1 + + PSH_PC_MAX_REGISTER_COUNT = 8; + } + else if (m_PSVersion >= D3DPS_VERSION(1, 2)) { + MaxConstantFloatRegisters = 8; + MaxTemporaryRegisters = 2; + MaxTextureCoordinateRegisters = 4; + MaxInputColorRegisters = 2; + MaxSamplerRegisters = 0; // Not yet in shader model 1 + + PSH_PC_MAX_REGISTER_COUNT = 8; + } + else { + // m_PSVersion >= D3DPS_VERSION(1, 1) + MaxConstantFloatRegisters = 8; + MaxTemporaryRegisters = 2; + MaxTextureCoordinateRegisters = 4; // Some sources say 2? + MaxInputColorRegisters = 2; + MaxSamplerRegisters = 0; // Not yet in shader model 1 + + PSH_PC_MAX_REGISTER_COUNT = 8; + } */ +} + +std::string PSH_XBOX_SHADER::ToString() +{ + std::string Result; + int i; + + for (i = 0; i < IntermediateCount; i++) + Result = Result + Intermediate[i].ToString() + "\n"; + + return Result; +} + +void PSH_XBOX_SHADER::Log(const char *PhaseStr) +{ + //if (MayLog(lfUnit)) + { + EmuLog(LOG_LEVEL::DEBUG, "New decoding - %s :", PhaseStr); + EmuLog(LOG_LEVEL::DEBUG, "%s", ToString().c_str()); + } +} + +PPSH_INTERMEDIATE_FORMAT PSH_XBOX_SHADER::NewIntermediate() +{ + PPSH_INTERMEDIATE_FORMAT Result = &Intermediate[IntermediateCount]; + Result->Initialize(PO_COMMENT); + ++IntermediateCount; + return Result; +} + +void PSH_XBOX_SHADER::InsertIntermediate(PPSH_INTERMEDIATE_FORMAT pIntermediate, int Index) +{ + int i; + i = IntermediateCount - 1; + while (i >= Index) + { + Intermediate[i + 1] = Intermediate[i]; + --i; + } + + Intermediate[Index] = *pIntermediate; + ++IntermediateCount; +} + +void PSH_XBOX_SHADER::DeleteIntermediate(int Index) +{ + int i; + for (i = Index; i < IntermediateCount - 1; i++) + Intermediate[i] = Intermediate[i + 1]; + + --IntermediateCount; +} + +void PSH_XBOX_SHADER::DeleteLastIntermediate() +{ + if (IntermediateCount > 0) + DeleteIntermediate(IntermediateCount - 1); +} + +std::string PSH_XBOX_SHADER::OriginalToString(XTL::X_D3DPIXELSHADERDEF *pPSDef) // static +{ + char buffer[4096]; + return std::string(buffer, sprintf(buffer, "PSAphaInputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" + "PSFinalCombinerInputsABCD = 0x%.08X\n" + "PSFinalCombinerInputsEFG = 0x%.08X\n" + "PSConstant0[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" + "PSConstant1[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" + "PSAlphaOutputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" + "PSRGBInputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" + "PSCompareMode = 0x%.08X\n" + "PSFinalCombinerConstant0 = 0x%.08X\n" + "PSFinalCombinerConstant1 = 0x%.08X\n" + "PSRGBOutputs[8] = 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X 0x%.08X\n" + "PSCombinerCount = 0x%.08X\n" + "PSTextureModes = 0x%.08X\n" + "PSDotMapping = 0x%.08X\n" + "PSInputTexture = 0x%.08X\n" + "PSC0Mapping = 0x%.08X\n" + "PSC1Mapping = 0x%.08X\n" + "PSFinalCombinerConstants = 0x%.08X\n", + pPSDef->PSAlphaInputs[0], pPSDef->PSAlphaInputs[1], pPSDef->PSAlphaInputs[2], pPSDef->PSAlphaInputs[3], + pPSDef->PSAlphaInputs[4], pPSDef->PSAlphaInputs[5], pPSDef->PSAlphaInputs[6], pPSDef->PSAlphaInputs[7], + pPSDef->PSFinalCombinerInputsABCD, + pPSDef->PSFinalCombinerInputsEFG, + pPSDef->PSConstant0[0], pPSDef->PSConstant0[1], pPSDef->PSConstant0[2], pPSDef->PSConstant0[3], + pPSDef->PSConstant0[4], pPSDef->PSConstant0[5], pPSDef->PSConstant0[6], pPSDef->PSConstant0[7], + pPSDef->PSConstant1[0], pPSDef->PSConstant1[1], pPSDef->PSConstant1[2], pPSDef->PSConstant1[3], + pPSDef->PSConstant1[4], pPSDef->PSConstant1[5], pPSDef->PSConstant1[6], pPSDef->PSConstant1[7], + pPSDef->PSAlphaOutputs[0], pPSDef->PSAlphaOutputs[1], pPSDef->PSAlphaOutputs[2], pPSDef->PSAlphaOutputs[3], + pPSDef->PSAlphaOutputs[4], pPSDef->PSAlphaOutputs[5], pPSDef->PSAlphaOutputs[6], pPSDef->PSAlphaOutputs[7], + pPSDef->PSRGBInputs[0], pPSDef->PSRGBInputs[1], pPSDef->PSRGBInputs[2], pPSDef->PSRGBInputs[3], + pPSDef->PSRGBInputs[4], pPSDef->PSRGBInputs[5], pPSDef->PSRGBInputs[6], pPSDef->PSRGBInputs[7], + pPSDef->PSCompareMode, + pPSDef->PSFinalCombinerConstant0, + pPSDef->PSFinalCombinerConstant1, + pPSDef->PSRGBOutputs[0], pPSDef->PSRGBOutputs[1], pPSDef->PSRGBOutputs[2], pPSDef->PSRGBOutputs[3], + pPSDef->PSRGBOutputs[4], pPSDef->PSRGBOutputs[5], pPSDef->PSRGBOutputs[6], pPSDef->PSRGBOutputs[7], + pPSDef->PSCombinerCount, + XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES), /* pPSDef->PSTextureModes is stored in a different place than pPSDef*/ + pPSDef->PSDotMapping, + pPSDef->PSInputTexture, + pPSDef->PSC0Mapping, + pPSDef->PSC1Mapping, + pPSDef->PSFinalCombinerConstants)); +} + +void PSH_XBOX_SHADER::GetPSTextureModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_TEXTUREMODES psTextureModes[XTL::X_D3DTS_STAGECOUNT]) +{ + for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) + { + psTextureModes[i] = (PS_TEXTUREMODES)((XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> (i * 5)) & 0x1F); + } +} + +void PSH_XBOX_SHADER::GetPSDotMapping(XTL::X_D3DPIXELSHADERDEF* pPSDef, PS_DOTMAPPING psDotMapping[XTL::X_D3DTS_STAGECOUNT]) +{ + psDotMapping[0] = (PS_DOTMAPPING)(0); + psDotMapping[1] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> 0) & 0x7); + psDotMapping[2] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> 4) & 0x7); + psDotMapping[3] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> 8) & 0x7); +} + +void PSH_XBOX_SHADER::GetPSCompareModes(XTL::X_D3DPIXELSHADERDEF* pPSDef, DWORD psCompareModes[XTL::X_D3DTS_STAGECOUNT]) +{ + for (int i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) + { + psCompareModes[i] = (pPSDef->PSCompareMode >> (i * 4)) & 0xF; + } +} + +void PSH_XBOX_SHADER::GetPSInputTexture(XTL::X_D3DPIXELSHADERDEF* pPSDef, int psInputTexture[XTL::X_D3DTS_STAGECOUNT]) +{ + psInputTexture[0] = -1; // Stage 0 has no predecessors + psInputTexture[1] = 0; // Stage 1 can only use stage 0 + psInputTexture[2] = (pPSDef->PSInputTexture >> 16) & 0x1; // Stage 2 can use stage 0 or 1 + psInputTexture[3] = (pPSDef->PSInputTexture >> 20) & 0x3; // Stage 3 can only use stage 0, 1 or 2 +} + +void PSH_XBOX_SHADER::Decode(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ + int i; + + /* Azurik likes to create and destroy the same shader every frame! O_o + LogFlags = lfUnit; + if (IsRunning(TITLEID_AZURIK)) + LogFlags = LogFlags | lfExtreme;*/ + + GetPSTextureModes(pPSDef, PSTextureModes); + GetPSCompareModes(pPSDef, PSCompareMode); + GetPSDotMapping(pPSDef, PSDotMapping); + GetPSInputTexture(pPSDef, PSInputTexture); + + NumberOfCombiners = (pPSDef->PSCombinerCount >> 0) & 0xF; + CombinerCountFlags = (pPSDef->PSCombinerCount >> 8); + + CombinerMuxesOnMsb = (CombinerCountFlags & PS_COMBINERCOUNT_MUX_MSB) > 0; + CombinerHasUniqueC0 = (CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C0) > 0; + CombinerHasUniqueC1 = (CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C1) > 0; + + // Backwards compatible decoding (purely for logging) : + { + for (i = 0; i < XTL::X_PSH_COMBINECOUNT; i++) { + Combiners[i].RGB.Decode(pPSDef->PSRGBInputs[i], pPSDef->PSRGBOutputs[i]); + Combiners[i].Alpha.Decode(pPSDef->PSAlphaInputs[i], pPSDef->PSAlphaOutputs[i], /*aIsAlpha=*/true); + } + + FinalCombiner.Decode(pPSDef->PSFinalCombinerInputsABCD, pPSDef->PSFinalCombinerInputsEFG, pPSDef->PSFinalCombinerConstants); + } +} + +PSH_RECOMPILED_SHADER PSH_XBOX_SHADER::Convert(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ + int i; + Recompiled = {}; + Recompiled.PSDef = *pPSDef; + + // Use a fluent interface to start with a pixel shader version opcode that knowns the host version + NewIntermediate()->Initialize(PO_XPS)->Parameters[6].Mask = m_PSVersion; + + for (i = 0; i < NumberOfCombiners; i++) + { + // Check that the RGB and Alpha inputs do the same operation : + if ( ((pPSDef->PSRGBInputs[i] & PS_NoChannelsMask) == (pPSDef->PSAlphaInputs[i] & PS_NoChannelsMask)) + // Check if all RGB channels are set to read from PS_CHANNEL_RGB : + && ((pPSDef->PSRGBInputs[i] & PS_AlphaChannelsMask) == 0) + // Check if all Alpha channels are set to read from PS_CHANNEL_ALPHA : + && ((pPSDef->PSAlphaInputs[i] & PS_AlphaChannelsMask) == PS_AlphaChannelsMask) + // Check that RGB and Alpha output to the same register(s) : + && (pPSDef->PSRGBOutputs[i] == pPSDef->PSAlphaOutputs[i])) + { + // In this case, we can convert RGB and Alpha together : + if (!NewIntermediate()->Decode(i, pPSDef->PSRGBInputs[i], pPSDef->PSRGBOutputs[i], MASK_RGBA)) + DeleteLastIntermediate(); + } + else + { + // Otherwise, we need to convert RGB and Alpha separately : + if (!NewIntermediate()->Decode(i, pPSDef->PSRGBInputs[i], pPSDef->PSRGBOutputs[i], MASK_RGB)) + DeleteLastIntermediate(); + + if (!NewIntermediate()->Decode(i, pPSDef->PSAlphaInputs[i], pPSDef->PSAlphaOutputs[i], MASK_A)) + DeleteLastIntermediate(); + } + } + + if ((pPSDef->PSFinalCombinerInputsABCD > 0) + || (pPSDef->PSFinalCombinerInputsEFG > 0)) { + if (NewIntermediate()->DecodeFinalCombiner(pPSDef->PSFinalCombinerInputsABCD, pPSDef->PSFinalCombinerInputsEFG)) + { + FinalCombinerFlags = (PS_FINALCOMBINERSETTING)((pPSDef->PSFinalCombinerInputsEFG >> 0) & 0xFF); +// dwPS_GLOBALFLAGS = (pPSDef->PSFinalCombinerConstants >> 8) & 0x1; + } + else + DeleteLastIntermediate(); + } + // Dump the contents of the PixelShader def + //if (MayLog(LogFlags)) + // dump pixel shader definition to string + // TODO : Reinstate : XTL_DumpPixelShaderToFile(pPSDef); + + //if (MayLog(LogFlags)) + { + // print relevant contents to the debug console + EmuLog(LOG_LEVEL::DEBUG, "%s", DecodedToString(pPSDef).c_str()); + } + + // TODO: + // - Insert tex* and def instructions + + Log("Parse result"); + + if (MoveRemovableParametersRight()) + Log("MoveRemovableParametersRight"); + + if (RemoveNops()) + Log("RemoveNops"); + + while (RemoveUselessWrites()) { + Log("RemoveUselessWrites"); + if (RemoveNops()) + Log("RemoveNops"); + } + + if (ConvertConstantsToNative(pPSDef, /*Recompiled=*/&Recompiled)) + Log("ConvertConstantsToNative"); + + // Handle Texture declarations : + if (DecodeTextureModes(pPSDef)) + Log("DecodeTextureModes"); + + ConvertXboxOpcodesToNative(pPSDef); + Log("ConvertXboxOpcodesToNative"); + + while (RemoveUselessWrites()) { // again + Log("RemoveUselessWrites"); + if (RemoveNops()) + Log("RemoveNops"); + } + + // Resolve all differences : + if (FixupPixelShader()) + Log("FixupPixelShader"); + + if (FixInvalidDstRegister()) + Log("FixInvalidDstRegister"); + + if (FixConstantParameters()) + Log("FixConstantParameters"); + + if (FixArgumentModifiers()) + Log("FixArgumentModifiers"); + + if (FixInstructionModifiers()) + Log("FixInstructionModifiers"); + + if (FixInvalidSrcSwizzle()) + Log("FixInvalidSrcSwizzle"); + + if (FixMissingR0a()) + Log("FixMissingR0a"); + + if (FixMissingR1a()) + Log("FixMissingR1a"); + + if (FixCoIssuedOpcodes()) + Log("FixCoIssuedOpcodes"); + + if (FixOverusedRegisters()) + Log("FixOverusedRegisters"); + + if (FixUninitializedReads()) + Log("FixUninitializedReads"); + + if (FinalizeShader()) + Log("FinalizeShader"); + + Log("End result"); + + Recompiled.NewShaderStr = ToString(); + return Recompiled; +} + +std::string PSH_XBOX_SHADER::DecodedToString(XTL::X_D3DPIXELSHADERDEF *pPSDef) +// print relevant contents to the debug console + + #define _AddStr1(aStr) \ + \ + Result = Result + aStr + "\n"; + + #define _AddStr(aStr, ...) \ + {\ + _AddStr1(std::string(buf, sprintf(buf, aStr, __VA_ARGS__))); \ + } +{ + char buf[256]; + int i; + + std::string Result = ""; + // Show the contents to the user + _AddStr1("\n-----PixelShader Definition Contents-----"); + _AddStr1(OriginalToString(pPSDef)); + + if (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) > 0) + { + _AddStr1("\nPSTextureModes ->"); // Texture addressing modes + _AddStr("Stage 0: %s", PS_TextureModesStr[PSTextureModes[0]]); + _AddStr("Stage 1: %s", PS_TextureModesStr[PSTextureModes[1]]); + _AddStr("Stage 2: %s", PS_TextureModesStr[PSTextureModes[2]]); + _AddStr("Stage 3: %s", PS_TextureModesStr[PSTextureModes[3]]); + } + + if (pPSDef->PSDotMapping > 0) // Input mapping for dot product modes + { + _AddStr1("\nPSDotMapping ->"); + _AddStr("Stage 1: %s", PS_DotMappingStr[PSDotMapping[1]]); + _AddStr("Stage 2: %s", PS_DotMappingStr[PSDotMapping[2]]); + _AddStr("Stage 3: %s", PS_DotMappingStr[PSDotMapping[3]]); + } + + if (pPSDef->PSCompareMode > 0) // Compare modes for clipplane texture mode + { + _AddStr1("\nPSCompareMode ->"); + _AddStr("Stage 0: %s", PS_CompareModeStr[(PSCompareMode[0] == 0) ? 0 : 1]); + _AddStr("Stage 1: %s", PS_CompareModeStr[(PSCompareMode[1] == 0) ? 2 : 3]); + _AddStr("Stage 2: %s", PS_CompareModeStr[(PSCompareMode[2] == 0) ? 4 : 5]); + _AddStr("Stage 3: %s", PS_CompareModeStr[(PSCompareMode[3] == 0) ? 6 : 7]); + } + + if (pPSDef->PSInputTexture > 0) // Texture source for some texture modes + { + _AddStr1("\nPSInputTexture ->"); + _AddStr("Stage 1: %d", PSInputTexture[1]); + _AddStr("Stage 2: %d", PSInputTexture[2]); + _AddStr("Stage 3: %d", PSInputTexture[3]); + } + + if (pPSDef->PSCombinerCount > 0) // Active combiner count (Stages 0-7) + { + _AddStr1("\nPSCombinerCount ->"); + _AddStr("Combiners: %d", NumberOfCombiners); + _AddStr("Mux: %s", PS_CombinerCountFlagsStr[(CombinerCountFlags & PS_COMBINERCOUNT_MUX_MSB) == 0 ? 0 : 1]); + _AddStr("C0: %s", PS_CombinerCountFlagsStr[(CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C0) == 0 ? 2 : 3]); + _AddStr("C1: %s", PS_CombinerCountFlagsStr[(CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C1) == 0 ? 4 : 5]); + } + + // Dxbx additions from here onwards : + + for (i = 0; i < NumberOfCombiners; i++) // Loop over all combiner stages + { + _AddStr1("\n"); + + _AddStr("PSRGBOutputs[%d] AB: %s", i, Combiners[i].RGB.OutputSUM.OutputAB.DecodedToString().c_str()); + _AddStr("PSRGBOutputs[%d] CD: %s", i, Combiners[i].RGB.OutputSUM.OutputCD.DecodedToString().c_str()); + _AddStr("PSRGBOutputs[%d] SUM: %s", i, Combiners[i].RGB.OutputSUM.DecodedToString().c_str()); + _AddStr("PSRGBOutputs[%d] flags: %s", i, PSCombinerOutputFlagsToStr(Combiners[i].RGB.CombinerOutputFlags, /*aIsAlpha=*/false).c_str()); + + _AddStr1("\n"); + _AddStr("PSRGBInputs[%d] A: %s", i, Combiners[i].RGB.OutputSUM.OutputAB.Input1.DecodedToString().c_str()); + _AddStr("PSRGBInputs[%d] B: %s", i, Combiners[i].RGB.OutputSUM.OutputAB.Input2.DecodedToString().c_str()); + _AddStr("PSRGBInputs[%d] C: %s", i, Combiners[i].RGB.OutputSUM.OutputCD.Input1.DecodedToString().c_str()); + _AddStr("PSRGBInputs[%d] D: %s", i, Combiners[i].RGB.OutputSUM.OutputCD.Input2.DecodedToString().c_str()); + + _AddStr1("\n"); + _AddStr("PSAlphaOutputs[%d] AB: %s", i, Combiners[i].Alpha.OutputSUM.OutputAB.DecodedToString().c_str()); + _AddStr("PSAlphaOutputs[%d] CD: %s", i, Combiners[i].Alpha.OutputSUM.OutputCD.DecodedToString().c_str()); + _AddStr("PSAlphaOutputs[%d] SUM: %s", i, Combiners[i].Alpha.OutputSUM.DecodedToString().c_str()); + _AddStr("PSAlphaOutputs[%d] flags: %s", i, PSCombinerOutputFlagsToStr(Combiners[i].Alpha.CombinerOutputFlags, /*aIsAlpha=*/true).c_str()); + + _AddStr1("\n"); + _AddStr("PSAlphaInputs[%d] A: %s", i, Combiners[i].Alpha.OutputSUM.OutputAB.Input1.DecodedToString().c_str()); + _AddStr("PSAlphaInputs[%d] B: %s", i, Combiners[i].Alpha.OutputSUM.OutputAB.Input2.DecodedToString().c_str()); + _AddStr("PSAlphaInputs[%d] C: %s", i, Combiners[i].Alpha.OutputSUM.OutputCD.Input1.DecodedToString().c_str()); + _AddStr("PSAlphaInputs[%d] D: %s", i, Combiners[i].Alpha.OutputSUM.OutputCD.Input2.DecodedToString().c_str()); + + _AddStr1("\n"); + _AddStr("PSConstant0[%d] : %x", i, pPSDef->PSConstant0[i]); // C0 for each stage + _AddStr("PSConstant1[%d] : %x", i, pPSDef->PSConstant1[i]); // C1 for each stage + } + + if ((pPSDef->PSFinalCombinerInputsABCD > 0) + || (pPSDef->PSFinalCombinerInputsEFG > 0)) // Final combiner inputs + { + _AddStr("\nPSFinalCombinerConstant0 : %x", pPSDef->PSFinalCombinerConstant0); // C0 in final combiner + _AddStr("PSFinalCombinerConstant1 : %x", pPSDef->PSFinalCombinerConstant1); // C1 in final combiner + + _AddStr1("\nPSFinalCombinerInputsABCD ->"); + _AddStr("Input A: %s", FinalCombiner.InputA.DecodedToString().c_str()); + _AddStr("Input B: %s", FinalCombiner.InputB.DecodedToString().c_str()); + _AddStr("Input C: %s", FinalCombiner.InputC.DecodedToString().c_str()); + _AddStr("Input D: %s", FinalCombiner.InputD.DecodedToString().c_str()); + + _AddStr1("\nPSFinalCombinerInputsEFG ->"); + _AddStr("Input E: %s", FinalCombiner.InputE.DecodedToString().c_str()); + _AddStr("Input F: %s", FinalCombiner.InputF.DecodedToString().c_str()); + _AddStr("Input G: %s", FinalCombiner.InputG.DecodedToString().c_str()); + _AddStr("Final combiner setting: %s", PSFinalCombinerSettingToStr((DWORD)(FinalCombiner.FinalCombinerFlags)).c_str()); + + _AddStr1("\nPSFinalCombinerConstants ->"); // Final combiner constant mapping + _AddStr("Offset of D3D constant for (C0: %d", FinalCombiner.FinalCombinerC0Mapping); + _AddStr("Offset of D3D constant for (C1: %d", FinalCombiner.FinalCombinerC1Mapping); + _AddStr("Adjust texture flag: %s", PS_GlobalFlagsStr[PS_GLOBALFLAGS(FinalCombiner.dwPS_GLOBALFLAGS)]); + } + + _AddStr1("\n"); + return Result; +} + + bool _OpcodeMustStayBeforeTextureMode(PSH_OPCODE Opcode, int i) + { + if (Opcode == PO_XPS) + return true; + + // Before texture modes, only keep the first comment (the one mentioning "xps" got converted into "ps") + if (Opcode == PO_COMMENT) + return (i == 0); + + if (Opcode == PO_PS) + return true; + + if (Opcode == PO_DEF) + return true; + + if (Opcode >= PO_DCL && Opcode <= PO_DCL_VOLUME) + return true; + + return false; + } + + bool PSH_XBOX_SHADER::_NextIs2D(int Stage) + { + if (Stage < XTL::X_D3DTS_STAGECOUNT-1) + return (PSTextureModes[Stage + 1] == PS_TEXTUREMODES_DOT_ST) || (PSTextureModes[Stage + 1] == PS_TEXTUREMODES_DOT_ZW); + else + return false; + } + +bool PSH_XBOX_SHADER::DecodeTextureModes(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ + int InsertPos; + PSH_INTERMEDIATE_FORMAT Ins = {}; + std::vector InsertIns; + int Stage; + + InsertIns.reserve(32); // arbitrary allotment of instructions + InsertIns.resize(XTL::X_D3DTS_STAGECOUNT); // default initialized to PO_COMMENT instructions + + bool Result = false; + + InsertPos = -1; + do { + ++InsertPos; + } while (_OpcodeMustStayBeforeTextureMode(Intermediate[InsertPos].Opcode, InsertPos)); + + Ins.Initialize(PO_DCL); + for (Stage = 0; Stage < XTL::X_D3DTS_STAGECOUNT; Stage++) + { + if (PSTextureModes[Stage] != PS_TEXTUREMODES_NONE || Stage < PSH_XBOX_MAX_T_REGISTER_COUNT) + { + switch (PSTextureModes[Stage]) + { + case PS_TEXTUREMODES_PROJECT2D: // argb = texture(r/q, s/q) TODO : Apply the division via D3DTOP_BUMPENVMAP ? + case PS_TEXTUREMODES_BUMPENVMAP: + case PS_TEXTUREMODES_BUMPENVMAP_LUM: + case PS_TEXTUREMODES_DOT_ST: + case PS_TEXTUREMODES_DPNDNT_AR: + case PS_TEXTUREMODES_DPNDNT_GB: + { + Ins.Opcode = PO_DCL_2D; + Ins.Output[0].SetRegister(PARAM_S, Stage, MASK_RGBA); + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + Result = true; + break; + } + case PS_TEXTUREMODES_PROJECT3D: // argb = texture(r/q, s/q, t/q) Note : 3d textures are sampled using PS_TEXTUREMODES_CUBEMAP + case PS_TEXTUREMODES_BRDF: + case PS_TEXTUREMODES_DOT_STR_3D: + { + Ins.Opcode = PO_DCL_VOLUME; + Ins.Output[0].SetRegister(PARAM_S, Stage, MASK_RGBA); + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + Result = true; + break; + } + case PS_TEXTUREMODES_CUBEMAP: // argb = cubemap(r/q, s/q, t/q) + case PS_TEXTUREMODES_DOT_RFLCT_DIFF: + case PS_TEXTUREMODES_DOT_RFLCT_SPEC: + case PS_TEXTUREMODES_DOT_STR_CUBE: + case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST: + { + Ins.Opcode = PO_DCL_CUBE; + Ins.Output[0].SetRegister(PARAM_S, Stage, MASK_RGBA); + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + Result = true; + break; + } + } + + Ins.Opcode = PO_DCL; + Ins.Output[0].SetRegister(PARAM_T, Stage, MASK_RGBA); + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + Result = true; + } + } + + for (int j = 0; j < PSH_XBOX_MAX_V_REGISTER_COUNT; ++j) + { + Ins.Opcode = PO_DCL; + Ins.Output[0].SetRegister(PARAM_V, j, MASK_RGBA); + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + Result = true; + } + + PSH_OPCODE Opcode; + + Opcode = PO_TEXLD2; + + for (Stage = 0; Stage < XTL::X_D3DTS_STAGECOUNT; Stage++) + { + // TODO : Apply conversions when PS_GLOBALFLAGS_TEXMODE_ADJUST is set (but ... how to check the texture type? read D3DRS_PSTEXTUREMODES?) + + // Convert the texture mode to a texture addressing instruction : + switch (PSTextureModes[Stage]) { // input = q,s,t,r (same layout as a,r,g,b, also known as w,x,y,z) + case PS_TEXTUREMODES_PROJECT2D: // argb = texture(r/q, s/q) TODO : Apply the division via D3DTOP_BUMPENVMAP ? + case PS_TEXTUREMODES_PROJECT3D: // argb = texture(r/q, s/q, t/q) Note : 3d textures are sampled using PS_TEXTUREMODES_CUBEMAP + case PS_TEXTUREMODES_CUBEMAP: { // argb = cubemap(r/q, s/q, t/q) + Opcode = PO_TEXLD2; + + if (m_PSVersion >= D3DPS_VERSION(3, 0)) + continue; + break; + } + case PS_TEXTUREMODES_NONE: + case PS_TEXTUREMODES_PASSTHRU: + Opcode = PO_MOV; + break; + case PS_TEXTUREMODES_CLIPPLANE: Opcode = PO_TEXKILL; break; + case PS_TEXTUREMODES_BUMPENVMAP: Opcode = PO_TEXBEM; break; + case PS_TEXTUREMODES_BUMPENVMAP_LUM: Opcode = PO_TEXBEML; break; + case PS_TEXTUREMODES_BRDF: Opcode = PO_TEXBRDF; break; // Note : Not supported by Direct3D8 ? + case PS_TEXTUREMODES_DOT_ST: Opcode = PO_TEXM3X2TEX; break; + case PS_TEXTUREMODES_DOT_ZW: Opcode = PO_TEXM3X2DEPTH; break; // Note : requires ps.1.3 and a preceding texm3x2pad + case PS_TEXTUREMODES_DOT_RFLCT_DIFF: Opcode = PO_TEXM3X3DIFF; break; // Note : Not supported by Direct3D8 ? + case PS_TEXTUREMODES_DOT_RFLCT_SPEC: Opcode = PO_TEXM3X3VSPEC; break; + case PS_TEXTUREMODES_DOT_STR_3D: Opcode = PO_TEXM3X3TEX; break; // Note : Uses a 3d texture + case PS_TEXTUREMODES_DOT_STR_CUBE: Opcode = PO_TEXM3X3TEX; break; // Note : Uses a cube texture + case PS_TEXTUREMODES_DPNDNT_AR: Opcode = PO_TEXREG2AR; break; + case PS_TEXTUREMODES_DPNDNT_GB: Opcode = PO_TEXREG2GB; break; + case PS_TEXTUREMODES_DOTPRODUCT: + if (_NextIs2D(Stage)) + Opcode = PO_TEXM3X2PAD; + else + Opcode = PO_TEXM3X3PAD; + break; + case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST: Opcode = PO_TEXM3X3SPEC; break; // Note : Needs 3 arguments! + default: + continue; + } + + InsertTextureModeInstruction(pPSDef, Stage, Opcode, InsertIns, InsertPos); + Result = true; + } + if (Result) + { + for (unsigned i = 0; i < InsertIns.size(); ++i) + { + if (i >= XTL::X_D3DTS_STAGECOUNT || InsertIns[i].Opcode != PO_COMMENT) + { + InsertIntermediate(&InsertIns[i], InsertPos); + ++InsertPos; + } + } + } + StartPos = InsertPos + 1; + return Result; +} + +int PSH_XBOX_SHADER::GetTextureStageModifiers(int Stage) +{ + int modifiers = 0; + switch (PSDotMapping[Stage]) + { + case PS_DOTMAPPING_ZERO_TO_ONE: + break; + case PS_DOTMAPPING_MINUS1_TO_1_D3D: + modifiers = (1 << ARGMOD_SCALE_BX2); + break; + case PS_DOTMAPPING_MINUS1_TO_1_GL: + break; + case PS_DOTMAPPING_MINUS1_TO_1: + break; + case PS_DOTMAPPING_HILO_1: + break; + case PS_DOTMAPPING_HILO_HEMISPHERE: + break; + default: + break; + } + + return modifiers; +} + +void PSH_XBOX_SHADER::InsertTex3x2Instructions(int Stage, int inputStage, std::vector& InsertIns) +{ + PSH_INTERMEDIATE_FORMAT Ins = {}; + + const int modifiers = GetTextureStageModifiers(Stage); + + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 1, 0); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); + Ins.Parameters[1].Modifiers = modifiers; + InsertIns.emplace_back(Ins); + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 0, 0); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); + Ins.Parameters[1].Modifiers = modifiers; + InsertIns.emplace_back(Ins); +} + +void PSH_XBOX_SHADER::InsertTex3x3Instructions(int Stage, int inputStage, std::vector& InsertIns) +{ + PSH_INTERMEDIATE_FORMAT Ins = {}; + + const int modifiers = GetTextureStageModifiers(Stage); + + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 2, 0); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); + Ins.Parameters[1].Modifiers = modifiers; + InsertIns.emplace_back(Ins); + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 1, 0); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); + Ins.Parameters[1].Modifiers = modifiers; + InsertIns.emplace_back(Ins); + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); + Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage - 0, 0); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, 0); + Ins.Parameters[1].Modifiers = modifiers; + InsertIns.emplace_back(Ins); +} + +bool PSH_XBOX_SHADER::InsertTextureModeInstruction(XTL::X_D3DPIXELSHADERDEF *pPSDef, int Stage, PSH_OPCODE opcode, std::vector& InsertIns, int& InsertPos) +{ + PSH_INTERMEDIATE_FORMAT Ins = {}; + + bool Result = false; + + PSH_ARGUMENT_TYPE type = PARAM_T; + int inputStage = Stage; + int mask = 0; + + // TODO: Refactor and optimize + // TODO: Update handling to support 1.4? + bool needsInitialization = false; + switch (opcode) + { + case PO_TEXBEM: + case PO_TEXBEML: + { + inputStage = PSInputTexture[Stage]; + + // If the bump-map texture format is X_D3DFMT_X8L8V8U8 or X_D3DFMT_L6V5U5 we need to apply a bias + // This happens because these formats are an alias of unsigned texture formats. + // Fixes an issue with the JSRF boost-dash effect + // NOTE: This assumes that this shader will only ever be used for the input bumpmap texture + // If this causes regressions in other titles, we'll need to be smarter about this + // and include the texture formats in the shader hash, somehow. + bool bias = false; + auto biasModifier = (1 << ARGMOD_SCALE_BX2); + auto pXboxTexture = g_pXbox_SetTexture[inputStage]; + if (pXboxTexture != nullptr) { + extern XTL::X_D3DFORMAT GetXboxPixelContainerFormat(const XTL::X_D3DPixelContainer *pXboxPixelContainer); // TODO : Move to XTL-independent header file + + switch (GetXboxPixelContainerFormat(pXboxTexture)) { + case XTL::X_D3DFMT_L6V5U5: { + extern XTL::X_D3DRESOURCETYPE GetXboxD3DResourceType(const XTL::X_D3DResource *pXboxResource); // TODO : Move to XTL-independent header file + extern bool IsSupportedFormat(XTL::X_D3DFORMAT X_Format, XTL::X_D3DRESOURCETYPE XboxResourceType, DWORD D3DUsage); // TODO : Move to XTL-independent header file + + // L6V5U5 format is converted incorrectly if not supported by the device + XTL::X_D3DRESOURCETYPE XboxResourceType = GetXboxD3DResourceType(pXboxTexture); + DWORD D3DUsage = 0; // TODO : Since it's not yet know how to determine D3DUsage in this case, 'hack' it by using no specific D3DUSAGE_* flags. + + bias = !IsSupportedFormat(/*XboxFormat=*/XTL::X_D3DFMT_L6V5U5, XboxResourceType, D3DUsage); + break; + } + case XTL::X_D3DFMT_X8L8V8U8: { + bias = true; + break; + } + } + } + + Ins.Initialize(PO_MAD); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT00, Stage, Recompiled); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_R); + + if (bias) { + Ins.Parameters[1].Modifiers = biasModifier; + } + + Ins.Parameters[2].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, MASK_R); + InsertIns.emplace_back(Ins); + Ins.Initialize(PO_MAD); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT10, Stage, Recompiled); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_G); + if (bias) { + Ins.Parameters[1].Modifiers = biasModifier; + } + Ins.Parameters[2].SetRegister(PARAM_R, 1, MASK_R); + InsertIns.emplace_back(Ins); + // + Ins.Initialize(PO_MAD); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT01, Stage, Recompiled); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_R); + if (bias) { + Ins.Parameters[1].Modifiers = biasModifier; + } + Ins.Parameters[2].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, MASK_G); + InsertIns.emplace_back(Ins); + Ins.Initialize(PO_MAD); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVMAT11, Stage, Recompiled); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_G); + if (bias) { + Ins.Parameters[1].Modifiers = biasModifier; + } + Ins.Parameters[2].SetRegister(PARAM_R, 1, MASK_G); + InsertIns.emplace_back(Ins); + + Ins.CommentString = ""; + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + Ins.Parameters[1].Modifiers = 0; + InsertIns.emplace_back(Ins); + + if (opcode == PO_TEXBEML) + { + // + Ins.Initialize(PO_MAD); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); + Ins.Parameters[0].SetScaleBemLumRegister(D3DTSS_BUMPENVLSCALE, Stage, Recompiled); + Ins.Parameters[1].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + inputStage, MASK_B); + Ins.Parameters[2].SetScaleBemLumRegister(D3DTSS_BUMPENVLOFFSET, Stage, Recompiled); + InsertIns.emplace_back(Ins); + // + Ins.Initialize(PO_MUL); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[1].SetRegister(PARAM_R, 1, MASK_B); + InsertIns.emplace_back(Ins); + } + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + + break; + } + case PO_TEXBRDF: + inputStage = PSInputTexture[Stage]; + break; + case PO_TEXM3X2TEX: + { + inputStage = PSInputTexture[Stage]; + + InsertTex3x2Instructions(Stage, inputStage, InsertIns); + + Ins.CommentString = ""; + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXM3X3TEX: + { + inputStage = PSInputTexture[Stage]; + + InsertTex3x3Instructions(Stage, inputStage, InsertIns); + + Ins.CommentString = ""; + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXM3X2DEPTH: + { + inputStage = PSInputTexture[Stage]; + + InsertTex3x2Instructions(Stage, inputStage, InsertIns); + + Ins.CommentString = ""; + Ins.Initialize(PO_RCP); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); + Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_G); + InsertIns.emplace_back(Ins); + + Ins.Initialize(PO_MUL); + Ins.Modifier = INSMOD_SAT; + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); + Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[1].SetRegister(PARAM_R, 1, MASK_B); + InsertIns.emplace_back(Ins); + + Ins.Initialize(PO_CMP); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_B); + Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); + Ins.Parameters[1].SetScaleConstRegister(1.0, Recompiled); + Ins.Parameters[2].SetRegister(PARAM_R, 1, MASK_B); + InsertIns.emplace_back(Ins); + + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_oDepth, 0, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, MASK_B); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXM3X3DIFF: + { + inputStage = PSInputTexture[Stage]; + + InsertTex3x3Instructions(Stage, inputStage, InsertIns); + + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXM3X3VSPEC: + case PO_TEXM3X3SPEC: + { + inputStage = PSInputTexture[Stage]; + + InsertTex3x3Instructions(Stage, inputStage, InsertIns); + + int baseRegister = PSH_XBOX_MAX_R_REGISTER_COUNT + PSH_XBOX_MAX_T_REGISTER_COUNT; + + // get eye-ray vector + Ins.Initialize(PO_COMMENT); + Ins.CommentString = "; get eye-ray vector"; + InsertIns.emplace_back(Ins); + if (opcode == PO_TEXM3X3VSPEC) + { + // E.x + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_T, Stage - 2, MASK_A); + InsertIns.emplace_back(Ins); + // E.y + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_T, Stage - 1, MASK_A); + InsertIns.emplace_back(Ins); + // E.z + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_B); + Ins.Parameters[0].SetRegister(PARAM_T, Stage - 0, MASK_A); + InsertIns.emplace_back(Ins); + // E.w + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, MASK_A); + Ins.Parameters[0].SetScaleConstRegister(0.0, Recompiled); + InsertIns.emplace_back(Ins); + } + else + { + // E + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 0, 0); + Ins.Parameters[0].SetRegister(PARAM_C, 0, 0); + InsertIns.emplace_back(Ins); + } + + // compute reflection vector + Ins.Initialize(PO_COMMENT); + Ins.CommentString = "; compute reflection vector"; + InsertIns.emplace_back(Ins); + // N.E + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_R, baseRegister + 0, 0); + InsertIns.emplace_back(Ins); + // 2 * (N.E) + Ins.Initialize(PO_MUL); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); + Ins.Parameters[1].SetScaleConstRegister(2.0, Recompiled); + InsertIns.emplace_back(Ins); + // N.N + Ins.Initialize(PO_DP3); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_R, 1, 0); + InsertIns.emplace_back(Ins); + // 1 / (N.N) + Ins.Initialize(PO_RCP); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_R, baseRegister + 1, MASK_G); + InsertIns.emplace_back(Ins); + // 2 * N.E / N.N + Ins.Initialize(PO_MUL); + Ins.Output[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_R, baseRegister + 1, MASK_R); + Ins.Parameters[1].SetRegister(PARAM_R, baseRegister + 1, MASK_G); + InsertIns.emplace_back(Ins); + // 2 * N.E / N.N * N - E + Ins.Initialize(PO_MAD); + Ins.Output[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_R, baseRegister + 1, MASK_R); + Ins.Parameters[2].SetRegister(PARAM_R, baseRegister + 0, 0); + Ins.Parameters[2].Modifiers = (1 << ARGMOD_NEGATE); + InsertIns.emplace_back(Ins); + + Ins.CommentString = ""; + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXREG2AR: + { + inputStage = PSInputTexture[Stage]; + + // E.x + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_A); + InsertIns.emplace_back(Ins); + // E.y + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_R); + InsertIns.emplace_back(Ins); + + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXREG2GB: + { + inputStage = PSInputTexture[Stage]; + + // E.x + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_R); + Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_G); + InsertIns.emplace_back(Ins); + // E.y + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, 1, MASK_G); + Ins.Parameters[0].SetRegister(PARAM_T, Stage, MASK_B); + InsertIns.emplace_back(Ins); + + Ins.Initialize(PO_TEXLD2); + Ins.Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + Ins.Parameters[0].SetRegister(PARAM_R, 1, 0); + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + InsertIns.emplace_back(Ins); + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + case PO_TEXM3X2PAD: + case PO_TEXM3X3PAD: + { + inputStage = PSInputTexture[Stage]; + + opcode = PO_MOV; + inputStage = Stage; + needsInitialization = true; + break; + } + + case PO_TEXLD: + case PO_TEXLD2: + case PO_TEXCRD: + case PO_MOV: + needsInitialization = true; + break; + default: + break; + } + + Ins.Initialize(opcode); + + if (needsInitialization) + { + type = PARAM_R; + + // Insert move instructions in reverse order to prevent overwriting wrong register + // Create instructions to move loaded temporary registers into extra temporary registers + InsertIns[XTL::X_D3DTS_STAGECOUNT - Stage - 1].Initialize(PO_MOV); + InsertIns[XTL::X_D3DTS_STAGECOUNT - Stage - 1].Output[0].SetRegister(PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, 0); + InsertIns[XTL::X_D3DTS_STAGECOUNT - Stage - 1].Parameters[0].SetRegister(PARAM_R, Stage, 0); + + if (Ins.Opcode == PO_TEXCRD) + { + mask = MASK_RGB; + } + else + { + } + + // Replace texture coordinate register usage up until first usage as output + int lastUsed = RegisterIsUsedFromIndexUntil(InsertPos, PARAM_T, Stage); + + if (lastUsed >= 0) + { + ReplaceInputRegisterFromIndexOnwards(InsertPos, PARAM_T, Stage, PARAM_R, PSH_XBOX_MAX_R_REGISTER_COUNT + Stage, lastUsed); + } + } + Ins.Output[0].SetRegister(type, Stage, mask); + + // For those texture modes that need it, add the source stage as argument : + if (PSH_OPCODE_DEFS[Ins.Opcode]._In >= 1) + { + Ins.Parameters[0].SetRegister(PARAM_T, inputStage, 0); + + if (Ins.Opcode >= PO_TEXDP3TEX && Ins.Opcode <= PO_TEXM3X3SPEC) + { + Ins.Parameters[0].Modifiers = GetTextureStageModifiers(Stage); + } + } + + if (PSH_OPCODE_DEFS[Ins.Opcode]._In >= 2) + { + if (Ins.Opcode == PO_TEXLD2) + { + Ins.Parameters[1].SetRegister(PARAM_S, Stage, 0); + } + + // Add the third argument : + switch (PSTextureModes[Stage]) { + case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST: + { + Ins.Parameters[1].SetRegister(PARAM_C, 0, 0); + Ins.CommentString = "Dxbx guess"; // TODO : Where do we get the 3rd argument to this? + break; + } + } + } + +// // Warn about unprocessed flag : +// if ((dwPS_GLOBALFLAGS & PS_GLOBALFLAGS_TEXMODE_ADJUST) > 0) +// Ins.CommentString = Ins.CommentString + " PS_GLOBALFLAGS_TEXMODE_ADJUST unhandled!"; + + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + Result = true; + + return Result; +} + +bool PSH_XBOX_SHADER::MoveRemovableParametersRight() +{ + int i; + + bool Result = false; + + // For all opcodes, try to put constant and discarded arguments in the rightmost slot, to ease following analysis : + i = IntermediateCount; + while (i > StartPos) + { + --i; + + switch (Intermediate[i].Opcode) { +// case PO_SUB: // 1-x is not the same as x-1, but can still be reduced - see SimplifySUB + case PO_ADD: + case PO_DP3: + case PO_DP4: + case PO_MUL: // All these opcodes have two swappable parameters, so try that : + if (Intermediate[i].MoveRemovableParametersRight(0, 1)) + Result = true; + break; + + case PO_XMMA: + case PO_XMMC: + case PO_XDD: + if (Intermediate[i].XMoveNonRegisterOutputsRight()) + Result = true; + break; + + case PO_XDM: + { + // Parameters may be swapped for both dot and mul, + // but the opcodes themselves may not, as we handle + // both XDM operations separately below : + if (Intermediate[i].MoveRemovableParametersRight(0, 1)) + Result = true; + + if (Intermediate[i].MoveRemovableParametersRight(2, 3)) + Result = true; + break; + } + } + } + return Result; +} // MoveRemovableParametersRight + +//bool PSH_XBOX_SHADER::ConvertConstantsToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled) + + void PSH_XBOX_SHADER::_SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLOR ConstColor) + { + D3DXCOLOR XColor; + + // Colors are defined in RGBA format, and range 0.0 - 1.0 (negative values + // can be obtained by supplying PS_INPUTMAPPING_SIGNED_NEGATE to the combiner + // that reads from these constants). + XColor = ConstColor; + NewIns.Parameters[0].SetConstValue(XColor.r); + NewIns.Parameters[1].SetConstValue(XColor.g); + NewIns.Parameters[2].SetConstValue(XColor.b); + NewIns.Parameters[3].SetConstValue(XColor.a); + } + + void PSH_XBOX_SHADER::_SetColor(/*var OUT*/PSH_INTERMEDIATE_FORMAT &NewIns, D3DCOLORVALUE ConstColor) + { + NewIns.Parameters[0].SetConstValue(ConstColor.r); + NewIns.Parameters[1].SetConstValue(ConstColor.g); + NewIns.Parameters[2].SetConstValue(ConstColor.b); + NewIns.Parameters[3].SetConstValue(ConstColor.a); + } + + // Try to fixup constants above the limit (c7 for PS.1.3) : + int PSH_XBOX_SHADER::_MapConstant(int ConstNr, bool *NativeConstInUse) + { + // 1-to-1 mapping for constants that can be supported native (if not used already) : + if ((ConstNr < MaxConstantFloatRegisters) && (!NativeConstInUse[ConstNr])) + { + return ConstNr; + } + + // Assign not-yet-defined constants bottom-to-up : + int Result = 0; + while (Result < MaxConstantFloatRegisters) + { + if (!NativeConstInUse[Result]) + return Result; + + ++Result; + } + + // Unresolved - fallback to 1st constant : + if (Result >= MaxConstantFloatRegisters) + Result = 0; + + EmuLog(LOG_LEVEL::WARNING, "; Too many constants to emulate, this pixel shader will give unexpected output!"); + return Result; + } + + int PSH_XBOX_SHADER::_HandleConst(int XboxConst, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled, bool *NativeConstInUse, bool *EmittedNewConstant) + { + int NativeConst; + + if (!Recompiled->ConstInUse[XboxConst]) + { + // Determine and remember a new mapping to native : + NativeConst = _MapConstant(XboxConst, NativeConstInUse); + NativeConstInUse[NativeConst] = true; + Recompiled->ConstMapping[XboxConst] = NativeConst; + Recompiled->ConstInUse[XboxConst] = true; + // Make sure we can check this is a new constant (so we can emit a constant declaration + // for any final combiner constants - because those cannot be set via SetPixelShaderConstant) : + *EmittedNewConstant = true; + } + + // Return the (previously) determined mapping : + return Recompiled->ConstMapping[XboxConst]; + } + +bool PSH_XBOX_SHADER::ConvertConstantsToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef, /*var OUT*/PSH_RECOMPILED_SHADER *Recompiled) +{ + int i, j; + PPSH_INTERMEDIATE_FORMAT Cur; + PPSH_IMD_ARGUMENT CurArg; + bool NativeConstInUse[224]; // Note : 224 = highest possible MaxConstantFloatRegisters + int16_t OriginalConstantNr; + bool EmittedNewConstant = false; + PSH_INTERMEDIATE_FORMAT NewIns = {}; + + bool Result = false; + + // Note : Recompiled.ConstMapping and Recompiled.ConstInUse[i] are still empty here. + for (i = 0; i < MaxConstantFloatRegisters; i++) + NativeConstInUse[i] = false; + + Result = true; + + NewIns.Initialize(PO_DEF); + + // Add constants used to represent common powers of 2 used by instruction and argument modifiers + // Represent constant 0.0 and common powers of 2 divisions + NewIns.Output[0].SetRegister(PARAM_C, _HandleConst(PSH_XBOX_CONSTANT_MUL1, Recompiled, &NativeConstInUse[0], &EmittedNewConstant), MASK_RGBA); + _SetColor(NewIns, { 0.0, 1.0 / 2.0, 1.0 / 4.0, 1.0 / 8.0 }); + InsertIntermediate(&NewIns, 1); + + // Represent common powers of 2 constants, also used as multipliers + NewIns.Output[0].SetRegister(PARAM_C, _HandleConst(PSH_XBOX_CONSTANT_MUL0, Recompiled, &NativeConstInUse[0], &EmittedNewConstant), MASK_RGBA); + _SetColor(NewIns, {1.0, 2.0, 4.0, 8.0}); + InsertIntermediate(&NewIns, 1); + + for (i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) + { + _HandleConst(PSH_XBOX_CONSTANT_BEM + i, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + _HandleConst(PSH_XBOX_CONSTANT_LUM + i, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + } + + // Loop over all opcodes to update the constant-indexes (Xbox uses C0 and C1 in each combiner) : + for (i = 0; i < IntermediateCount; i++) + { + // Loop over this opcodes' input arguments : + Cur = &(Intermediate[i]); + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) + { + // Only handle arguments that address a constant register : + CurArg = &(Cur->Parameters[j]); + + // The Fog register is not supported on PC so we convert it to a constant too : + // (But only if the MASK is not solely accessing the alpha-channel - we don't support that) + if (CurArg->Type == PARAM_FOG) + { + if (CurArg->Mask != MASK_A) + { + CurArg->Type = PARAM_C; + CurArg->Address = _HandleConst(PSH_XBOX_CONSTANT_FOG, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + CurArg->Mask = CurArg->Mask & (!MASK_A); + } + else + { + // Until we can get Alpha fog from the vertex shader somehow, + // set it to a constant value, so these shaders (like appearing + // in Dolphin samples) still compile and give reasonable output : + CurArg->SetConstValue(1.0); + Cur->CommentString = "FOG.a not emulated, using 1."; + } + + continue; + } + + if (CurArg->Type != PARAM_C) + continue; + + // Make sure we can detect new constants (and if it was C0 or C1), + // as we need this for fixing up final combiner constants : + EmittedNewConstant = false; + OriginalConstantNr = CurArg->Address; + + // For each constant being addressed, we find out which Xbox constant it is, + // and map it to a native constant (as far as we have space for them) : + switch (CurArg->Address) { + case 0: // Handle C0 (if present) : + { + // The final combiner has a separate C0 constant : + if (Cur->CombinerStageNr == XFC_COMBINERSTAGENR) + CurArg->Address = _HandleConst(PSH_XBOX_CONSTANT_FC0, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + else + { + // See if C0 has a unique index per combiner stage : + if (CombinerHasUniqueC0) + // C0 actually ranges from c0 to c7, one for each possible combiner stage (X_D3DRS_PSCONSTANT0_0..X_D3DRS_PSCONSTANT0_7) : + CurArg->Address = _HandleConst(Cur->CombinerStageNr, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + else + // Non-unique just reads the same C0 in every stage : + CurArg->Address = _HandleConst(0, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + } + break; + } + + case 1: // Handle C1 (if present) : + { + // The final combiner has a separate C1 constant : + if (Cur->CombinerStageNr == XFC_COMBINERSTAGENR) + CurArg->Address = _HandleConst(PSH_XBOX_CONSTANT_FC1, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + else + { + // See if C1 has a unique index per combiner stage : + if (CombinerHasUniqueC1) + // C1 actually ranges from c8 to c15, one for each possible combiner stage (X_D3DRS_PSCONSTANT1_0..X_D3DRS_PSCONSTANT1_7) : + CurArg->Address = _HandleConst(Cur->CombinerStageNr + 8, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + else + // Non-unique just reads the same C1 in every stage : + CurArg->Address = _HandleConst(1, Recompiled, &NativeConstInUse[0], &EmittedNewConstant); + } + break; + } + } // switch + + // New constants solely used for the final combiner must be DEFined separately, + // as there's no other way to set these (SetPixelShaderConstant can only write + // to the 16 slots X_D3DRS_PSCONSTANT1_0..X_D3DRS_PSCONSTANT1_7) : + if ((Cur->CombinerStageNr == XFC_COMBINERSTAGENR) && EmittedNewConstant) + { + // Output a new opcode to define this constant : + NewIns.Initialize(PO_DEF); + NewIns.Output[0].SetRegister(PARAM_C, CurArg->Address, MASK_RGBA); + if (OriginalConstantNr == 0) + _SetColor(NewIns, pPSDef->PSFinalCombinerConstant0); + else + _SetColor(NewIns, pPSDef->PSFinalCombinerConstant1); + + // PO_DEF opcodes go after the initial PO_XPS (which is not yet replaced by PO_COMMENT+PO_PS, + // see ConvertXboxOpcodesToNative calling ConvertXPSToNative for that) + InsertIntermediate(&NewIns, 1); + Result = true; + } + } // for arguments + } // for opcodes + + return Result; +} // ConvertConstantsToNative + +bool PSH_XBOX_SHADER::RemoveUselessWrites() +// Note : Xbox allows writing to V0 (diffuse color) and V1 (specular color), but native ps.1.3 doesn't! +// Some examples of this behaviour can be seen when running RayMan Arena. +{ + int i, j; + PPSH_INTERMEDIATE_FORMAT Cur; + PPSH_IMD_ARGUMENT CurArg; + DWORD RegUsage[/*PSH_ARGUMENT_TYPE*/PARAM_C - PARAM_VALUE + 1][224] = {}; // 224 = highest possible PSH_PC_MAX_REGISTER_COUNT + + // TODO : In Polynomial Texture Maps, one extra opcode could be deleted (sub r1.rgb, v0,v0), why doesn't it? + bool Result = false; + + // Mark only R0 (and discard) as initially 'read', as these may not result in a removal : + RegUsage[PARAM_R][0] = MASK_RGBA; + for (i = 0; i < PSH_PC_MAX_REGISTER_COUNT; i++) + RegUsage[PARAM_DISCARD][i] = MASK_RGBA; + + i = IntermediateCount; + while (i > StartPos) + { + --i; + Cur = &(Intermediate[i]); + if (!Cur->IsArithmetic()) + continue; + + // Loop over the output arguments : + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) + { + CurArg = &(Cur->Output[j]); + + // Remove useless flag, to ease up later comparisions : + CurArg->Modifiers = CurArg->Modifiers & ~(1 << ARGMOD_IDENTITY); + + // Discard useless writes : + if ( (CurArg->Address < MaxTemporaryRegisters) + && ((RegUsage[CurArg->Type][CurArg->Address] & CurArg->Mask) == 0)) + { + EmuLog(LOG_LEVEL::DEBUG, "; Removed useless assignment to register %s", CurArg->ToString().c_str()); + CurArg->Type = PARAM_DISCARD; + Result = true; + } + } + + // Loop over the input arguments : + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) + { + CurArg = &(Cur->Parameters[j]); + // Skip non-register parameters : + if (!CurArg->UsesRegister()) + continue; + + // Remove useless flag, to ease up later comparisions : + CurArg->Modifiers = CurArg->Modifiers & ~(1 << ARGMOD_IDENTITY); + + // Keep track of all register reads, so that we can discard useless writes : + if (CurArg->Address < MaxTemporaryRegisters) + RegUsage[CurArg->Type][CurArg->Address] = RegUsage[CurArg->Type][CurArg->Address] | CurArg->Mask; + } + } + return Result; +} // RemoveUselessWrites + +void PSH_XBOX_SHADER::ConvertXboxOpcodesToNative(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + std::string CommentString; + + // Do a bottom-to-top pass, converting all xbox opcodes into a native set of opcodes : + i = IntermediateCount; + while (i > 0) + { + --i; + Cur = &(Intermediate[i]); + + // Convert all Xbox opcodes into native opcodes : + CommentString = Cur->ToString(); + switch (Cur->Opcode) { + case PO_XPS: ConvertXPSToNative(i); break; + case PO_XMMA: ConvertXMMAToNative(i); break; + case PO_XMMC: ConvertXMMCToNative(i); break; + case PO_XDM: ConvertXDMToNative(i); break; + case PO_XDD: ConvertXDDToNative(i); break; + case PO_XFC: ConvertXFCToNative(i); break; // Can only occur once, as the last instruction + default: + CommentString = ""; break; + } + + if (!CommentString.empty()) { + PSH_INTERMEDIATE_FORMAT NewIns = {}; + NewIns.Initialize(PO_COMMENT)->CommentString = CommentString; + InsertIntermediate(&NewIns, i); + } + } +} // ConvertXboxOpcodesToNative + +void PSH_XBOX_SHADER::ConvertXPSToNative(int i) +{ + PPSH_INTERMEDIATE_FORMAT Cur; + + Cur = &(Intermediate[i]); + Cur->Opcode = PO_PS; +} + +bool PSH_XBOX_SHADER::ConvertXMMToNative_Except3RdOutput(int i) +{ + PPSH_INTERMEDIATE_FORMAT Cur; + int InsertPos; + PSH_INTERMEDIATE_FORMAT Ins = {}; + + bool Result = false; + Cur = &(Intermediate[i]); + InsertPos = i; + + // This block is meant for cases where XMMA/XMMC discards the 3rd output : + if (Cur->Output[2].Type == PARAM_DISCARD) + { + // Mark that this XMMA/XMMC opcode is already handled here : + Result = true; + + // The opcode must unconditionally change into a MUL (or two) : + Cur->Opcode = PO_MUL; + + // Is the second output ignored? + if (Cur->Output[1].Type == PARAM_DISCARD) + { + // If the first output is also ignored : + if (Cur->Output[0].Type == PARAM_DISCARD) + // The complete opcode can already be removed early on : + DeleteIntermediate(i); + else + ;// The first output is just a MUL, it's output (and first two parameters) are already in-place, so we're done + + return Result; + } + ++InsertPos; + + // Create a second MUL opcode for the second result : + Ins = *Cur; + Ins.XCopySecondOpcodeToFirst(PO_MUL); + InsertIntermediate(&Ins, InsertPos); + return Result; + } + + // The third output is needed, but what about the first and second output ? + + if (Cur->Output[0].Type == PARAM_DISCARD) + { + Cur->Output[0].Type = PARAM_T; + Cur->Output[0].Address = FakeRegNr_Xmm1; // 'r4' + } + + if (Cur->Output[1].Type == PARAM_DISCARD) + { + Cur->Output[1].Type = PARAM_T; + Cur->Output[1].Address = FakeRegNr_Xmm2; // 'r5' + } + + // Generate a MUL for the 1st output : + Ins = *Cur; + Ins.Opcode = PO_MUL; + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + + // Generate a MUL for the 2nd output : + Ins = *Cur; + Ins.XCopySecondOpcodeToFirst(PO_MUL); + InsertIntermediate(&Ins, InsertPos); + + // Note : If XMMA or XMMC writes to the third argument, we now have + // the first and second stored already (if they where not ignored). + // IF one (or both) are ignored, the intermediate result might be + // needed, but let XMMA/XMMC figure that out first - the resulting + // opcode(s) will probably require the initial opcode's removal! + return Result; +} // ConvertXMMToNative_Except3RdOutput + +void PSH_XBOX_SHADER::ConvertXMMAToNative(int i) +{ + PPSH_INTERMEDIATE_FORMAT Cur; + + // Handle the generic case of XMM, and check if the 3rd (Add) argument is ignored : + if (!ConvertXMMToNative_Except3RdOutput(i)) + { + // Add needs to be stored, we already have 2 MULs, so change the XMMA into an ADD : + Cur = &(Intermediate[i+2]); + Cur->Opcode = PO_ADD; + Cur->Modifier = INSMOD_NONE; + Cur->Parameters[0] = Cur->Output[0]; + Cur->Parameters[1] = Cur->Output[1]; + Cur->Output[0] = Cur->Output[2]; + } +} + +void PSH_XBOX_SHADER::ConvertXMMCToNative(int i) +{ + PPSH_INTERMEDIATE_FORMAT Cur; + + // Handle the generic case of XMM, and check if the 3rd (Compare) argument is ignored : + if (!ConvertXMMToNative_Except3RdOutput(i)) + { + // Add needs to be stored, we already have 2 MULs, so change the XMMC into an CND : + Cur = &(Intermediate[i+2]); + // TODO : If CombinerMuxesOnMsb is False, we should compare to the LeastSignificantBit of r0.a - but how? + Cur->Opcode = PO_CND; + Cur->Modifier = INSMOD_NONE; + // Begin the input of CND with the required r0.a parameter : + Cur->Parameters[0].SetRegister(PARAM_R, 0, MASK_A); + Cur->Parameters[0].Modifiers = (1 << ARGMOD_IDENTITY); + Cur->Parameters[0].Multiplier = 1.0; + // Follow that with the 2 selection registers : + Cur->Parameters[1] = Cur->Output[0]; + Cur->Parameters[2] = Cur->Output[1]; + // And put the result it in the final register : + Cur->Output[0] = Cur->Output[2]; + } +} + +void PSH_XBOX_SHADER::ConvertXDMToNative(int i) +{ + PPSH_INTERMEDIATE_FORMAT Cur; + PSH_INTERMEDIATE_FORMAT Ins = {}; + + Cur = &(Intermediate[i]); + + // XDM does two operations : + + // a multiply : + if (Cur->Output[1].Type != PARAM_DISCARD) + { + Ins = *Cur; + Ins.XCopySecondOpcodeToFirst(PO_MUL); + InsertIntermediate(&Ins, i+1); + } + + // and a dot product : + if (Cur->Output[0].Type == PARAM_DISCARD) + DeleteIntermediate(i); + else + Cur->Opcode = PO_DP3; +} + +void PSH_XBOX_SHADER::ConvertXDDToNative(int i) +{ + PPSH_INTERMEDIATE_FORMAT Cur; + PSH_INTERMEDIATE_FORMAT Ins = {}; + + Cur = &(Intermediate[i]); + + // XDD does two operations : + + // ...a dot product : + Cur->Opcode = PO_DP3; + + // and another dot product : + if (Cur->Output[1].Type != PARAM_DISCARD) + { + Ins = *Cur; + Ins.XCopySecondOpcodeToFirst(PO_DP3); + InsertIntermediate(&Ins, i+1); + } +} + +void PSH_XBOX_SHADER::ConvertXFCToNative(int i) +{ + PSH_INTERMEDIATE_FORMAT Cur = {}; + int InsertPos; + bool NeedsProd; + bool NeedsSum; + PPSH_IMD_ARGUMENT CurArg; + PSH_INTERMEDIATE_FORMAT Ins = {}; + + // Get a copy of XFC and remove it already, new instructions will replace it : + Cur = Intermediate[i]; + DeleteIntermediate(i); + InsertPos = i; + // 'final combiner - r0 = A*B + (1-A)*C + D'; + + // See if the final combiner uses the prod or sum input parameters : + NeedsProd = false; + NeedsSum = false; + for (i = 0; i < PSH_OPCODE_DEFS[Cur.Opcode]._In; i++) + { + CurArg = &(Cur.Parameters[i]); + + // Check for the three final-combiner-specific argument types : + switch (CurArg->Type) { + case PARAM_V1R0_SUM: + { + // Change SUM into a fake register, which will be resolved later : + CurArg->Type = PARAM_T; + CurArg->Address = FakeRegNr_Sum; // 'r2' + NeedsSum = true; + break; + } + + case PARAM_EF_PROD: + { + // Change PROD into a fake register, which will be resolved later : + CurArg->Type = PARAM_T; + CurArg->Address = FakeRegNr_Prod; // 'r3' + NeedsProd = true; + break; + } + + case PARAM_FOG: + { + // Change FOG into a constant of 1.0, as we can't simulate it otherwise : +// CurArg->SetConstValue(1.0); +// Cur->CommentString = "final combiner - FOG not emulated, using 1."; + break; + } + } + } // for input + + if (NeedsSum) + { + // Add a new opcode that calculates r0*v1 : + Ins.Initialize(PO_MUL); + Ins.Output[0].SetRegister(PARAM_T, FakeRegNr_Sum, MASK_RGBA); // 'r2' + + Ins.Parameters[0].SetRegister(PARAM_R, 0, MASK_RGB); + Ins.Parameters[1].SetRegister(PARAM_V, 1, MASK_RGB); + + // Take the FinalCombinerFlags that influence this result into account : + if ((FinalCombinerFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0) > 0) + Ins.Parameters[0].Modifiers = (1 << ARGMOD_INVERT); // (1-r0) is used as an input to the sum rather than r0 + if ((FinalCombinerFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_V1) > 0) + Ins.Parameters[1].Modifiers = (1 << ARGMOD_INVERT); // (1-v1) is used as an input to the sum rather than v1 + if ((FinalCombinerFlags & PS_FINALCOMBINERSETTING_CLAMP_SUM) > 0) + Ins.Modifier = INSMOD_SAT; // V1+R0 sum clamped to [0,1] + + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted final combiner calculation of V1R0_sum register"); + } + + if (NeedsProd) + { + // Add a new opcode that calculates E*F : + Ins.Initialize(PO_MUL); + Ins.Output[0].SetRegister(PARAM_T, FakeRegNr_Prod, MASK_RGBA); // 'r3' + Ins.Parameters[0] = Cur.Parameters[4]; // E + Ins.Parameters[1] = Cur.Parameters[5]; // F + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted final combiner calculation of EF_prod register"); + } + + // The final combiner calculates : r0.rgb=s0*s1 + (1-s0)*s2 + s3 + // Change that into a LRP + ADD, and let the optimizer reduce it; + + // Add a new opcode that calculates r0.rgb=s0*s1 + (1-s0)*s2 via a LRP : + // Set the output to r0.rgb (as r0.a is determined via s6.a) : + + // Watch out! If s3=r0.rgb, then the LRP cannot use r0, but must use r1 as temp! + if (Cur.Parameters[3].IsRegister(PARAM_R, 0, 0)) + Cur.Output[0].SetRegister(PARAM_R, 1, MASK_RGB); + else + Cur.Output[0].SetRegister(PARAM_R, 0, MASK_RGB); + + Ins = Cur; + Ins.Opcode = PO_LRP; + Ins.Modifier = INSMOD_NONE; + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + + // Add a new opcode that calculates r0.rgb=r0.rgb+s3 : + Ins.Opcode = PO_ADD; + Ins.Modifier = Cur.Modifier; + Ins.Output[0] = Cur.Output[0]; // = r0.rgb + Ins.Parameters[0] = Cur.Output[0]; // = r0.rgb + Ins.Parameters[1] = Cur.Parameters[3]; // =s3 from XFC + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + + // See if s6 is something else than "r0.a" : + if (Cur.Parameters[6].ToString() != "r0.a") + { + // Add a new opcode that moves s6 over to r0.a : + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, 0, MASK_A); + Ins.Parameters[0] = Cur.Parameters[6]; + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + } +} + +bool PSH_XBOX_SHADER::RemoveNops() +{ + int i, j; + PPSH_INTERMEDIATE_FORMAT Cur; + bool HasOutput; + + bool Result = false; + i = IntermediateCount; + while (i > StartPos) + { + --i; + Cur = &(Intermediate[i]); + + // Skip opcodes that have no output, but should stay anyway : + if (PSH_OPCODE_DEFS[Cur->Opcode]._Out == 0) + if (Cur->Opcode != PO_NOP) + continue; + + // See if this opcode writes to any of it's outputs : + { + HasOutput = false; + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) + if (Cur->Output[j].Type != PARAM_DISCARD) + { + HasOutput = true; + break; + } + + if (!HasOutput) + { + // Remove the opcode (as it doesn't change anything) : + // This applies to PO_NOP and opcodes that discard all their results : + DeleteIntermediate(i); + Result = true; + continue; + } + } + } + return Result; +} + +int PSH_XBOX_SHADER::MaxRegisterCount(PSH_ARGUMENT_TYPE aRegType) +{ + switch (aRegType) + { + case PARAM_R: + return MaxTemporaryRegisters; + case PARAM_T: + return MaxTextureCoordinateRegisters; + case PARAM_V: + return MaxInputColorRegisters; + case PARAM_C: + return MaxConstantFloatRegisters; + case PARAM_S: + return MaxSamplerRegisters; + } + + return 0; +} + +bool PSH_XBOX_SHADER::IsValidNativeOutputRegister(PSH_ARGUMENT_TYPE aRegType, int index /*= -1*/) +{ + bool valid = (PARAM_R == aRegType) && (MaxRegisterCount(PARAM_R) > index); + + return valid; +} + +int PSH_XBOX_SHADER::RegisterIsFreeFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + + for (i = aIndex; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + // Detect a read : + if (Cur->ReadsFromRegister(aRegType, aAddress)) + { + return -1; + } + // Detect a write : + if (Cur->WritesToRegister(aRegType, aAddress)) + { + break; + } + } + + return i; +} + +int PSH_XBOX_SHADER::RegisterIsUsedFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) +{ + int result = -1; + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + + for (i = aIndex; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + // Detect a read : + if (Cur->ReadsFromRegister(aRegType, aAddress)) + { + result = i; + } + // Detect a write : + if (Cur->WritesToRegister(aRegType, aAddress)) + { + break; + } + } + + return result; +} + +int PSH_XBOX_SHADER::NextFreeRegisterFromIndexUntil(int aIndex, PSH_ARGUMENT_TYPE aRegType, int bIndex /*= -1*/, int startAddress /*= 0*/, int excludeAddress /*= -1*/) +{ + const int registerCount = MaxRegisterCount(aRegType); + + if (bIndex < 0 || bIndex < aIndex) + bIndex = IntermediateCount; + + if (startAddress < 0) + startAddress = 0; + + int i; + + for (i = startAddress; i < registerCount; i++) + { + if (i == excludeAddress) + continue; + + if (RegisterIsFreeFromIndexUntil(aIndex, aRegType, i) >= bIndex) + { + return i; + } + } + + return -1; +} + +bool PSH_XBOX_SHADER::IsRegisterFreeFromIndexOnwards(int aIndex, PSH_ARGUMENT_TYPE aRegType, int16_t aAddress) +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + + for (i = aIndex; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + // Detect a write or read : + if (Cur->WritesToRegister(aRegType, aAddress) + || Cur->ReadsFromRegister(aRegType, aAddress)) + { + return false; + } + } + + return true; +} + +void PSH_XBOX_SHADER::ReplaceInputRegisterFromIndexOnwards(int aIndex, + PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, + PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex /*= -1*/) +{ + ReplaceRegisterFromIndexOnwards(aIndex, aSrcRegType, aSrcAddress, aDstRegType, aDstAddress, endIndex, true, false); +} + +void PSH_XBOX_SHADER::ReplaceOutputRegisterFromIndexOnwards(int aIndex, + PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, + PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex /*= -1*/) +{ + ReplaceRegisterFromIndexOnwards(aIndex, aSrcRegType, aSrcAddress, aDstRegType, aDstAddress, endIndex, false, true); +} + +void PSH_XBOX_SHADER::ReplaceRegisterFromIndexOnwards(int aIndex, + PSH_ARGUMENT_TYPE aSrcRegType, int16_t aSrcAddress, + PSH_ARGUMENT_TYPE aDstRegType, int16_t aDstAddress, int endIndex /*= -1*/, bool replaceInput /*= true*/, bool replaceOutput /*= true*/) +{ + int i; + int j; + PPSH_INTERMEDIATE_FORMAT Cur; + + for (i = aIndex; i < IntermediateCount && (i <= endIndex || endIndex == -1); i++) + { + Cur = &(Intermediate[i]); + + if (replaceOutput) + { + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) + if (Cur->Output[j].IsRegister(aSrcRegType, aSrcAddress)) + Cur->Output[j].SetRegister(aDstRegType, aDstAddress, Cur->Output[j].Mask); + } + + if (replaceInput) + { + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) + if (Cur->Parameters[j].IsRegister(aSrcRegType, aSrcAddress)) + Cur->Parameters[j].SetRegister(aDstRegType, aDstAddress, Cur->Parameters[j].Mask); + } + } +} + +bool PSH_XBOX_SHADER::FixArgumentModifiers() +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + + bool Result = false; + + // Do a bottom-to-top pass, preventing constant-modifiers via additional MOV's: + i = IntermediateCount; + while (i > 0) + { + --i; + Cur = &(Intermediate[i]); + if (Cur->Opcode < PO_TEX) // TODO : Check explicitly which instruction types are handled below + continue; + + int InsertPos = i; + // Detect modifiers on constant and arguments + for (int p = 0; p < 7 && p < PSH_OPCODE_DEFS[Cur->Opcode]._In; p++) { + if ((Cur->Parameters[p].Type == PARAM_C || Cur->Parameters[p].UsesRegister()) + && ((Cur->Parameters[p].Modifiers & ~(1 << ARGMOD_NEGATE)) != 0)) { + + PSH_INTERMEDIATE_FORMAT Ins = {}; + PSH_IMD_ARGUMENT Arg = {}; + + Arg = Cur->Parameters[p]; + + int excludeAddress = Cur->Output[0].Type == PARAM_R ? Cur->Output[0].Address : -1; + + PSH_ARGUMENT_TYPE type = PARAM_R; + int address = NextFreeRegisterFromIndexUntil(InsertPos, PARAM_R, InsertPos, 0, excludeAddress); + + if (IsValidNativeOutputRegister(Arg.Type, Arg.Address) && RegisterIsFreeFromIndexUntil(InsertPos + 1, Arg.Type, Arg.Address) > InsertPos) + { + type = Arg.Type; + address = Arg.Address; + } + + for (int modifier = ARGMOD_INVERT; modifier < ARGMOD_SATURATE; ++modifier) + { + Arg = Cur->Parameters[p]; + + if (!Arg.HasModifier((PSH_ARG_MODIFIER)modifier)) + continue; + + bool needInsert = false; + switch ((PSH_ARG_MODIFIER)modifier) + { + case ARGMOD_INVERT: + { + if (Arg.HasModifier(ARGMOD_NEGATE)) + { + Ins.Initialize(PO_SUB); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[1].SetScaleConstRegister(1.0f, Recompiled); + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'invert' with 'negate' argument modifier (register - 1)"; + ++modifier; + } + else + { + Ins.Initialize(PO_SUB); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[0].SetScaleConstRegister(1.0f, Recompiled); + Ins.Parameters[1] = Cur->Parameters[p]; + Ins.Parameters[1].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'invert' argument modifier (1 - register)"; + } + needInsert = true; + + break; + } + case ARGMOD_NEGATE: + { + // Skip as this modifier is still supported in current shader models + // Included here for completeness + break; + Ins.Initialize(PO_MOV); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); + Ins.CommentString = "Inserted to replace 'negate' argument modifier (-register)"; + needInsert = true; + + break; + } + case ARGMOD_BIAS: + { + Ins.Initialize(PO_SUB); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'bias' argument modifier (register - 0.5)"; + needInsert = true; + + break; + } + case ARGMOD_SCALE_X2: + { + Ins.Initialize(PO_MUL); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'x2' argument modifier (2 * register)"; + needInsert = true; + + break; + } + case ARGMOD_SCALE_BX2: + { + Ins.Initialize(PO_MAD); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[2].SetScaleConstRegister(-1.0f, Recompiled); + Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'bx2' argument modifier (2 * register - 1)"; + needInsert = true; + + break; + } + case ARGMOD_SCALE_X4: + { + Ins.Initialize(PO_MUL); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[1].SetScaleConstRegister(4.0f, Recompiled); + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'x4' argument modifier (4 * register)"; + needInsert = true; + + break; + } + case ARGMOD_SCALE_D2: + { + Ins.Initialize(PO_MUL); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace 'd2' argument modifier (0.5 * register)"; + needInsert = true; + + break; + } + default: + { + Ins.Initialize(PO_MOV); + // No need to check if output is a constant - those cannot be assigned to anyway + Ins.Output[0].SetRegister(type, address, Arg.Mask); + // Move constant into register + Ins.Parameters[0] = Cur->Parameters[p]; + Ins.Parameters[0].Modifiers = 0; + Ins.CommentString = "Inserted to replace argument with modifier"; + needInsert = true; + + break; + } + } + + if (needInsert == true) + { + for (int q = p; q < PSH_OPCODE_DEFS[Cur->Opcode]._In; q++) + { + // overwrite all matching parameters to avoid duplicate instructions + if (Arg.Type == Cur->Parameters[q].Type + && Arg.Address == Cur->Parameters[q].Address + && Arg.Mask == Cur->Parameters[q].Mask + && Arg.Modifiers == Cur->Parameters[q].Modifiers + && Arg.Multiplier == Cur->Parameters[q].Multiplier) + { + Cur->Parameters[q] = Ins.Output[0]; + // Apply modifier to register instead of constant + Cur->Parameters[q].Modifiers = (Arg.Modifiers & (1 << ARGMOD_NEGATE)) | (Arg.Modifiers & (~0 << (modifier + 1))); + } + } + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + ++Cur; + EmuLog(LOG_LEVEL::DEBUG, "; Used intermediate move to avoid argument modifier"); + Result = true; + } + } + } + } + } + return Result; +} // FixArgumentModifiers + +bool PSH_XBOX_SHADER::FixConstantParameters() +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + + bool Result = false; + + // Do a bottom-to-top pass, preventing constant-modifiers via additional MOV's: + i = IntermediateCount; + while (i > StartPos) + { + --i; + Cur = &(Intermediate[i]); + + if (!Cur->IsArithmetic()) + continue; + + for (int p = 0; p < PSH_OPCODE_DEFS[Cur->Opcode]._In; ++p) + { + if (Cur->Parameters[p].Type != PARAM_VALUE) + continue; + + if (Cur->Parameters[p].SetScaleConstRegister(Cur->Parameters[p].GetConstValue(), Recompiled)) + { + EmuLog(LOG_LEVEL::DEBUG, "; Replaced constant value with constant register"); + Result = true; + } + } + } + return Result; +} // FixConstantParameters + +bool PSH_XBOX_SHADER::FixInstructionModifiers() +{ + int i; + int InsertPos; + PPSH_INTERMEDIATE_FORMAT Cur; + PSH_INTERMEDIATE_FORMAT Ins = {}; + + bool Result = false; + + // Do a bottom-to-top pass, preventing constant-modifiers via additional MOV's: + i = IntermediateCount; + while (i > StartPos) + { + InsertPos = i; + --i; + Cur = &(Intermediate[i]); + + if (!Cur->IsArithmetic()) + continue; + + bool insert = true; + switch (Cur->Modifier) + { + case INSMOD_BIAS: // y = x - 0.5 // Xbox only : TODO : Fixup occurrances! + { + Ins.Initialize(PO_SUB); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_bias"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_bias"); + break; + } + case INSMOD_X2: // y = x * 2 + { + Ins.Initialize(PO_MUL); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_x2"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_x2"); + break; + } + case INSMOD_BX2: // y = (x - 0.5) * 2 // Xbox only : TODO : Fixup occurrances! + { + Ins.Initialize(PO_MAD); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(2.0f, Recompiled); + Ins.Parameters[2].SetScaleConstRegister(-1.0f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_bx2"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_bx2"); + break; + } + case INSMOD_X4: // y = x * 4 + { + Ins.Initialize(PO_MUL); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(4.0f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_x4"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_x4"); + break; + } + case INSMOD_D2: // y = x * 0.5 + { + Ins.Initialize(PO_MUL); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_d2"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_d2"); + break; + } + case INSMOD_X8: // y = x * 8 // ps 1.4 only + { + Ins.Initialize(PO_MUL); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(8.0f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_x8"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_x8"); + break; + } + case INSMOD_D4: // y = x * 0.25 // ps 1.4 only + { + Ins.Initialize(PO_MUL); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(0.25f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_d4"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_d4"); + break; + } + case INSMOD_D8: // y = x * 0.125 // ps 1.4 only + { + Ins.Initialize(PO_MUL); + Ins.Output[0] = Ins.Parameters[0] = Cur->Output[0]; + Ins.Parameters[1].SetScaleConstRegister(0.125f, Recompiled); + Ins.CommentString = "; Inserted adjustment by constant register for INST_d8"; + EmuLog(LOG_LEVEL::DEBUG, "; Inserted adjustment by constant register for INST_d8"); + break; + } + case INSMOD_SAT: // Xbox doesn"t support this, but has ARGMOD_SATURATE instead + case INSMOD_NONE: // y = x + default: + insert = false; + break; + } + + if (insert) + { + Cur->Modifier = INSMOD_NONE; + InsertIntermediate(&Ins, InsertPos); + Result = true; + } + } + return Result; +} // FixInstructionModifiers + +bool PSH_XBOX_SHADER::FinalizeShader() +{ + PSH_INTERMEDIATE_FORMAT Ins = {}; + + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_oC, 0, MASK_RGBA); + Ins.Parameters[0].SetRegister(PARAM_R, 0, MASK_RGBA); + InsertIntermediate(&Ins, IntermediateCount); + + return true; +} // FinalizeShader + +//bool PSH_XBOX_SHADER::CombineInstructions() + + bool _CanLerp(PPSH_INTERMEDIATE_FORMAT Mul1, PPSH_INTERMEDIATE_FORMAT Mul2, PPSH_INTERMEDIATE_FORMAT AddOpcode, int Left, int Right) + { + PPSH_IMD_ARGUMENT ParamLeft, ParamRight; + + // Check if Left and Right are the same register : + ParamLeft = &(Mul1->Parameters[Left]); + ParamRight = &(Mul2->Parameters[Right]); + if ((ParamLeft->Type != ParamRight->Type) + || (ParamLeft->Address != ParamRight->Address) + || (ParamLeft->Mask != ParamRight->Mask)) + return false; + + // Is the left argument inverted and the right not (or the other way around) ? + if (ParamLeft->HasModifier(ARGMOD_INVERT) != ParamRight->HasModifier(ARGMOD_INVERT)) + { + // In that case, already move the arguments over to AddOpcode so we create a LRP : + AddOpcode->Parameters[0] = *ParamLeft; + AddOpcode->Parameters[1] = Mul1->Parameters[1-Left]; + AddOpcode->Parameters[2] = Mul2->Parameters[3-Right]; + return true; + } + return false; + } + + bool _CanMad(int ConstOne, PPSH_INTERMEDIATE_FORMAT Mul1, PPSH_INTERMEDIATE_FORMAT Mul2, PPSH_INTERMEDIATE_FORMAT AddOpcode) + { + // Check if the given parameter is 1 : + bool Result = Mul1->Parameters[ConstOne].GetConstValue() == 1.0; + if (Result) + { + // Put the other 3 parameters int the resulting opcode, so we can make it a MAD : + AddOpcode->Parameters[0] = Mul2->Parameters[0]; + AddOpcode->Parameters[1] = Mul2->Parameters[1]; + AddOpcode->Parameters[2] = Mul1->Parameters[1-ConstOne]; + } + return Result; + } + +bool PSH_XBOX_SHADER::CombineInstructions() +{ + int i; + PPSH_INTERMEDIATE_FORMAT Op0; + PPSH_INTERMEDIATE_FORMAT Op1; + PPSH_INTERMEDIATE_FORMAT Op2; + bool CanOptimize; + int j; + int k; + + bool Result = false; + + i = IntermediateCount - 1; + while (i > StartPos) + { + --i; + Op0 = &(Intermediate[i+0]); + Op1 = &(Intermediate[i+1]); + Op2 = &(Intermediate[i+2]); + + // Check if there are two consecutive opcodes reading from a fake R register; + // We outputted these ourselves, in order to ease the conversion and profit + // from having generic optimizations in one place : + if ( (Op0->Output[0].Type == PARAM_T) + && (Op0->Output[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT) + && (Op1->Output[0].Type == PARAM_T) + && (Op1->Output[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT)) + { + // Did we output those from a CND opcode (originally XMMC) ? + if (Op2->Opcode == PO_CND) + { + if ( (Op0->Opcode == PO_MOV) + && (Op1->Opcode == PO_MOV) + && (Op1->Modifier == Op0->Modifier)) + { + Op2->Modifier = Op0->Modifier; + Op2->Parameters[1] = Op0->Parameters[0]; + Op2->Parameters[2] = Op1->Parameters[0]; + DeleteIntermediate(i); + DeleteIntermediate(i); + EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,CND via MOV,MOV,CND into a single CND"); + Result = true; + continue; + } + } + + // Did we output those from a ADD opcode (originally XMMA) ? + if (Op2->Opcode == PO_ADD) + { + if ( (Op0->Opcode == PO_MUL) + && (Op1->Opcode == PO_MUL) + && (Op1->Modifier == Op0->Modifier)) + { + // Check if we can lerp - we just need the same register on both sides that's inverted on the other : + if (_CanLerp(Op0, Op1, Op2, 0, 2) + || _CanLerp(Op0, Op1, Op2, 1, 2) + || _CanLerp(Op0, Op1, Op2, 0, 3) + || _CanLerp(Op0, Op1, Op2, 1, 3)) + { + // The lerp can be done, and the correct parameters are already set to Op2, + // so all we need to do now, it fixup the rest and remove the two MOV's : + Op2->Opcode = PO_LRP; + Op2->Modifier = Op0->Modifier; + DeleteIntermediate(i); + DeleteIntermediate(i); + EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a single LRP"); + Result = true; + continue; + } + + // Check if we can mad - we just need a constant 1 in one argument : + if (_CanMad(0, Op0, Op1, Op2) + || _CanMad(1, Op0, Op1, Op2) + || _CanMad(0, Op1, Op0, Op2) + || _CanMad(1, Op1, Op0, Op2)) + { + // The mad can be done, and the correct parameters are already set to Op2, + // so all we need to do now, it fixup the rest and remove the two MOV's : + Op2->Opcode = PO_MAD; + Op2->Modifier = Op0->Modifier; + DeleteIntermediate(i); + DeleteIntermediate(i); + EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a single MAD"); + Result = true; + continue; + } + + // No single opcode possible, so change it into a MUL + MAD : + // The first mul may write to the last output register (without a modifier) : + Op0->Modifier = INSMOD_NONE; + Op0->Output[0] = Op2->Output[0]; + // Change the second MUL into a MAD : + Op1->Opcode = PO_MAD; + Op1->Output[0] = Op2->Output[0]; + Op1->Parameters[2] = Op0->Output[0]; + // Remove the trailing ADD : + DeleteIntermediate(i+2); + EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a MUL,MAD"); + Result = true; + continue; + } + + // Was it a MUL,MUL,ADD? + if ( (Op0->Opcode == PO_MUL) + && (Op1->Opcode == PO_MUL) + && (Op0->Parameters[1].GetConstValue() == 1.0) + && (Op1->Parameters[1].GetConstValue() == 1.0)) + { + // Remove the two MOV's and fold their arguments into a MUL : + Op2->Opcode = PO_MUL; + Op2->Parameters[0] = Op0->Parameters[0]; + Op2->Parameters[1] = Op1->Parameters[0]; + DeleteIntermediate(i); + DeleteIntermediate(i); + EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MUL,ADD into a MUL"); + Result = true; + continue; + } + } + } + + // Do two neighbouring opcodes output to the same register (without a modifier) ? + if ( (Op0->Output[0].ToString() == Op1->Output[0].ToString()) + && (Op0->Modifier == INSMOD_NONE) + && (Op1->Modifier == INSMOD_NONE)) + { + // Is it MUL,ADD ? + if ( (Op0->Opcode == PO_MUL) + && (Op1->Opcode == PO_ADD)) + { + // Is the output of the MUL input to the ADD ? + if ( (Op0->Output[0].Type == Op1->Parameters[0].Type) + && (Op0->Output[0].Address == Op1->Parameters[0].Address) + && (Op0->Output[0].Modifiers == Op1->Parameters[0].Modifiers)) + // Mask and Multiplier are not important here + { + Op0->Opcode = PO_MAD; + Op0->Parameters[2] = Op1->Parameters[1]; + DeleteIntermediate(i+1); + EmuLog(LOG_LEVEL::DEBUG, "; Changed MUL,ADD into a single MAD"); + Result = true; + continue; + } + } + } + +/* + // Combinations that can be made if their intermediate result is not read again or overwritten later: + + MOV+ADD > ADD (if MOV.Output[0] was only read by ADD.Parameter[0] or ADD.Parameter[1]) + MOV+SUB > SUB (if MOV.Output[0] was only read by SUB.Parameter[0] or SUB.Parameter[1]) + MOV+MUL > MUL (if MOV.Output[0] was only read by MOV.Parameter[0] or MOV.Parameter[1]) + + MUL+MOV > MUL (if MUL.Output[0] was only read by MOV.Parameter[0]) + MUL+ADD > MAD (if MUL.Output[0] was only read by ADD.Parameter[0] or ADD.Parameter[1]) + MUL+SUB > MAD (if MUL.Output[0] was only read by SUB.Parameter[0] - Do invert MAD.Parameter[2]) +*/ + + // We can remove a MOV entirely if the input is not changed while + // the output is read, up until the output is re-written; We can change all + // these occurances into a read from the input of this MOV instead : + // This fixes some shaders in Turok, that are reduced to 8 instead of 9 opcodes. + if ( (Op0->Opcode == PO_MOV) + && (Op0->Modifier == INSMOD_NONE) + && (Op0->Output[0].Mask == MASK_RGBA)) + { + CanOptimize = false; + j = i + 1; + while (j < IntermediateCount) + { + // Don't optimize if the output is needed for CND or CMP (which must read from r0) : + // This fixes : "(Validation Error) First source for cnd instruction must be 'r0.a'" in Modify Pixel Shader XDK sample. + if ( ((Intermediate[j].Opcode == PO_CND) || (Intermediate[j].Opcode == PO_CMP)) + && (Op0->Output[0].IsRegister(PARAM_R, 0))) + break; + + // TODO : Add other prevention rules here (like too many texture-reads, and other scases) + + // We can optimize if the MOV-output is written to again before the end of the shader : + CanOptimize = true; + + // ensure this is not "constant with modifier" optimization pattern to prevent infinite loop + for (int p = 0; p < PSH_OPCODE_DEFS[Intermediate[j].Opcode]._In; p++) + { + if ((Op0->Parameters[0].Type == PARAM_C) + && (Intermediate[j].Parameters[p].Type == Op0->Output[0].Type) + && (Intermediate[j].Parameters[p].Address == Op0->Output[0].Address) + && (Intermediate[j].Parameters[p].Modifiers != 0)) + { + CanOptimize = false; + break; + } + }; + + if (Intermediate[j].WritesToRegister(Op0->Output[0].Type, Op0->Output[0].Address, MASK_RGBA)) + break; + + CanOptimize = false; + ++j; + } + + if (CanOptimize) + { + // Loop over all instructions in between, and try to replace reads : + CanOptimize = false; + while (j > i) + { + // For Intermediate[j].Parameters, change all occurrances of Op0.Output[0] into Op0.Parameters[0] : + for (k = 0; k < PSH_OPCODE_DEFS[Intermediate[j].Opcode]._In; k++) + if ( (Intermediate[j].Parameters[k].Type == Op0->Output[0].Type) + && (Intermediate[j].Parameters[k].Address == Op0->Output[0].Address)) + { + Intermediate[j].Parameters[k].Type = Op0->Parameters[0].Type; + Intermediate[j].Parameters[k].Address = Op0->Parameters[0].Address; + // Signal that a replacement is actually done : + CanOptimize = true; + } + + --j; + } + + if (CanOptimize) + { + DeleteIntermediate(i); + EmuLog(LOG_LEVEL::DEBUG, "; Moved MOV input into following instructions"); + Result = true; + } + } + } + + // Fix Dolphin : + // mul r3, r0,t0 ; d0=s0*s1 + // mov r0.rgb, r3 ; d0=s0 final combiner - FOG not emulated, using 1. + if ( (Op0->Output[0].Type == PARAM_T) + && (Op0->Output[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT) + && (Op1->Parameters[0].Type == PARAM_T) + && (Op1->Parameters[0].Address >= PSH_XBOX_MAX_T_REGISTER_COUNT)) + { + if ( (Op0->Opcode == PO_MUL) + && (Op1->Opcode == PO_MOV)) + { + // > mul r0.rgb, r0,t0 + Op0->Output[0] = Op1->Output[0]; + DeleteIntermediate(i+1); + EmuLog(LOG_LEVEL::DEBUG, "; Changed temporary MUL,MOV into a MUL"); + Result = true; + continue; + } + } + + // Fix Crash bandicoot xfc leftover r3 : + if (Op0->Output[0].IsRegister(PARAM_T, FakeRegNr_Prod)) // 'r3' + { + // The final combiner uses r3, try to use r1 instead : + if (IsRegisterFreeFromIndexOnwards(i, PARAM_R, 1)) + { + ReplaceRegisterFromIndexOnwards(i, Op0->Output[0].Type, Op0->Output[0].Address, PARAM_R, 1); + EmuLog(LOG_LEVEL::DEBUG, "; Changed fake register by r1"); + Result = true; + continue; + } + } + } // while + return Result; +} // CombineInstructions + +bool PSH_XBOX_SHADER::SimplifyMOV(PPSH_INTERMEDIATE_FORMAT Cur) +{ + bool CanSimplify; + float Factor; + + // NOP-out MOV's that read and write to the same register : + if ( (Cur->Output[0].Type == Cur->Parameters[0].Type) + && (Cur->Output[0].Address == Cur->Parameters[0].Address) + && (Cur->Output[0].Mask == Cur->Parameters[0].Mask)) + { + if (Cur->Output[0].Type == PARAM_VALUE) + CanSimplify = Cur->Output[0].GetConstValue() == Cur->Parameters[0].GetConstValue(); + else + CanSimplify = (Cur->Output[0].Modifiers == Cur->Parameters[0].Modifiers) + && (Cur->Output[0].Multiplier == Cur->Parameters[0].Multiplier); + + if (CanSimplify) + { + Cur->Opcode = PO_NOP; // This nop will be removed in a recursive fixup + EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV into a NOP"); + return true; + } + } + + // Does this MOV put a 0 (zero) in the output? + if (Cur->Parameters[0].GetConstValue() == 0.0) + { + // Attempt to find a constant with the value 0, and use that if present. + if (!Cur->Parameters[0].SetScaleConstRegister(0.0f, Recompiled)) + { + // Simulate 0 by subtracting a (guaranteed) register from itself : + // Fixup via "sub d0=v0,v0" : + Cur->Opcode = PO_SUB; + Cur->Parameters[0].Type = PARAM_V; + Cur->Parameters[0].Address = 0; + Cur->Parameters[0].Modifiers = 0; + Cur->Parameters[1] = Cur->Parameters[0]; + EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV 0 into a SUB v0,v0"); + } + else + { + EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV 0 into a MOV c0"); + } + + return true; + } + + // Does this MOV put a constant in the output? + if (Cur->Parameters[0].Type == PARAM_VALUE) + { + // TODO : If there's a constant equal to GetConstValue(), use that. + Factor = Cur->Parameters[0].GetConstValue(); + + if (!Cur->Parameters[0].SetScaleConstRegister(Factor, Recompiled)) + { + // Fixup via a SUB (which can calculate a constant value) : + Cur->Opcode = PO_SUB; + Cur->Parameters[0].Type = PARAM_V; + Cur->Parameters[0].Address = 0; + + if (Factor < 0.0) + { + // Simulate -1 by calculating it via a (guaranteed) register : + // We follow this : (-v0) - (1-v0) = -v0 - 1 + v0 = -1 + Cur->Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); + Cur->Parameters[1] = Cur->Parameters[0]; + Cur->Parameters[1].Modifiers = (1 << ARGMOD_INVERT); + // Go on with a positive factor, to ease the scaling : + Factor = -Factor; + } + else + { + // Simulate 1 by calculating it via a (guaranteed) register : + // We follow this : (1-v0) - (-v0) = (1-v0) + v0 = 1 + Cur->Parameters[0].Modifiers = (1 << ARGMOD_INVERT); + Cur->Parameters[1] = Cur->Parameters[0]; + Cur->Parameters[1].Modifiers = (1 << ARGMOD_NEGATE); + } + + // Try to simulate all factors (0.5, 1.0 and 2.0) using an output modifier : + Cur->ScaleOutput(Factor); + + EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV {const} into a SUB_factor 1-v0,-v0"); + } + else + { + EmuLog(LOG_LEVEL::DEBUG, "; Changed MOV {const} into a MOV c#"); + } + return true; + } + return false; +} + +bool PSH_XBOX_SHADER::SimplifyADD(PPSH_INTERMEDIATE_FORMAT Cur) +{ + // Is this an addition of s0+0 ? + if (Cur->Parameters[1].GetConstValue() == 0.0) + { + // Change it into a MOV (the first argument is already in-place) + Cur->Opcode = PO_MOV; + EmuLog(LOG_LEVEL::DEBUG, "; Changed ADD s0,0 into a MOV s0"); + return true; + } + return false; +} + +bool PSH_XBOX_SHADER::SimplifyMAD(PPSH_INTERMEDIATE_FORMAT Cur, int index) +{ + // Is this 0*s1+s2 or s0*0+s2 ? + if (Cur->Parameters[0].GetConstValue() == 0.0 + || Cur->Parameters[1].GetConstValue() == 0.0) + { + // Change it into s2 : + Cur->Opcode = PO_MOV; + Cur->Parameters[0] = Cur->Parameters[2]; + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,0 into a MOV s0"); + return true; + } + + // Is this s0*s1+0 ? + if (Cur->Parameters[2].GetConstValue() == 0.0) + { + // Change it into s0*s1 : + Cur->Opcode = PO_MUL; + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0, s1,0 into a MUL s0, s1"); + return true; + } + + // Is this s0*1+s2 ? + if (Cur->Parameters[1].GetConstValue() == 1.0) + { + // Change it into s0+s2 : + Cur->Opcode = PO_ADD; + Cur->Parameters[1] = Cur->Parameters[2]; + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,1,s2 into a ADD s0,s2"); + return true; + } + + // Is this s0*-1+s2 ? + if (Cur->Parameters[1].GetConstValue() == -1.0) + { + // Change it into s2-s0 : + Cur->Opcode = PO_SUB; + Cur->Parameters[1] = Cur->Parameters[0]; + Cur->Parameters[0] = Cur->Parameters[2]; + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,-1,s2 into a SUB s2,s0"); + return true; + } + + PSH_INTERMEDIATE_FORMAT Ins = {}; + + // Is this 0.5*s1+s2 ? + if (Cur->Parameters[0].GetConstValue() == 0.5f && Cur->Parameters[1].UsesRegister()) + { + if (!Cur->Parameters[0].SetScaleConstRegister(0.5f, Recompiled)) + { + // Change it into s2 : + Cur->Opcode = PO_ADD; + Cur->Parameters[0] = Cur->Parameters[1]; + Cur->Parameters[1] = Cur->Parameters[2]; + + Ins.Initialize(PO_MOV); + Ins.Modifier = INSMOD_D2; + Ins.Output[0] = Ins.Parameters[0] = Cur->Parameters[1]; + Ins.CommentString = "; Inserted to perform division by 2"; + InsertIntermediate(&Ins, index); + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD 0.5,s1,s2 into a MOV_d2 s1, s1 ADD s1, s2"); + } + else + { + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD 0.5,s1,s2 into a MAD c#,s1,s2"); + } + return true; + } + + // Is this s0*0.5+s2 ? + if (Cur->Parameters[1].GetConstValue() == 0.5f && Cur->Parameters[0].UsesRegister()) + { + if (!Cur->Parameters[1].SetScaleConstRegister(0.5f, Recompiled)) + { + // Change it into s2 : + Cur->Opcode = PO_ADD; + Cur->Parameters[0] = Cur->Parameters[0]; + Cur->Parameters[1] = Cur->Parameters[2]; + + Ins.Initialize(PO_MOV); + Ins.Modifier = INSMOD_D2; + Ins.Output[0] = Ins.Parameters[0] = Cur->Parameters[0]; + Ins.CommentString = "; Inserted to perform division by 2"; + InsertIntermediate(&Ins, index); + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,0.5,s2 into a MOV_d2 s0, s0 ADD s0, s2"); + } + else + { + EmuLog(LOG_LEVEL::DEBUG, "; Changed MAD s0,0.5,s2 into a MAD s0,c#,s2"); + } + return true; + } + return false; +} + +bool PSH_XBOX_SHADER::SimplifySUB(PPSH_INTERMEDIATE_FORMAT Cur) +{ + // Is this an subtraction of s0-0 ? + if (Cur->Parameters[1].GetConstValue() == 0.0) + { + // Change it into a MOV (the first argument is already in-place) + Cur->Opcode = PO_MOV; + EmuLog(LOG_LEVEL::DEBUG, "; Changed SUB x, 0 into a MOV x"); + return true; + } + return false; +} + +bool PSH_XBOX_SHADER::SimplifyMUL(PPSH_INTERMEDIATE_FORMAT Cur) +{ + // Is the result of this multiplication zero ? + if (Cur->Parameters[1].GetConstValue() == 0.0) + { + // Change it into a MOV (the 0 argument will be resolve in a recursive MOV fixup) : + Cur->Opcode = PO_MOV; + Cur->Parameters[0].SetConstValue(0.0); + EmuLog(LOG_LEVEL::DEBUG, "; Changed MUL s0,0 into a MOV 0"); + return true; + } + + // Is this a multiply-by-const ? + if (Cur->Parameters[1].Type == PARAM_VALUE) + { + // Change it into a simple MOV and scale the output instead : + Cur->Opcode = PO_MOV; + Cur->ScaleOutput(Cur->Parameters[1].GetConstValue()); + EmuLog(LOG_LEVEL::DEBUG, "; Changed MUL s0,{const} into a MOV_factor s0"); + return true; + } + return false; +} // SimplifyMUL + +bool PSH_XBOX_SHADER::SimplifyLRP(PPSH_INTERMEDIATE_FORMAT Cur, int index) +{ + // LRP calculates : d0=s0*s1+(1-s0)*s2 which can also be read as : d0=s0*(s1-s2)+s2 + + // Is the right part ((1-s0)*s2) zero? + if ((Cur->Parameters[0].GetConstValue() == 1.0) || (Cur->Parameters[2].GetConstValue() == 0.0)) + { + // Change it into a MUL (calculating the left part : s0*s1 : + Cur->Opcode = PO_MUL; + EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,s1,s2 (where (1-s0)*s2=0) into a MUL s0,s1"); + return true; + } + + // Is the left part (s0*s1) zero? + if ((Cur->Parameters[0].GetConstValue() == 0.0) || (Cur->Parameters[1].GetConstValue() == 0.0)) + { + // Change it into a MUL (calculating the right part : (1-s0)*s2) : + Cur->Opcode = PO_MUL; + Cur->Parameters[0].Invert(); + Cur->Parameters[1] = Cur->Parameters[2]; + EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,s1,s2 (where s0*s1=0) into a MUL (1-s0),s2"); + return true; + } + + // Is it d0=s0*s1+(1-s0)*1 ? + if (Cur->Parameters[2].GetConstValue() == 1.0) + { + // Change it into a d0=s0*s1+(1-s0) + Cur->Opcode = PO_MAD; + Cur->Parameters[2] = Cur->Parameters[0]; + Cur->Parameters[2].Invert(); + EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,s1,1 into a MAD s0,s1,1-s0"); + return true; + } + + // Is it d0=s0*(1-s2)+s2 ? + if (Cur->Parameters[1].GetConstValue() == 1.0) + { + // Change it into a d0=s0*(1-s2)+s2 + Cur->Opcode = PO_MAD; + Cur->Parameters[1] = Cur->Parameters[2]; + Cur->Parameters[1].Invert(); + EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,1,s2 into a MAD s0,1-s2,s2"); + return true; + } + + int output = NextFreeRegisterFromIndexUntil(index, PARAM_R, index, 0, Cur->Output[0].Address); + + if (output >= 0) + { + bool insert = false; + for (int p = 0; p < PSH_OPCODE_DEFS[Cur->Opcode]._In; ++p) + { + if (Cur->Output[0].Type == Cur->Parameters[p].Type + && Cur->Output[0].Address == Cur->Parameters[p].Address) + { + insert = true; + Cur->Parameters[p].Address = output; + } + } + if (insert) + { + PSH_INTERMEDIATE_FORMAT Ins = {}; + + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(PARAM_R, output, 0); + Ins.Parameters[0].SetRegister(Cur->Output[0].Type, Cur->Output[0].Address, 0); + Ins.CommentString = "; Inserted to avoid LRP parameters referencing the output register"; + InsertIntermediate(&Ins, index); + EmuLog(LOG_LEVEL::DEBUG, "; Changed LRP s0,1,s2 into a MAD s0,1-s2,s2"); + return true; + } + } + + return false; +} // SimplifyLRP + +bool PSH_XBOX_SHADER::FixupCND(PPSH_INTERMEDIATE_FORMAT Cur, int index) +{ + PSH_INTERMEDIATE_FORMAT Ins = {}; + + // TODO: Look into using predicate register + Cur->Opcode = PO_CMP; + + int output = NextFreeRegisterFromIndexUntil(index, PARAM_R, index); + Ins.Initialize(PO_SUB); + Ins.Output[0].SetRegister(PARAM_R, output, Cur->Parameters[0].Mask); + Ins.Parameters[0] = Cur->Parameters[0]; + Ins.Parameters[1].SetScaleConstRegister(0.5f, Recompiled); + Cur->Parameters[0] = Ins.Output[0]; + Cur->Parameters[0].Modifiers = (1 << ARGMOD_NEGATE); + std::swap(Cur->Parameters[1], Cur->Parameters[2]); + Ins.CommentString = Cur->CommentString = "; Changed CND into SUB CMP"; + InsertIntermediate(&Ins, index); + EmuLog(LOG_LEVEL::DEBUG, "; Changed CND into SUB CMP"); + return true; +} + +bool PSH_XBOX_SHADER::FixupPixelShader() +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + + bool Result = RemoveNops(); + + // TODO : Fixup writes to read-only registers (V0, V1) via another free register (if possible) + // TODO : Fixup the usage of non-existent register numbers (like FakeRegNr_Sum and FakeRegNr_Prod) + // TODO : Fixup the usage of the unsupported INSMOD_BIAS and INSMOD_BX2 instruction modifiers + // TODO : Use the INSMOD_SAT instruction modifier instead of the ARGMOD_SATURATE argument modifier + // TODO : Condense constants registers, to avoid the non-existant C8-C15 (requires a mapping in SetPixelShaderConstant too...) + // TODO : Convert numeric arguments (-2, -1, 0, 1, 2) into modifiers on the other argument + // TODO : Complete to port to D3D9 to support all 18 constants (including C8..C15 + FC0+FC1) + + if (CombineInstructions()) + Result = true; + + if (MoveRemovableParametersRight()) + Result = true; + + // Simplify instructions, which can help to compress the result : + i = IntermediateCount; + while (i > StartPos) + { + --i; + Cur = &(Intermediate[i]); + + switch (Cur->Opcode) { + case PO_MOV: + if (SimplifyMOV(Cur)) + Result = true; + break; + + case PO_ADD: + if (SimplifyADD(Cur)) + Result = true; + break; + + case PO_MAD: + if (SimplifyMAD(Cur, i)) + Result = true; + break; + + case PO_SUB: + if (SimplifySUB(Cur)) + Result = true; + break; + + case PO_MUL: + if (SimplifyMUL(Cur)) + Result = true; + break; + + case PO_LRP: + if (SimplifyLRP(Cur, i)) + Result = true; + break; + + case PO_CND: + if (FixupCND(Cur, i)) + Result = true; + break; + } // case + } // for + + // If the above code made any alteration, repeat it as some changes require a followup (like MUL>MOV>NOP) : + if (Result) + { + Log("Fixup intermediate result"); + FixupPixelShader(); + } + return Result; +} // FixupPixelShader + +bool PSH_XBOX_SHADER::FixInvalidSrcSwizzle() +{ + int i, j; + PPSH_INTERMEDIATE_FORMAT Cur; + PPSH_IMD_ARGUMENT CurArg; + + bool Result = false; + for (i = StartPos; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + if (Cur->IsArithmetic()) + { + // Loop over the input arguments : + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._In; j++) + { + CurArg = &(Cur->Parameters[j]); + + // Fix "Invalid src swizzle" : + if (CurArg->Mask == MASK_RGB) + { + CurArg->Mask = MASK_RGBA; + Result = true; + } + } + } + } + return Result; +} + +bool PSH_XBOX_SHADER::FixMissingR0a() +// On the Xbox, the alpha portion of the R0 register is initialized to +// the alpha component of texture 0 if texturing is enabled for texture 0 : +{ + int R0aDefaultInsertPos; + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + PSH_INTERMEDIATE_FORMAT NewIns = {}; + + // Detect a read of r0.a without a write, as we need to insert a "MOV r0.a, t0.a" as default (like the xbox has) : + R0aDefaultInsertPos = -1; + for (i = 0; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + if (Cur->Opcode < PO_TEX) // TODO : Check explicitly which instruction types are handled below + continue; + + // Make sure if we insert at all, it'll be after the DEF's : + if (R0aDefaultInsertPos < 0) + R0aDefaultInsertPos = i; + + // First, check if r0.a is read by this opcode : + if (Cur->ReadsFromRegister(PARAM_R, 0, MASK_A)) + { + R0aDefaultInsertPos = i; + break; + } + + // If this opcode writes to r0.a, we're done : + if (Cur->WritesToRegister(PARAM_R, 0, MASK_A)) + return false; + } + + if (R0aDefaultInsertPos >= 0) + { + // Insert a new opcode : MOV r0.a, t0.a + NewIns.Initialize(PO_MOV); + NewIns.Output[0].SetRegister(PARAM_R, 0, MASK_A); + NewIns.Parameters[0] = NewIns.Output[0]; + NewIns.Parameters[0].Type = PARAM_T; + NewIns.CommentString = "Inserted r0.a default"; + InsertIntermediate(&NewIns, R0aDefaultInsertPos); + return true; + } + return false; +} // FixMissingR0a + +bool PSH_XBOX_SHADER::FixMissingR1a() +// On the Xbox, the alpha portion of the R1 register is initialized to +// the alpha component of texture 1 if texturing is enabled for texture 1 : +{ + int R1aDefaultInsertPos; + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + PSH_INTERMEDIATE_FORMAT NewIns = {}; + + // Detect a read of r1.a without a write, as we need to insert a "MOV r1.a, t1.a" as default (like the xbox has) : + R1aDefaultInsertPos = -1; + for (i = 0; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + if (Cur->Opcode < PO_TEX) // TODO : Check explicitly which instruction types are handled below + continue; + + // First, check if r1.a is read by this opcode : + if (Cur->ReadsFromRegister(PARAM_R, 1, MASK_A)) + { + // Make sure if we insert at all, it'll be after the DEF's : + if (R1aDefaultInsertPos < 0) + R1aDefaultInsertPos = i; + + R1aDefaultInsertPos = i; + break; + } + + // If this opcode writes to r1.a, we're done : + if (Cur->WritesToRegister(PARAM_R, 1, MASK_A)) + return false; + } + + if (R1aDefaultInsertPos >= 0) + { + // Insert a new opcode : MOV r1.a, t1.a + NewIns.Initialize(PO_MOV); + NewIns.Output[0].SetRegister(PARAM_R, 1, MASK_A); + NewIns.Parameters[0] = NewIns.Output[0]; + NewIns.Parameters[0].Type = PARAM_T; + NewIns.CommentString = "Inserted r1.a default"; + InsertIntermediate(&NewIns, R1aDefaultInsertPos); + return true; + } + + return false; +} // FixMissingR1a + +bool PSH_XBOX_SHADER::FixUninitializedReads() +// On the Xbox, the alpha portion of the R1 register is initialized to +// the alpha component of texture 1 if texturing is enabled for texture 1 : +{ + int R1aDefaultInsertPos; + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + PSH_INTERMEDIATE_FORMAT NewIns = {}; + bool Result = false; + + int readPositions[32][4] = {}; + int writePositions[32][4] = {}; + int initPositions[32] = {}; + int initMasks[32] = {}; + + // Detect a read of r1.a without a write, as we need to insert a "MOV r1.a, t1.a" as default (like the xbox has) : + R1aDefaultInsertPos = -1; + for (i = 0; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + + for (int j = 0; j < MaxRegisterCount(PARAM_R); ++j) + { + // check reads + if (readPositions[j][0] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_R)) + { + readPositions[j][0] = i; + } + if (readPositions[j][1] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_G)) + { + readPositions[j][1] = i; + } + if (readPositions[j][2] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_B)) + { + readPositions[j][2] = i; + } + if (readPositions[j][3] == 0 && Cur->ReadsFromRegister(PARAM_R, j, MASK_A)) + { + readPositions[j][3] = i; + } + + // check writes + if (writePositions[j][0] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_R)) + { + writePositions[j][0] = i; + } + if (writePositions[j][1] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_G)) + { + writePositions[j][1] = i; + } + if (writePositions[j][2] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_B)) + { + writePositions[j][2] = i; + } + if (writePositions[j][3] == 0 && Cur->WritesToRegister(PARAM_R, j, MASK_A)) + { + writePositions[j][3] = i; + } + } + } + + for (int j = 0; j < MaxRegisterCount(PARAM_R); ++j) + { + int mask = 0; + int pos = IntermediateCount; + for (int i = 0; i < 4; ++i) + { + if (readPositions[j][i] <= writePositions[j][i] && readPositions[j][i] != 0) + { + mask |= (1 << i); + pos = std::min(pos, readPositions[j][i]); + } + } + + initPositions[j] = pos; + initMasks[j] = mask; + } + + NewIns.Initialize(PO_MOV); + NewIns.CommentString = "; Inserted to initialize register"; + for (int j = 0; j < MaxRegisterCount(PARAM_R); ++j) + { + int mask = initMasks[j]; + if (mask) + { + // Insert a new opcode : MOV r#.???, c0.??? + NewIns.Output[0].SetRegister(PARAM_R, j, mask); + NewIns.Parameters[0].SetScaleConstRegister(0.0f, Recompiled); + // r0 and r1 take their alpha from the respective texture coordinate + if (j < PSH_XBOX_MAX_R_REGISTER_COUNT) + { + mask &= MASK_RGB; + } + + InsertIntermediate(&NewIns, std::min(StartPos, initPositions[j])); + Result = true; + } + } + + return Result; +} // FixUninitializedReads + + +bool PSH_XBOX_SHADER::FixCoIssuedOpcodes() +{ + int i; + PPSH_INTERMEDIATE_FORMAT Cur; + bool Result = false; + // Since we're targetting m_PSVersion >= D3DPS_VERSION(2, 0), co-issued instructions are no longer supported, thus reset all IsCombined flags : + for (i = StartPos; i < IntermediateCount; i++) + { + Cur = &(Intermediate[i]); + if (Cur->IsArithmetic()) + { + if (Cur->IsCombined) + { + Cur->IsCombined = false; + Result = true; + } + } + } + return Result; +} + +bool PSH_XBOX_SHADER::FixInvalidDstRegister() +{ + int i, j; + PPSH_INTERMEDIATE_FORMAT Cur; + PPSH_IMD_ARGUMENT CurArg; + + bool Result = false; + for (i = IntermediateCount - 1; i >= StartPos; --i) + { + Cur = &(Intermediate[i]); + // Skip non-arithmetic opcodes + if (!Cur->IsArithmetic()) + continue; + + // Loop over the output arguments : + for (j = 0; j < PSH_OPCODE_DEFS[Cur->Opcode]._Out; j++) + { + CurArg = &(Cur->Output[j]); + + if (IsValidNativeOutputRegister(CurArg->Type, CurArg->Address)) + continue; + + int lastUsed = RegisterIsUsedFromIndexUntil(i + 1, CurArg->Type, CurArg->Address); + + PSH_ARGUMENT_TYPE dstType = PARAM_R; + int dstIndex = -1; + + if (IsValidNativeOutputRegister(PARAM_T)) + { + dstType = PARAM_T; + dstIndex = NextFreeRegisterFromIndexUntil(i + 1, PARAM_T, lastUsed); + } + + if (dstIndex == -1) + { + dstType = PARAM_R; + dstIndex = NextFreeRegisterFromIndexUntil(i + 1, PARAM_R, lastUsed); + } + + if (dstIndex != -1) + { + Result = true; + + if (Cur->ReadsFromRegister(CurArg->Type, CurArg->Address)) + { + if (lastUsed >= 0) ++lastUsed; + + PSH_INTERMEDIATE_FORMAT Ins = {}; + + Ins.Initialize(PO_MOV); + Ins.Output[0].SetRegister(dstType, dstIndex, 0); + Ins.Parameters[0].SetRegister(CurArg->Type, CurArg->Address, 0); + InsertIntermediate(&Ins, i); + ++Cur; + CurArg = &(Cur->Output[j]); + } + + ReplaceInputRegisterFromIndexOnwards(i + 1, CurArg->Type, CurArg->Address, dstType, dstIndex, lastUsed); + + CurArg->Type = dstType; + CurArg->Address = dstIndex; + } + } + } + return Result; +} + +// TODO: Refactor and optimize +bool PSH_XBOX_SHADER::FixOverusedRegisters() +{ + int i; + + bool Result = false; + + PSH_INTERMEDIATE_FORMAT Ins = {}; + Ins.Initialize(PO_MOV); + + // For all opcodes, try to put constant and discarded arguments in the rightmost slot, to ease following analysis : + i = IntermediateCount; + while (i > StartPos) + { + --i; + + int InsertPos = i; + + // Skip this operation on LRP instructions + // This prevents "error X5765: Dest register for LRP cannot be the same as first or third source register" in WWE RAW2 + if (Intermediate[i].Opcode == PO_LRP) { + continue; + } + + // Handle PARAM_C, PARAM_V and PARAM_T (in that order) : + for (int t = PARAM_C; t >= PARAM_T; t--) { + enum PSH_ARGUMENT_TYPE param_t = (enum PSH_ARGUMENT_TYPE)t; + int max_total = (t == PARAM_C) ? 2 : (t == PARAM_V) ? 999 : 1; + int addressCount = 0; + int total = 0; + while (Intermediate[i].ReadsFromRegister(param_t, -1, addressCount, total) && (addressCount > 1 || total > max_total)) + { + for (int p = 0; p < PSH_OPCODE_DEFS[Intermediate[i].Opcode]._In; ++p) + { + if (Intermediate[i].Parameters[p].Type == param_t) + { + int output = NextFreeRegisterFromIndexUntil(i, PARAM_R, i); + + // This inserts a MOV opcode that writes to R, reading from a C, V or T register + Ins.Output[0].SetRegister(PARAM_R, output, 0); + Ins.Parameters[0].SetRegister(Intermediate[i].Parameters[p].Type, Intermediate[i].Parameters[p].Address, 0); + InsertIntermediate(&Ins, InsertPos); + ++InsertPos; + + ReplaceInputRegisterFromIndexOnwards(InsertPos, Intermediate[InsertPos].Parameters[p].Type, Intermediate[InsertPos].Parameters[p].Address, PARAM_R, output, InsertPos); + Result = true; + break; + } + } + } + } + } + return Result; +} // FixOverusedRegisters + +// TODO : FocusBlur sample needs a zero in 'cnd' opcode + +/* RPSRegisterObject */ + +void RPSRegisterObject::Decode(uint8_t Value, bool aIsAlpha) +{ + IsAlpha = aIsAlpha; + Reg = (PS_REGISTER)(Value); +} + +std::string RPSRegisterObject::DecodedToString() +{ + assert((PS_REGISTER_DISCARD <= Reg) && (Reg <= PS_REGISTER_EF_PROD)); + + return PS_RegisterStr[Reg + 1]; +} + +/* RPSInputRegister */ + +void RPSInputRegister::Decode(uint8_t Value, bool aIsAlpha) +{ + RPSRegisterObject::Decode(Value & PS_NoChannelMask, aIsAlpha); + + Channel = (PS_CHANNEL)(Value & PS_CHANNEL_ALPHA); + InputMapping = (PS_INPUTMAPPING)(Value & 0xe0); + + // Remove the above flags from the register : + Reg = (PS_REGISTER)(Reg & 0xf); + + // Check if the input Register is ZERO, in which case we want to allow the extended registers : + if (Reg == PS_REGISTER_ZERO) + { + switch (InputMapping) { + case PS_REGISTER_ONE: case PS_REGISTER_NEGATIVE_ONE: case PS_REGISTER_ONE_HALF: case PS_REGISTER_NEGATIVE_ONE_HALF: + // These input mapping have their own register - keep these in 'Reg', so we can check for them : + Reg = (PS_REGISTER)(InputMapping); + break; + + case PS_INPUTMAPPING_EXPAND_NEGATE: + // This case has no separate PS_REGISTER define, but when applied to zero, also results in one : + Reg = PS_REGISTER_ONE; + break; + } + } +} + +std::string RPSInputRegister::DecodedToString() +{ + std::string Result; + std::string InputMappingStr = ""; + switch (Reg) { + case PS_REGISTER_ZERO: + { + Result = PS_RegisterStr[0]; + return Result; + } + case PS_REGISTER_ONE: + Result = PS_RegisterStr[0x11]; + break; + case PS_REGISTER_NEGATIVE_ONE: + Result = PS_RegisterStr[0x12]; + break; + case PS_REGISTER_ONE_HALF: + Result = PS_RegisterStr[0x13]; + break; + case PS_REGISTER_NEGATIVE_ONE_HALF: + Result = PS_RegisterStr[0x14]; + break; + default: + Result = RPSRegisterObject::DecodedToString(); + InputMappingStr = " | " + PS_InputMappingStr[(InputMapping >> 5) & 7]; + } + + // Render the channel as a string : + Result = Result + " | " + PS_ChannelStr[(Channel > 0) ? /*Alpha*/2 : (IsAlpha ? /*Blue*/1 : /*RGB*/0)] + InputMappingStr; + return Result; +} + +/* RPSCombinerOutput */ + +void RPSCombinerOutput::Decode(uint8_t Value, DWORD PSInputs, bool aIsAlpha) +{ + RPSRegisterObject::Decode(Value, aIsAlpha); + + // Decode PSAlphaInputs / PSRGBInputs : + Input1.Decode((PSInputs >> 8) & 0xFF, aIsAlpha); + Input2.Decode((PSInputs >> 0) & 0xFF, aIsAlpha); +} + +/* RPSCombinerStageChannel */ + +void RPSCombinerStageChannel::Decode(DWORD PSInputs, DWORD PSOutputs, bool aIsAlpha/* = false*/) +{ + // Get the combiner output flags : + CombinerOutputFlags = (PS_COMBINEROUTPUT)(PSOutputs >> 12); + + // Decompose the combiner output flags : + OutputSUM.OutputAB.DotProduct = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) > 0; // false=Multiply, true=DotProduct + OutputSUM.OutputCD.DotProduct = (CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0; // false=Multiply, true=DotProduct + + if (!aIsAlpha) + { + OutputSUM.OutputAB.BlueToAlpha = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) > 0; // false=Alpha-to-Alpha, true=Blue-to-Alpha + OutputSUM.OutputCD.BlueToAlpha = (CombinerOutputFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) > 0; // false=Alpha-to-Alpha, true=Blue-to-Alpha + } + + // Decode PSAlphaOutputs / PSRGBOutputs and PSAlphaInputs / PSRGBInputs : + OutputSUM.OutputAB.Decode((PSOutputs >> 4) & 0xF, (PSInputs >> 16) & 0xFFFF, aIsAlpha); + OutputSUM.OutputCD.Decode((PSOutputs >> 0) & 0xF, (PSInputs >> 0) & 0xFFFF, aIsAlpha); + OutputSUM.Decode((PSOutputs >> 8) & 0xF, aIsAlpha); + + AB_CD_SUM = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_CD_MUX) == 0; // true=AB+CD, false=MUX(AB,CD) based on R0.a +} + +// Note : On a hardware level, there are only 4 pixel shaders instructions present in the Nvidia NV2A GPU : +// - xdd (dot/dot/discard) > calculating AB=A.B and CD=C.D +// - xdm (dot/mul/discard) > calculating AB=A.B and CD=C*D +// - xmmc (mul/mul/mux) > calculating AB=A*B and CD=C*D and Mux=AB?CD +// - xmma (mul/mul/sum) > calculating AB=A*B and CD=C*D and Sum=AB+CD +// (One of the implications is, that once a dot-product is issued, no Sum or Mux operation is possible.) +// All other instructions (mov, add, sub, mul, lrp, dp3) are compiled into one of these 4 using varying arguments. +// All 4 instruction specify up to three output registers, all of which must be unique (or be discarded). +// +// Apart from the r0,r1 and t0-t3 registers, the NV2A allows writing to the v0,v1 (this conflicts with PS.1.3!) +// +// The precision of registers is also different; On the Xbox, all 4 color components (RGBA) for constant registers +// range from 0.0 to 1.0 (with 8 bits of precision), while all other registers (r, t and v) range from -1.0 to 1.0. +// +// This is different from native PS.1.3 in which constant registers suddenly have a range -1.0 to 1.0, but vertex +// registers (v0 and v1) range from 0.0 to 1.0 instead, and the temporary and texture registers have a range +// from negative 'MaxPixelShaderValue' to positive 'MaxPixelShaderValue', which value must at least be 1.0 +// (but depending on hardware capabilities can be higher). +// +// TODO : Correct emulation should correct these differences; The range of constant-registers must be converted +// from 0.0-1.0 to -1.0-1.0, and vertex-registers must be converted from -1.0..1.0 to 0.0..1.0 (if anything like +// that is at all possible!) +// +// register | Xbox range | Native range | Xbox | Native | +// C0..C8 | 0.0 - 1.0 ! -1.0 - 1.0 | readonly | readonly | +// R0..R1 | -1.0 - 1.0 | -1.0 - 1.0 | writeable | writeable | +// T0..T3 | -1.0 - 1.0 | -1.0 - 1.0 | writeable | writeable | +// V0..V1 | -1.0 - 1.0 ! 0.0 - 1.0 | writeable ! readonly | +// +// "-C0_bias_x2" shifts range from [ 0..1] to [-1..1] +// "-V0_bias_d2" shifts range from [-1..1] to [ 0..1] + +/* RPSFinalCombiner */ + +void RPSFinalCombiner::Decode(const DWORD PSFinalCombinerInputsABCD, const DWORD PSFinalCombinerInputsEFG, const DWORD PSFinalCombinerConstants) +{ + InputA.Decode((PSFinalCombinerInputsABCD >> 24) & 0xFF, /*aIsAlpha=*/false); + InputB.Decode((PSFinalCombinerInputsABCD >> 16) & 0xFF, /*aIsAlpha=*/false); + InputC.Decode((PSFinalCombinerInputsABCD >> 8) & 0xFF, /*aIsAlpha=*/false); + InputD.Decode((PSFinalCombinerInputsABCD >> 0) & 0xFF, /*aIsAlpha=*/false); + + InputE.Decode((PSFinalCombinerInputsEFG >> 24) & 0xFF, /*aIsAlpha=*/false); + InputF.Decode((PSFinalCombinerInputsEFG >> 16) & 0xFF, /*aIsAlpha=*/false); + InputG.Decode((PSFinalCombinerInputsEFG >> 8) & 0xFF, /*aIsAlpha=*/false); + FinalCombinerFlags = (PS_FINALCOMBINERSETTING)((PSFinalCombinerInputsEFG >> 0) & 0xFF); + + FinalCombinerC0Mapping = (PSFinalCombinerConstants >> 0) & 0xF; + FinalCombinerC1Mapping = (PSFinalCombinerConstants >> 4) & 0xF; + dwPS_GLOBALFLAGS = (PSFinalCombinerConstants >> 8) & 0x1; +} + +void XTL_DumpPixelShaderToFile(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ + static int PshNumber = 0; // Keep track of how many pixel shaders we've attempted to convert. + // Don't dump more than 100 shaders, to prevent cluttering the filesystem : + if (PshNumber >= 100) + return; + + char szPSDef[32]; + + sprintf(szPSDef, "PSDef%.03d.txt", PshNumber++); + FILE* out = fopen(szPSDef, "w"); + if (out) + { + fprintf(out, PSH_XBOX_SHADER::OriginalToString(pPSDef).c_str()); + fclose(out); + } +} + +PSH_RECOMPILED_SHADER XTL_EmuRecompilePshDef(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ + uint32_t PSVersion = D3DPS_VERSION(2, 0); // Use pixel shader model 2.0 by default + + extern D3DCAPS g_D3DCaps; + + if (g_D3DCaps.PixelShaderVersion > D3DPS_VERSION(3, 0)) { + // TODO : Test PSVersion = D3DPS_VERSION(3, 0); // g_D3DCaps.PixelShaderVersion; + // TODO : Make the pixel shader version configurable + } + + PSH_XBOX_SHADER PSH = {}; + PSH.SetPSVersion(PSVersion); + PSH.Decode(pPSDef); + return PSH.Convert(pPSDef); +} + +// From Dxbx uState.pas : + +PSH_RECOMPILED_SHADER DxbxRecompilePixelShader(XTL::X_D3DPIXELSHADERDEF *pPSDef) +{ +static const + char *szDiffusePixelShader = + "ps_2_x\n" + "dcl_2d s0\n" + "dcl t0.xy\n" + "texld r0, t0, s0\n" + "mov oC0, r0\n"; + std::string ConvertedPixelShaderStr; + DWORD hRet; + LPD3DXBUFFER pShader; + LPD3DXBUFFER pErrors; + DWORD *pFunction; + + // Attempt to recompile PixelShader + PSH_RECOMPILED_SHADER Result = XTL_EmuRecompilePshDef(pPSDef); + ConvertedPixelShaderStr = Result.NewShaderStr; + + // assemble the shader + pShader = nullptr; + pErrors = nullptr; + hRet = D3DXAssembleShader( + ConvertedPixelShaderStr.c_str(), + ConvertedPixelShaderStr.length(), + /*pDefines=*/nullptr, + /*pInclude=*/nullptr, + /*Flags=*/0, // D3DXASM_DEBUG, + /*ppCompiledShader=*/&pShader, + /*ppCompilationErrors*/&pErrors); + + if (hRet != D3D_OK) + { + EmuLog(LOG_LEVEL::WARNING, "Could not create pixel shader"); + EmuLog(LOG_LEVEL::WARNING, std::string((char*)pErrors->GetBufferPointer(), pErrors->GetBufferSize()).c_str()); + + printf(ConvertedPixelShaderStr.c_str()); + + hRet = D3DXAssembleShader( + szDiffusePixelShader, + strlen(szDiffusePixelShader), + /*pDefines=*/nullptr, + /*pInclude=*/nullptr, + /*Flags=*/0, // Was D3DXASM_SKIPVALIDATION, + /*ppCompiledShader=*/&pShader, + /*ppCompilationErrors*/&pErrors); + + if (hRet != D3D_OK) { + EmuLog(LOG_LEVEL::WARNING, "Could not create pixel shader"); + EmuLog(LOG_LEVEL::WARNING, std::string((char*)pErrors->GetBufferPointer(), pErrors->GetBufferSize()).c_str()); + CxbxKrnlCleanup("Cannot fall back to the most simple pixel shader!"); + } + + EmuLog(LOG_LEVEL::WARNING, "We're lying about the creation of a pixel shader!"); + } + + if (pShader) + { + pFunction = (DWORD*)(pShader->GetBufferPointer()); + if (hRet == D3D_OK) { + // redirect to windows d3d + hRet = g_pD3DDevice->CreatePixelShader + ( + pFunction, + (IDirect3DPixelShader**)(&(Result.ConvertedHandle)) //fixme + ); + + if (hRet != D3D_OK) { + printf(D3DErrorString(hRet)); + } + } + + // Dxbx note : We must release pShader here, else we would have a resource leak! + pShader->Release(); + pShader = nullptr; + } + + // Dxbx addition : We release pErrors here (or it would become a resource leak!) + if (pErrors) + { + pErrors->Release(); + pErrors = nullptr; + } + return Result; +} // DxbxRecompilePixelShader + +std::vector g_RecompiledPixelShaders; + +VOID DxbxUpdateActivePixelShader() // NOPATCH +{ + XTL::X_D3DPIXELSHADERDEF *pPSDef; + PPSH_RECOMPILED_SHADER RecompiledPixelShader; + DWORD ConvertedPixelShaderHandle; + DWORD CurrentPixelShader; + int i; + DWORD Register_; + D3DCOLOR dwColor; + D3DXCOLOR fColor; + + HRESULT Result = D3D_OK; + + // The first RenderState is PSAlpha, + // The pixel shader is stored in pDevice->m_pPixelShader + // For now, we still patch SetPixelShader and read from there... + + // Use the pixel shader stored in D3D__RenderState rather than the set handle + // This allows changes made via SetRenderState to actually take effect! + // NOTE: PSTextureModes is in a different location in the X_D3DPIXELSHADERFEF than in Render State mappings + // All other fields are the same. + // We cast D3D__RenderState to a pPSDef for these fields, but + // manually read from D3D__RenderState[X_D3DRS_PSTEXTUREMODES) for that one field. + // See D3DDevice_SetPixelShaderCommon which implements this + + pPSDef = g_pXbox_PixelShader != nullptr ? (XTL::X_D3DPIXELSHADERDEF*)(XboxRenderStates.GetPixelShaderRenderStatePointer()) : nullptr; + + if (pPSDef != nullptr) + { + RecompiledPixelShader = nullptr; + + // Now, see if we already have a shader compiled for this declaration : + for (auto it = g_RecompiledPixelShaders.begin(); it != g_RecompiledPixelShaders.end(); ++it) { + // Only compare parts that form a unique shader (ignore the constants and Direct3D8 run-time fields) : + if ((memcmp(&(it->PSDef.PSAlphaInputs[0]), &(pPSDef->PSAlphaInputs[0]), (8 + 2) * sizeof(DWORD)) == 0) + && (memcmp(&(it->PSDef.PSAlphaOutputs[0]), &(pPSDef->PSAlphaOutputs[0]), (8 + 8 + 3 + 8 + 4) * sizeof(DWORD)) == 0)) { + RecompiledPixelShader = &(*it); + break; + } + } + + // If none was found, recompile this shader and remember it : + if (RecompiledPixelShader == nullptr) { + // Recompile this pixel shader : + g_RecompiledPixelShaders.push_back(DxbxRecompilePixelShader(pPSDef)); + RecompiledPixelShader = &g_RecompiledPixelShaders.back(); + } + + // Switch to the converted pixel shader (if it's any different from our currently active + // pixel shader, to avoid many unnecessary state changes on the local side). + ConvertedPixelShaderHandle = RecompiledPixelShader->ConvertedHandle; + + g_pD3DDevice->GetPixelShader(/*out*/(IDirect3DPixelShader**)(&CurrentPixelShader)); + if (CurrentPixelShader != ConvertedPixelShaderHandle) + g_pD3DDevice->SetPixelShader((IDirect3DPixelShader*)ConvertedPixelShaderHandle); + + // Note : We set the constants /after/ setting the shader, so that any + // constants in the shader declaration can be overwritten (this will be + // needed for the final combiner constants at least)! + + // TODO: Figure out a method to forward the vertex-shader oFog output to the pixel shader FOG input register : + // We could use the unused oT4.x to output fog from the vertex shader, and read it with 'texcoord t4' in pixel shader! + // For now, we still disable native fog if pixel shader is said to handle it, this prevents black screen issues in titles using pixel shader fog. + // NOTE: Disabled: This breaks fog in XDK samples such as DolphinClassic. +#if-0 + if ((RecompiledPixelShader->PSDef.PSFinalCombinerInputsABCD > 0) || (RecompiledPixelShader->PSDef.PSFinalCombinerInputsEFG > 0)) { + g_pD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + } +#endif + + //PS_TEXTUREMODES psTextureModes[XTL::X_D3DTS_STAGECOUNT]; + //PSH_XBOX_SHADER::GetPSTextureModes(pPSDef, psTextureModes); + // + //for (i = 0; i < XTL::X_D3DTS_STAGECOUNT; i++) + //{ + // switch (psTextureModes[i]) + // { + // case PS_TEXTUREMODES_BUMPENVMAP: + // g_pD3DDevice->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP); + // break; + // case PS_TEXTUREMODES_BUMPENVMAP_LUM: + // g_pD3DDevice->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE); + // break; + // default: + // break; + // } + //} + + // Set constants, not based on g_PixelShaderConstants, but based on + // the render state slots containing the pixel shader constants, + // as these could have been updated via SetRenderState or otherwise : + for (i = 0; i < PSH_XBOX_CONSTANT_MAX; i++) + { + if (RecompiledPixelShader->ConstInUse[i]) + { + // Read the color from the corresponding render state slot : + switch (i) { + case PSH_XBOX_CONSTANT_FOG: + // Note : FOG.RGB is correct like this, but FOG.a should be coming + // from the vertex shader (oFog) - however, D3D8 does not forward this... + fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_FOGCOLOR); + break; + case PSH_XBOX_CONSTANT_FC0: + fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSFINALCOMBINERCONSTANT0); + break; + case PSH_XBOX_CONSTANT_FC1: + fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSFINALCOMBINERCONSTANT1); + break; + case PSH_XBOX_CONSTANT_MUL0: + case PSH_XBOX_CONSTANT_MUL1: + continue; + case PSH_XBOX_CONSTANT_BEM + 0: + case PSH_XBOX_CONSTANT_BEM + 1: + case PSH_XBOX_CONSTANT_BEM + 2: + case PSH_XBOX_CONSTANT_BEM + 3: + { + int stage = i - PSH_XBOX_CONSTANT_BEM; + DWORD* value = (DWORD*)&fColor; + + g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT00, &value[0]); + g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT01, &value[1]); + g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT11, &value[2]); + g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVMAT10, &value[3]); + break; + } + case PSH_XBOX_CONSTANT_LUM + 0: + case PSH_XBOX_CONSTANT_LUM + 1: + case PSH_XBOX_CONSTANT_LUM + 2: + case PSH_XBOX_CONSTANT_LUM + 3: + { + int stage = i - PSH_XBOX_CONSTANT_LUM; + DWORD* value = (DWORD*)&fColor; + + g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVLSCALE, &value[0]); + g_pD3DDevice->GetTextureStageState(stage, D3DTSS_BUMPENVLOFFSET, &value[1]); + value[2] = 0; + value[3] = 0; + break; + } + default: + fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSCONSTANT0_0 + i); + break; + } + + // Convert it back to 4 floats : + //fColor = dwColor; + // Read the register we can use on PC : + Register_ = RecompiledPixelShader->ConstMapping[i]; + // TODO : Avoid the following setter if it's no different from the previous update (this might speed things up) + // Set the value locally in this register : + g_pD3DDevice->SetPixelShaderConstantF + ( + Register_, + (PixelShaderConstantType*)(&fColor), + 1 + ); + } + } + } + else + { + ConvertedPixelShaderHandle = 0; + g_pD3DDevice->SetPixelShader((IDirect3DPixelShader*)ConvertedPixelShaderHandle); + } +} + +// End of Dxbx code + +#define REVEL8N_PIXEL_SHADER_CHANGES + +// help functions +char *pCodeBuffer=nullptr; + +void WriteCode(const char *str, ...) +{ + char szBuffer[256]; + va_list argp; + + va_start(argp, str); + vsprintf(szBuffer, str, argp); + va_end(argp); + + //printf("%s", szBuffer); + if(pCodeBuffer) + strcat(pCodeBuffer, szBuffer); +} + +void InsertString(char *szStr, int iOffset, char *szInsert, int iInsertLen, int iRemoveLen); + +inline void HandleInputOutput +( + DWORD dwInput, + DWORD dwOutput, + BOOL bAlpha, + int iCStage, + BOOL bUniqueC0, + BOOL bUniqueC1, + int *iPSC0, + int *iPSC1, + + BOOL bGlobalRGBA, + + BOOL bFinalCombiner +); + +inline void GetRegister +( + WORD wRegister, + char *szRegister, + BOOL bUniqueC0, + BOOL bUniqueC1, + int iCStage, + int *pPSC0, + int *pPSC1 +); + +inline void GetInputMapping(WORD wInputMapping, char *szInputMapping, char *szInputMappingAfter, char *szConst); +inline void GetChannel(WORD wInputChannel, char *szInput, BOOL bAlpha, BOOL bGlobalRGBA); + +inline void GetOutputFlags +( + WORD wOutputFlags, + char *szInstMod, + char *szABOp, + char *szCDOp, + char *szABCDOp, + + BOOL *bAB_BA, + BOOL *bCD_BA, + + BOOL *bShl1Bias, + BOOL *bBias +); + +//inline BOOL CheckOpForMov(char *szOp, char *szInputs1, char *szInput2, char *szRegInput); +inline BOOL OptimizeOperation +( + char *szOp, + char *szOp1, + + char *szOp2, + char *szMod, + + char *szInputAB1, + char *szInputAB2, + + char *szInputCD1, + char *szInputCD2, + + char *szConstRegAB1, + char *szConstRegAB2, + char *szConstRegCD1, + char *szConstRegCD2, + + char *szOutAB, + char *szOutCD, + char *szABCDOutput, + + char *szCommand +); + +inline void ClearConstRegVars(); +inline void CorrectConstToReg(char *szConst, int *pPSC0, int *pPSC1); + +int iPreRunLen=0; + +// This is set to true if an operation tries to read from r0 +// before r0 was written, in that case we do the same as the xbox +// we write the value of t0.a to r0 ;-) +BOOL bR0WAccess=FALSE; +BOOL bR0Written=FALSE; +BOOL bR0AWritten=FALSE; +/* +BOOL bR1WAccess=FALSE; +BOOL bR1AWAccess=FALSE; +BOOL bR1RGBWAccess=FALSE; + +BOOL bR1AWritten=FALSE; +BOOL bR1RGBWritten=FALSE; +BOOL bR1Written=FALSE; +*/ +BOOL bR0AlphaOutput = FALSE; + +BOOL bLastOpRGB = FALSE; + +BOOL bEFProduct = FALSE; +BOOL bV1R0Reg = FALSE; + +#define DEF_VAR_TABLE_LEN 7 +char szVar[][10] = +{ + "r0", + "r1", + "t0", + "t1", + "t2", + "t3", + "t4" +}; + +inline void HandleInputOutput +( + DWORD dwInput, + DWORD dwOutput, + BOOL bAlpha, + int iCStage, + BOOL bUniqueC0, + BOOL bUniqueC1, + int *iPSC0, + int *iPSC1, + + BOOL bGlobalRGBA, + + BOOL bFinalCombiner +) +{ + // INPUTS + if(bFinalCombiner) printf("\npPSD.PSFinalCombinerInputsABCD = PS_COMBINERINPUTS(\n"); + else if(bAlpha) printf("\npPSD.PSAlphaInputs[%d] = PS_COMBINERINPUTS(\n", iCStage); + else printf("\npPSD.PSRGBInputs[%d] = PS_COMBINERINPUTS(\n", iCStage); + + WORD wCombinerInputs[4]; // 0=a, 1=b, 2=c, 3=d + wCombinerInputs[0] = (WORD) ((dwInput>>24) & 0xFF); + wCombinerInputs[1] = (WORD) ((dwInput>>16) & 0xFF); + wCombinerInputs[2] = (WORD) ((dwInput>> 8) & 0xFF); + wCombinerInputs[3] = (WORD) ( dwInput & 0xFF); + + char szInput[4][20] = {0}; + char szConst[4][20] = {0}; + char szInputMapping[4][20] = {0}; + char szInputMappingAfter[4][20] = {0}; + char szChannels[4][5] = {0}; + + // Go through inputs + int i=0; + for(i=0; i<4; i++) + { + szInput[i][0]=0x00; // Fast way to zero a string ;-) + szConst[i][0]=0x00; + szInputMapping[i][0]=0x00; + szInputMappingAfter[i][0]=0x00; + szChannels[i][0]=0x00; + + GetRegister(wCombinerInputs[i] & 0xF, szInput[i], bUniqueC0, bUniqueC1, iCStage, iPSC0, iPSC1); + + if(strcmp(szInput[i], "r0")==0) + { + if(!bR0AWritten) + strcpy(szInput[i], "t0"); + + if(!bR0Written) { + strcpy(szInput[i], "t0"); + //bR0WAccess=TRUE; + } + } + + printf(" | "); + GetInputMapping(wCombinerInputs[i] & 0x1E0, szInputMapping[i], szInputMappingAfter[i], szConst[i]); + printf(" | "); + GetChannel(wCombinerInputs[i] & 0x10, szChannels[i], bAlpha, bGlobalRGBA); + printf(",\n"); + + if((wCombinerInputs[i] & 0xF)==0x00) + szInput[i][0]=0x00; + + // 6928: check this as I doubt whether it works really like that + /*if(strcmp(szInput[i], "r1")==0) + { + // EmuLog(LOG_LEVEL::DEBUG, "channel: %s", szChannels[i]); + // Sleep(3000); + + if((strcmp(szChannels[i], ".a")==0) && (!bR1AWritten)) { + bR1AWAccess=TRUE; + + strcpy(szInput[i], " t1"); + } else if((strcmp(szChannels[i], ".rgb")==0) && (!bR1RGBWritten)) { + bR1RGBWAccess=TRUE; + + strcpy(szInput[i], " t1"); + } else if(!bR1Written) { + bR1WAccess=TRUE; + + strcpy(szInput[i], " t1"); + } + + if(bR1AWAccess && bR1RGBWAccess) + bR1WAccess=TRUE; + + //if(bR1AWAccess || bR1RGBWAccess) + // strcpy(szInput[i], "t1"); + }*/ + + //printf("\n*** szInput[%d]: %s\n", i, szInput[i]); + } + + // Input stuff + BOOL bInput[4] = {0, 0, 0, 0}; + if(szInput[0][0]) bInput[0]=TRUE; + if(szInput[1][0]) bInput[1]=TRUE; + if(szInput[2][0]) bInput[2]=TRUE; + if(szInput[3][0]) bInput[3]=TRUE; + +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + // Correct param if a constant is used! + if(!bInput[0]) + CorrectConstToReg(szConst[0], iPSC0, iPSC1); + if(!bInput[1]) + CorrectConstToReg(szConst[1], iPSC0, iPSC1); + if(!bInput[2]) + CorrectConstToReg(szConst[2], iPSC0, iPSC1); + if(!bInput[3]) + CorrectConstToReg(szConst[3], iPSC0, iPSC1); + + bool bEmptyChannel = false; +#endif + + char szCompleteInput[4][20] = {0}; + for(i=0; i<4; i++) + { + strcpy(szCompleteInput[i], szInputMapping[i]); +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + if(bInput[i]) + { +#endif + strcat(szCompleteInput[i], szInput[i]); +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + bEmptyChannel = bEmptyChannel || (szChannels[i][0] == 0); + } + else + strcat(szCompleteInput[i], &szConst[i][4]); +#endif + strcat(szCompleteInput[i], szInputMappingAfter[i]); + strcat(szCompleteInput[i], szChannels[i]); + } + + printf(");\n"); + + if(!bFinalCombiner) + { + // OUTPUTS + if(bAlpha) printf("\npPSD.PSAlphaOutputs[%d] = PS_COMBINEROUTPUTS(\n", iCStage); + else printf("\npPSD.PSRGBOutputs[%d] = PS_COMBINEROUTPUTS(\n", iCStage); + + WORD wCombinerOutputs[3]; // 0=d0 (ab), 1=d1 (cd), 2=d2 (mux_sum) + wCombinerOutputs[0] = (WORD) ((dwOutput>> 4) & 0xF); + wCombinerOutputs[1] = (WORD) ( dwOutput & 0xF); + wCombinerOutputs[2] = (WORD) ((dwOutput>> 8) & 0xF); + WORD wCombinerOutputFlags = (WORD) ((dwOutput>>12) & 0xFF); + + char szOutput[3][10] = {0}; + char szOutputMod[10]="\0"; + + char szABOp[10]="\0"; + char szCDOp[10]="\0"; + char szABCDOp[10]="\0"; + + BOOL bAB_B2A; + BOOL bCD_B2A; + + BOOL bR0Now = FALSE; + BOOL bR0ANow = FALSE; + BOOL bVAccess[3] = {0,0,0}; + + BOOL bOpRGB_Current = FALSE; + BOOL bCurrOpRealAlpha = FALSE; + + // Go through outputs + for(i=0; i<3; i++) + { + szOutput[i][0]=0x00; // Fast way to zero a string ;-) + + GetRegister(wCombinerOutputs[i], szOutput[i], bUniqueC0, bUniqueC1, iCStage, iPSC0, iPSC1); + if(strcmp(szOutput[i], "r0")==0) + { + bR0Now=TRUE; + + // this checks for output to r0.a + if(bGlobalRGBA || (!bGlobalRGBA && bAlpha)) + bR0AlphaOutput=TRUE; + } + + if((strcmp(szOutput[i], "v0")==0) || (strcmp(szOutput[i], "v1")==0)) { bVAccess[i] = TRUE; } + + /*BOOL bR1_Written = FALSE; + if(strcmp(szOutput[i], "r1")==0) + bR1_Written=TRUE;*/ + + // check channel! + if(!bGlobalRGBA && bAlpha) + { + strcat(szOutput[i], ".a"); + bCurrOpRealAlpha = TRUE; + + if(bR0Now) + bR0ANow=TRUE; + + /*if(bR1_Written) + bR1AWritten=TRUE;*/ + } + else if(!bGlobalRGBA && !bAlpha +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + && !bEmptyChannel +#endif + ) + { + strcat(szOutput[i], ".rgb"); + + if(wCombinerOutputs[i]) + bOpRGB_Current = TRUE; + + /*if(bR1_Written) + bR1RGBWritten=TRUE;*/ + } + else + { + /*if(bR1_Written) + bR1Written=TRUE;*/ + + if(bR0Now) + bR0ANow=TRUE; + } + + printf(",\n"); + + if(wCombinerOutputs[i]==0x00) + szOutput[i][0]=0x00; + + //printf("\n*** szOutput[%d]: %s\n", i, szOutput[i]); + } + + BOOL bBias=FALSE; + BOOL bSh1Bias=FALSE; + + GetOutputFlags( + wCombinerOutputFlags, + szOutputMod, + + szABOp, + szCDOp, + szABCDOp, + + &bAB_B2A, + &bCD_B2A, + + &bSh1Bias, + &bBias); + + if(bR0Now) + bR0Written=TRUE; + + if(bR0ANow) + bR0AWritten=TRUE; + + printf(");\n"); + + // Find output for the operations + char szOut[10]="\0"; + char szOut1[10]="\0"; + + //printf("|****| %s |****|\n", szOutput[1]); + + if(szOutput[0][0]) + strcpy(szOut, szOutput[0]); + if(szOutput[1][0]) + strcpy(szOut1, szOutput[1]); + +#ifndef REVEL8N_PIXEL_SHADER_CHANGES + if(szOutput[2][0]) + { + /* + //EmuWarningMsg("THIS IS WRONG, FIX ME!"); + //if(!szOutput[1][0]) + // strcpy(szOut1, szOutput[2]); + EmuLog(LOG_LEVEL::DEBUG, "(!szOutput[0][0] || !szOutput[1][0]) && szOutput[2][0] = TRUE!"); + + BOOL bUsable=TRUE; + for(i=2; i<4; i++) + { + if((strcmp(szOutput[2], szInput[i])==0) || (strcmp(szOutput[2], szOut1)==0)) { + bUsable=FALSE; + } + } + if(bUsable && !szOutput[0][0]) + { + + strcpy(szOut, szOutput[2]); + + EmuLog(LOG_LEVEL::DEBUG, "BUsable = TRUE, new output: %s", szOut); + + } + else { + printf("!WARNING!: The operation uses the output register also as input!" + "Trying to find a free output register. It is possible that the pixel shader " + "will generate garbage because the new free one contains data used " + "in an other comming operation!\n\n"); + + for(int j=0; j> 24) & 0xFF); + wEFG[1] = (WORD) ((dwOutput >> 16) & 0xFF); + wEFG[2] = (WORD) ((dwOutput >> 8) & 0xFF); + + BOOL bInputEFG[3] = {0, 0, 0}; + char szCompleteInputEFG[3][10]; + + char szInputEFG[3][10]; + char szInputMappingEFG[3][10]; + char szInputMappingAfterEFG[3][10]; + char szConstEFG[3][10]; + + for(i=0; i<3; i++) + { + szInputEFG[i][0]=0x00; + szInputMappingEFG[i][0]=0x00; + szInputMappingAfterEFG[i][0]=0x00; + szConstEFG[i][0]=0x00; + + GetRegister(wEFG[i] & 0xF, szInputEFG[i], bUniqueC0, bUniqueC1, 0, iPSC0, iPSC1); + printf(" | "); + GetInputMapping(wEFG[i] & 0x1E0, szInputMappingEFG[i], szInputMappingAfterEFG[i], szConstEFG[i]); + printf(" | "); + GetChannel(wEFG[i] & 0x10, szInputEFG[i], bAlpha, FALSE); + printf(", \n"); + + strcpy(szCompleteInputEFG[i], szInputMappingEFG[i]); + strcat(szCompleteInputEFG[i], szInputEFG[i]); + strcat(szCompleteInputEFG[i], szInputMappingAfterEFG[i]); + + if(szInputEFG[i][0]) + bInputEFG[i]=TRUE; + else + { + // add that constant as a reg + CorrectConstToReg(szConstEFG[i], iPSC0, iPSC1); + } + } + + if(dwV1R0_EFProd_Flags & 0x20) + printf("PS_FINALCOMBINERSETTINGS_COMPLEMENT_R0"); + else if(dwV1R0_EFProd_Flags & 0x40) + printf("PS_FINALCOMBINERSETTINGS_COMPLEMENT_V1"); + else if(dwV1R0_EFProd_Flags & 0x80) + printf("PS_FINALCOMBINERSETTINGS_CLAMP_SUM"); + else + printf("0"); + + printf(");\n"); + + if (bV1R0Reg) + { + char sMod[10] = {0}; + char sV1[10] = {0}; + char sR0[10] = {0}; + if(dwV1R0_EFProd_Flags & 0x20) + strcpy(sR0, "1-"); + else if(dwV1R0_EFProd_Flags & 0x40) + strcpy(sV1, "1-"); + else if(dwV1R0_EFProd_Flags & 0x80) + strcpy(sMod, "_sat"); + + if (bEFProduct) + { + EmuLog(LOG_LEVEL::WARNING, "EF Product and V1R0 register used at the same time!"); + } + else + { + WriteCode("; (v1 + r0)\nadd%s r0, %sr0, %sv1\n\n", sMod, sR0, sV1); + } + } + + // only we we will use this later in final combiner stuff!! + // all inputs are known now, so check: + if(bEFProduct) { + + // r0 = E * F (E or F must be the r0 calculated before otherwise the stage results + // are lost, problem??? + if(! + ((!bInputEFG[0] && szConstEFG[0][0]=='0') && + (!bInputEFG[1] && szConstEFG[1][0]=='0'))) { + WriteCode(";E * F\nmul r0, %s, %s\n\n", bInputEFG[0] ? szCompleteInputEFG[0] : &szConstEFG[0][4], + bInputEFG[1] ? szCompleteInputEFG[1] : &szConstEFG[1][4]); + } + + } + + // Now the result: + + // What is done by the final combiner: + // final color = s0*s1 + (1-s0)*s2 + s3 + + // lrp r0, s0, s1, s2 + // add r0, r0, s3 + // s0 = szInput[0] + // s1 = szInput[1] + // s2 = szInput[2] + // s3 = szInput[3] + + // Check whether it is a mov r0, r0 + // for example: lrp r0, 1, r0, 0 + // r0 = 1*r0 + (1-1)*r0 + 0 + // --> r0 = r0 + + for(i=0; i<4; i++) + { + if(!bInput[i]) + CorrectConstToReg(szConst[i], iPSC0, iPSC1); + } + + if(!((!bInput[0]) && (szConst[0][0] == '1') && (strncmp(szCompleteInput[1], "r0", 2)==0))) + { + // cases for s2 + // s2 == 0 --> final color = s0*s1 + s3 + if((!bInput[2]) && (szConst[2][0] == '0')) + { + WriteCode("mul r0.rgb, %s, %s\n", + bInput[0] ? szCompleteInput[0] : &szConst[0][4], + bInput[1] ? szCompleteInput[1] : &szConst[1][4]); + } + // s0 == 0 --> final color = s2 + s3 + else if((!bInput[0]) && (szConst[0][0] == '0')) { + // Check whether s2 is r0!!! + if(!(bInput[2] && (strncmp(szCompleteInput[2], "r0", 2)==0))) + WriteCode("mov r0.rgb, %s\n", + bInput[2] ? szCompleteInput[2] : &szConst[2][4]); + } + // s0 == 1 --> final color = s1 + s3 + else if((!bInput[0]) && (szConst[0][0] == '1')) { + // Check whether s1 is r0!!! + if(!(bInput[1] && (strncmp(szCompleteInput[1], "r0", 2)==0))) + WriteCode("mov r0.rgb, %s\n", + bInput[1] ? szCompleteInput[1] : &szConst[1][4]); + } + // no special cases + else if(bInput[2] || bInput[0]) + { + WriteCode("lrp r0.rgb, %s, %s, %s\n", + bInput[0] ? szCompleteInput[0] : &szConst[0][4], + bInput[1] ? szCompleteInput[1] : &szConst[1][4], + bInput[2] ? szCompleteInput[2] : &szConst[2][4]); + } + } + // case for s3 + if(bInput[3] || (szConst[3][0] != '0')) + WriteCode("add r0.rgb, r0, %s\n", bInput[3] ? szCompleteInput[3] : &szConst[3][4]); + + // Alpha ouput (G) + if(bInputEFG[2] && (strncmp(szInputEFG[2], "r0", 2)!=0)) + { + bR0AlphaOutput=TRUE; + + WriteCode("mov r0.a, %s\n", + bInputEFG[2] ? szCompleteInputEFG[2] : &szConstEFG[2][4]); + } + + //else + // WriteCode("mov r0.a, v0.a\n"); + //*/ + //Sleep(3000); + } +} + +inline void GetRegister(WORD wRegister, char *szRegister, BOOL bUniqueC0, BOOL bUniqueC1, int iCStage, int *iPSC0, int *iPSC1) +{ + // Determine register + switch(wRegister) + { + case 0x00: + printf("PS_REGISTER_ZERO"); + break; + case 0x01: // read + printf("PS_REGISTER_C0"); + if(bUniqueC0) + sprintf(szRegister, "c%d", iPSC0[iCStage]); + else + strcpy(szRegister, "c0"); + break; + case 0x02: // read + printf("PS_REGISTER_C1"); + if(bUniqueC0) + sprintf(szRegister, "c%d", iPSC1[iCStage]); + else + strcpy(szRegister, "c1"); + break; + case 0x03: // read + { + printf("PS_REGISTER_FOG"); + + char szOneHalf[40] = "0.5\0"; + CorrectConstToReg(szOneHalf, iPSC0, iPSC1); + + strcpy(szRegister, &szOneHalf[4]); // Unsupported + break; + } + case 0x04: // read/(write ???) + printf("PS_REGISTER_V0"); + strcpy(szRegister, "v0"); + break; + case 0x05: // read/(write ???) + printf("PS_REGISTER_V1"); + strcpy(szRegister, "v1"); + break; + case 0x08: // read/write + printf("PS_REGISTER_T0"); + strcpy(szRegister, "t0"); + //strcpy(szRegister, "r2"); + break; + case 0x09: // read/write + printf("PS_REGISTER_T1"); + strcpy(szRegister, "t1"); + //strcpy(szRegister, "r3"); + break; + case 0x0A: // read/write + printf("PS_REGISTER_T2"); + strcpy(szRegister, "t2"); + //strcpy(szRegister, "r4"); + break; + case 0x0B: // read/write + printf("PS_REGISTER_T3"); + strcpy(szRegister, "t3"); + //strcpy(szRegister, "r5"); + break; + case 0x0C: // read/write + printf("PS_REGISTER_R0"); + strcpy(szRegister, "r0"); + break; + case 0x0D: // read/write + printf("PS_REGISTER_R1"); + strcpy(szRegister, "r1"); + break; + case 0x0E: // read + printf("PS_REGISTER_V1R0_SUM"); + + bV1R0Reg = TRUE; + strcpy(szRegister, "r0"); //"V1R0");//(v1+r0)"); + break; + case 0x0F: + printf("PS_REGISTER_EF_PROD"); + + // we save it in r0 + bEFProduct = TRUE; + strcpy(szRegister, "r0");/* e * f --> combiner input */ + break; + default: + printf("/*Unknown register %d*/", wRegister); + break; + } +} + +inline void GetInputMapping(WORD wInputMapping, char *szInputMapping, char *szInputMappingAfter, char *szConst) +{ + strcpy(szConst, "0"); + switch(wInputMapping) + { + case 0x00: // max(0,x) [ok for final combiner] + printf("PS_INPUTMAPPING_UNSIGNED_IDENTITY"); + break; + case 0x20: // 1 - max(0,x) [ok for final combiner] + printf("PS_INPUTMAPPING_UNSIGNED_INVERT"); + strcpy(szInputMapping, "1-"); + strcpy(szConst, "1"); + break; + case 0x40: // 2*max(0,x) - 1 [invalid for final combiner] + printf("PS_INPUTMAPPING_EXPAND_NORMAL"); + strcpy(szInputMappingAfter, "_bx2"); // right??? + strcpy(szConst, "-1"); + break; + case 0x60: // 1 - 2*max(0,x) [invalid for final combiner] + printf("PS_INPUTMAPPING_EXPAND_NEGATE"); + + strcpy(szInputMapping, "-"); + strcpy(szInputMappingAfter, "_bx2"); + strcpy(szConst, "1"); + break; + case 0x80: // max(0,x) - 1/2 [invalid for final combiner] + printf("PS_INPUTMAPPING_HALFBIAS_NORMAL"); + strcpy(szInputMappingAfter, "_bias"); + + strcpy(szConst, "-0.5"); + break; + case 0xA0: // 1/2 - max(0,x) [invalid for final combiner] + printf("PS_INPUTMAPPING_HALFBIAS_NEGATE"); + + strcpy(szConst, "0.5"); + + // Negate is run last if combined with bias + strcpy(szInputMapping, "-"); + strcpy(szInputMappingAfter, "_bias"); + break; + case 0xC0: // x [invalid for final combiner] + printf("PS_INPUTMAPPING_SIGNED_IDENTITY"); + break; + case 0xE0: // -x [invalid for final combiner] + printf("PS_INPUTMAPPING_SIGNED_NEGATE"); + strcpy(szInputMapping, "-"); + break; + default: + printf("/*Unknown input mapping %d!*/", wInputMapping); + break; + } +} + +inline void GetChannel(WORD wInputChannel, char *szInput, BOOL bAlpha, BOOL bGlobalRGBA) +{ + switch(wInputChannel) + { + case 0x00: + if(bAlpha) { + printf("PS_CHANNEL_BLUE"); + strcat(szInput, ".b"); + } else { + printf("PS_CHANNEL_RGB"); + + //if (!bGlobalRGBA) + // strcat(szInput, ".rgb"); + } + break; + case 0x10: + printf("PS_CHANNEL_ALPHA"); + + // TODO: check this || !bAlpha, it should mean that alpha channel + // is detected in a RGB register, then it must be set also + // if both commands the same are (in that case it has to be RGB!) + if (!bGlobalRGBA || !bAlpha) + strcat(szInput, ".a"); + break; + default: + printf("/*Unknown channel %d!*/", wInputChannel); + break; + } +} + +inline void GetOutputFlags +( + WORD wOutputFlags, + char *szInstMod, + char *szABOp, + char *szCDOp, + char *szABCDOp, + + BOOL *bAB_BA, + BOOL *bCD_BA, + + BOOL *bShl1Bias, + BOOL *bBias +) +{ + // Output mapping + switch (wOutputFlags & 0x38) { + case PS_COMBINEROUTPUT_BIAS: + { + printf("PS_COMBINEROUTPUT_BIAS"); // y = x - 0.5 + //strcpy(szInstMod, "_bias"); + + // Only over this: + // mov y, y_bias + (*bBias)=TRUE; + break; + } + case PS_COMBINEROUTPUT_SHIFTLEFT_1: // 0x10L + { + printf("PS_COMBINEROUTPUT_SHIFTLEFT_1"); // y = x*2 + strcpy(szInstMod, "_x2"); + break; + } + case PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS: // 0x18L + { + LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS"); + printf("PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS"); // y = (x - 0.5)*2 + + //strcpy(szInstMod, "_x2"); + // what is missing is a subtraction of 1 + // --> 2 * (x - 0.5) = 2x - 1 + + // But this won't work because we would have to do 2 movs + // to subtract 1 + // Let's do this: mov_x2 y, y_bias + (*bShl1Bias)=TRUE; + break; + } + case PS_COMBINEROUTPUT_SHIFTLEFT_2: // 0x20L + { + LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTLEFT_2"); + printf("PS_COMBINEROUTPUT_SHIFTLEFT_2"); // y = x*4 + strcpy(szInstMod, "_x4"); + break; + } + // case PS_COMBINEROUTPUT_SHIFTLEFT_2_BIAS: // 0x28L, // y = (x - 0.5)*4 + case PS_COMBINEROUTPUT_SHIFTRIGHT_1: // 0x30L + { + LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTRIGHT_1"); + printf("PS_COMBINEROUTPUT_SHIFTRIGHT_1"); // y = x/2 + strcpy(szInstMod, "_d2"); + break; + } + // case PS_COMBINEROUTPUT_SHIFTRIGHT_1_BIAS: // 0x38L, // y = (x - 0.5)/2 + default: + printf("PS_COMBINEROUTPUT_IDENTITY"); + } + + printf(" | "); + + // MUX operation + if(wOutputFlags & 0x04) { + printf("PS_COMBINEROUTPUT_AB_CD_MUX"); + strcpy(szABCDOp, "cnd"); + + if((!bR0Written) || (!bR0AWritten)) + bR0WAccess=TRUE; + } + else + { + printf("PS_COMBINEROUTPUT_AB_CD_SUM"); // 3rd output is AB+CD + strcpy(szABCDOp, "add"); + } + + printf(" | "); + + // Function for ab side + if(wOutputFlags & 0x02) + { + printf("PS_COMBINEROUTPUT_AB_DOT_PRODUCT"); // RGB only + strcpy(szABOp, "dp3"); + } else { + printf("PS_COMBINEROUTPUT_AB_MULTIPLY"); + strcpy(szABOp, "mul"); + } + + printf(" | "); + + // Functiomn for cd side + if(wOutputFlags & 0x01) + { + printf("!!!PS_COMBINEROUTPUT_CD_DOT_PRODUCT!!!"); // RGB only + strcpy(szCDOp, "dp3"); + } else { + printf("PS_COMBINEROUTPUT_CD_MULTIPLY"); + strcpy(szCDOp, "mul"); + } + + // Blue to alpha for ab side + if(wOutputFlags & 0x80) { + printf(" | PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA"); // RGB only + (*bAB_BA)=TRUE; + } else (*bAB_BA)=FALSE; + + // Blue to alpha for cd side + if(wOutputFlags & 0x40) { + printf(" | PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA"); // RGB only + (*bCD_BA)=TRUE; + } else (*bCD_BA)=FALSE; +} + +enum OpType +{ + OPTYPE_NOP = -1, + OPTYPE_MOV = 0, + OPTYPE_ADD, + OPTYPE_MUL, + OPTYPE_DP3, + OPTYPE_CND, +}; + +inline BOOL OptimizeOperation +( + char *szOp, + char *szOp1, + + char *szOp2, + char *szMod, + + char *szInputAB1, + char *szInputAB2, + + char *szInputCD1, + char *szInputCD2, + + char *szConstRegAB1, + char *szConstRegAB2, + char *szConstRegCD1, + char *szConstRegCD2, + + char *szOutAB, + char *szOutCD, + char *szABCDOutput, + + char *szCommand) +{ + printf("----------\nszOp: |%s|\nszOp1: |%s|\nszOp2: |%s|\nszMod: |%s|\n" + "szInputAB1: |%s|\nszInputAB2: |%s|\nszInputCD1: |%s|\nszInputCD2: |%s|\n" + "szOutAB: |%s|\nszOutCD: |%s|\nszABCDOutput: |%s|\n", + szOp, szOp1, szOp2, szMod, szInputAB1, szInputAB2, szInputCD1, szInputCD2, + szOutAB, szOutCD, szABCDOutput); + + char szABCDInput[2][10]; + szABCDInput[0][0]=0x00; + szABCDInput[1][0]=0x00; + + szCommand[0]=0x00; + + char *szOps[3]; + szOps[0] = szOp; + szOps[1] = szOp1; + szOps[2] = szOp2; + + char *szInputs[4]; + szInputs[0] = szInputAB1; + szInputs[1] = szInputAB2; + szInputs[2] = szInputCD1; + szInputs[3] = szInputCD2; + + char *szRealInputs[4]; + szRealInputs[0] = szConstRegAB1; + szRealInputs[1] = szConstRegAB2; + szRealInputs[2] = szConstRegCD1; + szRealInputs[3] = szConstRegCD2; + +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + char *szOutputs[3]; + szOutputs[0] = szOutAB; + szOutputs[1] = szOutCD; + szOutputs[2] = szABCDOutput; +#endif + + // TODO: check mov: other operations like lrp + // are ignored because of a shitty mul with 1 + BOOL bMov[3]={0, 0, 0}; + + int i=0; + for(i=0; i<2; i++) + { + //printf("szOps[i]: %s\n", szOps[i]); + //printf("szInputs[i*2+1]: %s\n", szInputs[i*2+1]); + if(strcmp(szOps[i], "mul")==0) + { + // If it is a mul, it can also be only a mov + if(strcmp(szInputs[i*2], "1")==0) { + //strcpy(szABCDInput[i], szInputs[i*2+1]); +#ifndef REVEL8N_PIXEL_SHADER_CHANGES + strcpy(szABCDInput[i], szRealInputs[i*2+1]); +#endif + + strcpy(szOps[i], "mov"); + + strcpy(szInputs[i*2], szInputs[i*2+1]); + strcpy(szRealInputs[i*2], szRealInputs[i*2+1]); + + strcpy(szInputs[i*2+1], ""); + strcpy(szRealInputs[i*2+1], ""); + + bMov[i]=TRUE; + + } else if(strcmp(szInputs[i*2+1], "1")==0) { + //strcpy(szABCDInput[i], szInputs[i*2]); +#ifndef REVEL8N_PIXEL_SHADER_CHANGES + strcpy(szABCDInput[i], szRealInputs[i*2]); +#endif + + strcpy(szOps[i], "mov"); + + strcpy(szInputs[i*2+1], ""); + strcpy(szRealInputs[i*2+1], ""); + + bMov[i]=TRUE; + } + } + } + +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + OpType eOpTypes[3] = {OPTYPE_NOP, OPTYPE_NOP, OPTYPE_NOP}; + for (i = 0; i < 3; ++i) + { + if (strcmp(szOps[i], "mov") == 0) + eOpTypes[i] = OPTYPE_MOV; + else if (strcmp(szOps[i], "add") == 0) + eOpTypes[i] = OPTYPE_ADD; + else if (strcmp(szOps[i], "mul") == 0) + eOpTypes[i] = OPTYPE_MUL; + else if (strcmp(szOps[i], "dp3") == 0) + eOpTypes[i] = OPTYPE_DP3; + else if (strcmp(szOps[i], "cnd") == 0) + eOpTypes[i] = OPTYPE_CND; + else + eOpTypes[i] = OPTYPE_NOP; + } + + bool bHandled = false; + int iOffset = 0; + int iOpCount = 0; + if (szOps[2][0] && szOutputs[2][0] && szOutputs[2][0] != 'v') + { + if (!szOutputs[0][0] && + !szOutputs[1][0]) + { + if (szMod[0]) + { + EmuLog(LOG_LEVEL::WARNING, "Destination modifier present!"); + } + switch (eOpTypes[2]) + { + case OPTYPE_ADD: + { + if (eOpTypes[0] == OPTYPE_MOV && + eOpTypes[1] == OPTYPE_MOV) + { + iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, %s\n", + szMod, szOutputs[2], szRealInputs[0], szRealInputs[2]); + ++iOpCount; + bHandled = true; + } + else if (eOpTypes[0] == OPTYPE_MOV && + eOpTypes[1] == OPTYPE_MUL) + { + iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", + szMod, szOutputs[2], szRealInputs[2], szRealInputs[3], szRealInputs[0]); + bHandled = true; + ++iOpCount; + } + else if (eOpTypes[0] == OPTYPE_MUL && + eOpTypes[1] == OPTYPE_MOV) + { + iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", + szMod, szOutputs[2], szRealInputs[0], szRealInputs[1], szRealInputs[2]); + bHandled = true; + ++iOpCount; + } + else if (eOpTypes[0] == OPTYPE_MUL && + eOpTypes[1] == OPTYPE_MUL) + { + // nice, mul, mul, add can be converted to lrp + // lrp r0, t0, t1, c2 + // --> r0 = t0 * t1 + (1-t0) * c2 + // or r0 = c2 + t0 * (t1 - c2), but that would mean we have to mul in the ABCD op + // and that is not possible + + for(i=0; i<2; i++) + { + // To match the first option, the first input of the AB/CD op must inverted + BOOL bInvert[2] = {0, 0}; + if((szRealInputs[2*i][0] == '1') && (szRealInputs[2*i][1] == '-')) + //if((szInputs[2*i][0] == '1') && (szInputs[2*i][1] == '-')) + bInvert[0]=TRUE; + + if((szRealInputs[2*i+1][0] == '1') && (szRealInputs[2*i+1][1] == '-')) + //if((szInputs[2*i+1][0] == '1') && (szInputs[2*i+1][1] == '-')) + bInvert[1]=TRUE; + + //printf("szInputs[2*i]: %s\nszInputs[2*i+1]: %s\n", szInputs[2*i], szInputs[2*i+1]); + //printf("bInvert[0]: %d\nbInvert[1]: %d\n", bInvert[0], bInvert[1]); + + if((bInvert[0] || bInvert[1]) && (!(bInvert[0] && bInvert[1]))) + { + char szParam[3][10] = {0}; + char szRealParam0[10] = {0}; + if(bInvert[0]) + { + // copy over the not inverted param + strcpy(szParam[i+1], /*szInputs*/szRealInputs[2*i+1]); + + // and the inverted + strcpy(szParam[0], &szInputs[2*i][2]); + strcpy(szRealParam0, &szRealInputs[2*i][2]); + } + else if(bInvert[1]) + { + // copy over the not inverted param + strcpy(szParam[i+1], /*szInputs*/szRealInputs[2*i]); + + // and the inverted + strcpy(szParam[0], &szInputs[2*i+1][2]); + strcpy(szRealParam0, &szRealInputs[2*i+1][2]); + } + int iOtherOp = i == 0 ? 1 : 0; + + bHandled = true; + if (strcmp(szRealInputs[2*iOtherOp], szRealParam0/*szParam[0]*/)==0) + strcpy(szParam[iOtherOp+1], /*szInputs*/szRealInputs[2*iOtherOp+1]); + else if (strcmp(szRealInputs[2*iOtherOp+1], szRealParam0/*szParam[0]*/)==0) + strcpy(szParam[iOtherOp+1], /*szInputs*/szRealInputs[2*iOtherOp]); + else + bHandled = false; + if (bHandled) + { + // ok, we have it + iOffset += sprintf(szCommand, "lrp%s %s, %s, %s, %s\n", + szMod, szABCDOutput, szRealParam0/*szParam[0]*/, szParam[1], szParam[2]); + ++iOpCount; + break; + } + } + } + + if (!bHandled) + { + iOffset += sprintf(szCommand + iOffset, "mul r1, %s, %s\n", + szRealInputs[0], szRealInputs[1]); + ++iOpCount; + iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, r1\n", + szMod, szOutputs[2], szRealInputs[2], szRealInputs[3]); + ++iOpCount; + + bHandled = true; + } + } + } + break; + case OPTYPE_CND: + { + if (eOpTypes[0] == OPTYPE_MOV && + eOpTypes[1] == OPTYPE_MOV) + { + iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, %s\n", + szMod, szOutputs[2], szRealInputs[2], szRealInputs[0]); + ++iOpCount; + bHandled = true; + } + else if (eOpTypes[0] == OPTYPE_MUL && + eOpTypes[1] == OPTYPE_MUL) + { + if (szOutputs[2][0] != 'r') + { + EmuLog(LOG_LEVEL::WARNING, "Destination not temporary register!"); + } + // ab input + iOffset += sprintf(szCommand + iOffset, "mul%s r1, %s, %s\n", + szMod, szRealInputs[0], szRealInputs[1]); + ++iOpCount; + // cd input + iOffset += sprintf(szCommand + iOffset, "mul%s r0, %s, %s\n", + szMod, szRealInputs[2], szRealInputs[3]); + ++iOpCount; + // abcd output + iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, r0, r1\n", + szMod, szOutputs[2]); + ++iOpCount; + bHandled = true; + } + } + break; + } + if (!bHandled && strcmp(szOps[2], "add") == 0) + { + if ((strcmp(szOps[0], "mov")==0)) + { + if ((strcmp(szOps[1], "mul")==0)) + { + char szParam[10]="\0"; + + if(strcmp(szInputCD1, "-1")==0) + strcpy(szParam, szInputCD2); + else if(strcmp(szInputCD2, "-1")==0) + strcpy(szParam, szInputCD1); + + if(szParam[0] && szConstRegAB1[0] && szABCDOutput[0]) + { + iOffset += sprintf(szCommand, "sub%s %s, %s, %s\n", + szMod, szABCDOutput, szConstRegAB1, szParam); + bHandled = true; + ++iOpCount; + } +// else +// { +// iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", +// szMod, szOutputs[2], szRealInputs[2], szRealInputs[3], szRealInputs[0]); +// bHandled = true; +// ++iOpCount; +// } + } + } +// else if ((strcmp(szOps[0], "mul")==0)) +// { +// if ((strcmp(szOps[1], "mov")==0)) +// { +// iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", +// szMod, szOutputs[2], szRealInputs[0], szRealInputs[1], szRealInputs[2]); +// bHandled = true; +// ++iOpCount; +// } +// else if ((strcmp(szOps[1], "mul")==0)) +// { +// // nice, mul, mul, add can be converted to lrp +// // lrp r0, t0, t1, c2 +// // --> r0 = t0 * t1 + (1-t0) * c2 +// // or r0 = c2 + t0 * (t1 - c2), but that would mean we have to mul in the ABCD op +// // and that is not possible +// +// for(i=0; i<2; i++) +// { +// // To match the first option, the first input of the AB/CD op must inverted +// BOOL bInvert[2] = {0, 0}; +// if((szRealInputs[2*i][0] == '1') && (szRealInputs[2*i][1] == '-')) +// //if((szInputs[2*i][0] == '1') && (szInputs[2*i][1] == '-')) +// bInvert[0]=TRUE; +// +// if((szRealInputs[2*i+1][0] == '1') && (szRealInputs[2*i+1][1] == '-')) +// //if((szInputs[2*i+1][0] == '1') && (szInputs[2*i+1][1] == '-')) +// bInvert[1]=TRUE; +// +// //printf("szInputs[2*i]: %s\nszInputs[2*i+1]: %s\n", szInputs[2*i], szInputs[2*i+1]); +// //printf("bInvert[0]: %d\nbInvert[1]: %d\n", bInvert[0], bInvert[1]); +// +// if((bInvert[0] || bInvert[1]) && (!(bInvert[0] && bInvert[1]))) +// { +// char szParam[3][10]; +// char szRealParam0[10]; +// if(bInvert[0]) +// { +// // copy over the not inverted param +// strcpy(szParam[2], /*szInputs*/szRealInputs[2*i+1]); +// +// // and the inverted +// strcpy(szParam[0], &szInputs[2*i][2]); +// strcpy(szRealParam0, &szRealInputs[2*i][2]); +// } +// else if(bInvert[1]) +// { +// // copy over the not inverted param +// strcpy(szParam[2], /*szInputs*/szRealInputs[2*i]); +// +// // and the inverted +// strcpy(szParam[0], &szInputs[2*i+1][2]); +// strcpy(szRealParam0, &szRealInputs[2*i+1][2]); +// } +// int iOtherOp = i == 0 ? 1 : 0; +// +// bHandled = true; +// if (strcmp(szRealInputs[2*iOtherOp], szRealParam0/*szParam[0]*/)==0) +// strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp+1]); +// else if (strcmp(szRealInputs[2*iOtherOp+1], szRealParam0/*szParam[0]*/)==0) +// strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp]); +// else +// bHandled = false; +// if (bHandled) +// { +// // ok, we have it +// iOffset += sprintf(szCommand, "lrp%s %s, %s, %s, %s\n", +// szMod, szABCDOutput, szRealParam0/*szParam[0]*/, szParam[1], szParam[2]); +// ++iOpCount; +// break; +// } +// } +// } +// +// if (!bHandled) +// { +// iOffset += sprintf(szCommand + iOffset, "mul r1, %s, %s\n", +// szRealInputs[0], szRealInputs[1]); +// ++iOpCount; +// iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, r1\n", +// szMod, szOutputs[2], szRealInputs[2], szRealInputs[3]); +// ++iOpCount; +// +// bHandled = true; +// } +// } +// } + } + } + } + + if (!bHandled) + { + for (i = 0; i < 2; ++i) + { + if (szOps[i][0] && szOutputs[i][0] && szOutputs[i][0] != 'v') + { + ++iOpCount; + // copy output value to final input + strcpy(szABCDInput[i], szOutputs[i]); + // insert command + iOffset += sprintf(szCommand + iOffset, "%s%s %s, %s\n", szOps[i], szMod, szOutputs[i], szRealInputs[i * 2 + 0]); + + // if there are more parameters... + if (szRealInputs[i * 2 + 1][0]) + { + // backspace of the newline character + --iOffset; + // insert remaining parameters + iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[i * 2 + 1]); + } + bHandled = true; + } + } + +// if (szOutputs[2][0]) +// { +// if(!szOutputs[1][0]) +// strcpy(szOutputs[1], "r0"); +// if(!szOutputs[0][0]) +// strcpy(szOutputs[0], "r1"); +// } + + if (szOps[2][0] && szOutputs[2][0] && szOutputs[2][0] != 'v') + { + switch (eOpTypes[2]) + { + case OPTYPE_ADD: + { + if (szABCDInput[0][0] && + szABCDInput[1][0]) + { + iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, %s\n", + szMod, szOutputs[2], szABCDInput[0], szABCDInput[1]); + ++iOpCount; + bHandled = true; + } + else if (szABCDInput[0][0] && + !szABCDInput[1][0]) + { + switch (eOpTypes[1]) + { + case OPTYPE_MUL: + { + iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", + szMod, szOutputs[2], szRealInputs[2], szRealInputs[3], szABCDInput[0]); + ++iOpCount; + bHandled = true; + } + break; + case OPTYPE_DP3: + { + { + ++iOpCount; + // insert command + iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[1], szMod, szRealInputs[2]); + + // if there are more parameters... + if (szRealInputs[3][0]) + { + // backspace of the newline character + --iOffset; + // insert remaining parameters + iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[3]); + } + } + { + iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, r1\n", + szMod, szOutputs[2], szABCDInput[0]); + ++iOpCount; + bHandled = true; + } + } + break; + default: + break; + } + } + else if (!szABCDInput[0][0] && + szABCDInput[1][0]) + { + switch (eOpTypes[0]) + { + case OPTYPE_MUL: + { + iOffset += sprintf(szCommand + iOffset, "mad%s %s, %s, %s, %s\n", + szMod, szOutputs[2], szRealInputs[0], szRealInputs[1], szABCDInput[1]); + ++iOpCount; + bHandled = true; + } + break; + case OPTYPE_DP3: + { + { + ++iOpCount; + // insert command + iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[0], szMod, szRealInputs[0]); + + // if there are more parameters... + if (szRealInputs[1][0]) + { + // backspace of the newline character + --iOffset; + // insert remaining parameters + iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[1]); + } + } + { + iOffset += sprintf(szCommand + iOffset, "add%s %s, r1, %s\n", + szMod, szOutputs[2], szABCDInput[1]); + ++iOpCount; + bHandled = true; + } + } + break; + default: + break; + } + } + } + break; + case OPTYPE_CND: + { + if (szABCDInput[0][0] && + szABCDInput[1][0]) + { + iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, %s\n", + szMod, szOutputs[2], szABCDInput[1], szABCDInput[0]); + ++iOpCount; + bHandled = true; + } + else if (szABCDInput[0][0] && + !szABCDInput[1][0]) + { + { + ++iOpCount; + // insert command + iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[1], szMod, szRealInputs[2]); + + // if there are more parameters... + if (szRealInputs[3][0]) + { + // backspace of the newline character + --iOffset; + // insert remaining parameters + iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[3]); + } + } + { + iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, r1, %s\n", + szMod, szOutputs[2], szABCDInput[0]); + ++iOpCount; + bHandled = true; + } + } + else if (!szABCDInput[0][0] && + szABCDInput[1][0]) + { + { + ++iOpCount; + // insert command + iOffset += sprintf(szCommand + iOffset, "%s%s r1, %s\n", szOps[0], szMod, szRealInputs[0]); + + // if there are more parameters... + if (szRealInputs[1][0]) + { + // backspace of the newline character + --iOffset; + // insert remaining parameters + iOffset += sprintf(szCommand + iOffset, ", %s\n", szRealInputs[1]); + } + } + { + iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, r1\n", + szMod, szOutputs[2], szABCDInput[1]); + ++iOpCount; + bHandled = true; + } + } + } + break; + } + if (!bHandled) + { + EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); + } +// if (strcmp(szOps[2], "add") == 0) +// { +// if (szABCDInput[0][0] && +// szABCDInput[1][0]) +// { +// iOffset += sprintf(szCommand + iOffset, "add%s %s, %s, %s\n", +// szMod, szOutputs[2], szABCDInput[1], szABCDInput[0]); +// ++iOpCount; +// bHandled = true; +// } +// else +// { +// EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); +// } +// } +// else if (strcmp(szOps[2], "cnd") == 0) +// { +// if (szABCDInput[0][0] && +// szABCDInput[1][0]) +// { +// iOffset += sprintf(szCommand + iOffset, "cnd%s %s, r0.a, %s, %s\n", +// szMod, szOutputs[2], szABCDInput[1], szABCDInput[0]); +// ++iOpCount; +// bHandled = true; +// } +// else +// { +// EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); +// } +// } +// else +// { +// EmuLog(LOG_LEVEL::WARNING, "Unhandled pixel shader instruction!"); +// } + } + } + + if(szCommand[0]) + printf("new command:\n%s\n", szCommand); + return (bHandled && (iOpCount == 1)) ? (TRUE) : (FALSE); +#endif + + if( + (strcmp(szOp, "mul")==0) && + (strcmp(szOp1, "mov")==0) && //bMov[1] && + (strcmp(szOp2, "add")==0) && + szABCDOutput[0]) + { + sprintf(szCommand, "mad%s %s, %s, %s, %s\n", + szMod, szABCDOutput, + /*szInput*/szConstRegAB1, + /*szInput*/szConstRegAB2, + /*szInput*/szConstRegCD1 /*because it's a mov now*/); + } + else if( + (strcmp(szOp, "mul")==0) && + (strcmp(szOp1, "mul")==0) && + (strcmp(szOp2, "add")==0) && + szABCDOutput[0]) // TODO: check that strange lrp/ABCDOutput[0]=0 case + { + // nice, mul, mul, add can be converted to lrp + // lrp r0, t0, t1, c2 + // --> r0 = t0 * t1 + (1-t0) * c2 + // or r0 = c2 + t0 * (t1 - c2), but that would mean we have to mul in the ABCD op + // and that is not possible + + for(i=0; i<2; i++) + { + // To match the first option, the first input of the AB/CD op must inverted + BOOL bInvert[2] = {0, 0}; + if((szInputs[2*i][0] == '1') && (szInputs[2*i][1] == '-')) + bInvert[0]=TRUE; + + if((szInputs[2*i+1][0] == '1') && (szInputs[2*i+1][1] == '-')) + bInvert[1]=TRUE; + + //printf("szInputs[2*i]: %s\nszInputs[2*i+1]: %s\n", szInputs[2*i], szInputs[2*i+1]); + //printf("bInvert[0]: %d\nbInvert[1]: %d\n", bInvert[0], bInvert[1]); + + if((bInvert[0] || bInvert[1]) && (!(bInvert[0] && bInvert[1]))) + { + char szParam[3][10]; + char szRealParam0[10]; + if(bInvert[0]) + { + // copy over the not inverted param + strcpy(szParam[2], /*szInputs*/szRealInputs[2*i+1]); + + // and the inverted + strcpy(szParam[0], &szInputs[2*i][2]); + strcpy(szRealParam0, &szRealInputs[2*i][2]); + } + else if(bInvert[1]) + { + // copy over the not inverted param + strcpy(szParam[2], /*szInputs*/szRealInputs[2*i]); + + // and the inverted + strcpy(szParam[0], &szInputs[2*i+1][2]); + strcpy(szRealParam0, &szRealInputs[2*i+1][2]); + } + int iOtherOp = i == 0 ? 1 : 0; + + if(strcmp(szInputs[2*iOtherOp], szParam[0])==0) + strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp+1]); + else + strcpy(szParam[1], /*szInputs*/szRealInputs[2*iOtherOp]); + // ok, we have it + sprintf(szCommand, "lrp%s %s, %s, %s, %s\n", + szMod, szABCDOutput, szRealParam0/*szParam[0]*/, szParam[1], szParam[2]); + + break; + } + } + } else if(strcmp(szOp2, "cnd")==0) { +#ifdef REVEL8N_PIXEL_SHADER_CHANGES + iOffset = 0; + i = 0; + for (i = 0; i < 2; ++i) + { + if (strcmp(szOps[i], "mul")==0) + { + strcpy(szABCDInput[i], szOutputs[i]); + iOffset += sprintf(szCommand + iOffset, "mul %s, %s, %s\n", szOutputs[i], szRealInputs[i * 2 + 0], szRealInputs[i * 2 + 1]); + } + } + sprintf(szCommand + iOffset, "cnd%s %s, %s, %s, %s\n", + szMod, szABCDOutput, "r0.a", szABCDInput[1], szABCDInput[0]); +#else + sprintf(szCommand, "cnd%s %s, %s, %s, %s\n", + szMod, szABCDOutput, "r0.a", szABCDInput[1], szABCDInput[0]); +#endif + + bMov[1]=0; + bMov[0]=0; + } else if( + (strcmp(szOp, "mov")==0) && + (strcmp(szOp1, "mul")==0) && + (strcmp(szOp2, "add")==0)) + { + char szParam[10]="\0"; + + if(strcmp(szInputCD1, "-1")==0) + strcpy(szParam, szInputCD2); + else if(strcmp(szInputCD2, "-1")==0) + strcpy(szParam, szInputCD1); + + if(szParam[0] && szConstRegAB1[0] && szABCDOutput[0]) + { + sprintf(szCommand, "sub%s %s, %s, %s\n", + szMod, szABCDOutput, szConstRegAB1, szParam); + } + + } +//do_operation_with_new_input: + + if(bMov[0] && bMov[1] && szABCDOutput[0]) { + sprintf(szCommand, "%s%s %s, %s, %s\n", szOp2, szMod, szABCDOutput, szABCDInput[0], szABCDInput[1]); + } + + if(szCommand[0]) + printf("new command: %s", szCommand); + return TRUE; +} + +float fConstants[20] = {0.0f}; +int iConstants[20] = {0}; +int iConstCount=0; + +inline void ClearConstRegVars() +{ + iConstCount=0; + memset(fConstants, 0x00, 20*sizeof(float)); + memset(iConstants, 0x00, 20*sizeof(int)); +} + +inline void CorrectConstToReg(char *szConst, int *pPSC0, int *pPSC1) +{ + printf("Looking for %s\n", szConst); + float fConst = (float)atof(szConst); + + // check whether we already saved it + int i=0; + for(i=0; iPSAlphaInputs[0], pPSDef->PSAlphaInputs[1], pPSDef->PSAlphaInputs[2], pPSDef->PSAlphaInputs[3], + pPSDef->PSAlphaInputs[4], pPSDef->PSAlphaInputs[5], pPSDef->PSAlphaInputs[6], pPSDef->PSAlphaInputs[7], + pPSDef->PSFinalCombinerInputsABCD, + pPSDef->PSFinalCombinerInputsEFG, + pPSDef->PSConstant0[0], pPSDef->PSConstant0[1], pPSDef->PSConstant0[2], pPSDef->PSConstant0[3], + pPSDef->PSConstant0[4], pPSDef->PSConstant0[5], pPSDef->PSConstant0[6], pPSDef->PSConstant0[7], + pPSDef->PSConstant1[0], pPSDef->PSConstant1[1], pPSDef->PSConstant1[2], pPSDef->PSConstant1[3], + pPSDef->PSConstant1[4], pPSDef->PSConstant1[5], pPSDef->PSConstant1[6], pPSDef->PSConstant1[7], + pPSDef->PSAlphaOutputs[0], pPSDef->PSAlphaOutputs[1], pPSDef->PSAlphaOutputs[2], pPSDef->PSAlphaOutputs[3], + pPSDef->PSAlphaOutputs[4], pPSDef->PSAlphaOutputs[5], pPSDef->PSAlphaOutputs[6], pPSDef->PSAlphaOutputs[7], + pPSDef->PSRGBInputs[0], pPSDef->PSRGBInputs[1], pPSDef->PSRGBInputs[2], pPSDef->PSRGBInputs[3], + pPSDef->PSRGBInputs[4], pPSDef->PSRGBInputs[5], pPSDef->PSRGBInputs[6], pPSDef->PSRGBInputs[7], + pPSDef->PSCompareMode, + pPSDef->PSFinalCombinerConstant0, + pPSDef->PSFinalCombinerConstant1, + pPSDef->PSRGBOutputs[0], pPSDef->PSRGBOutputs[1], pPSDef->PSRGBOutputs[2], pPSDef->PSRGBOutputs[3], + pPSDef->PSRGBOutputs[4], pPSDef->PSRGBOutputs[5], pPSDef->PSRGBOutputs[6], pPSDef->PSRGBOutputs[7], + pPSDef->PSCombinerCount, + XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES), /* pPSDef->PSTextureModes is stored in a different place than pPSDef*/ + pPSDef->PSDotMapping, + pPSDef->PSInputTexture, + pPSDef->PSC0Mapping, + pPSDef->PSC1Mapping, + pPSDef->PSFinalCombinerConstants ); + if (pszCode) + { + fprintf(out, "\n\n%s\n", pszCode); + } + + fclose( out ); + } +} + +// print relevant contents to the debug console +void PrintPixelShaderDefContents(XTL::X_D3DPIXELSHADERDEF* pPSDef ) +{ + // Show the contents to the user + if( pPSDef ) + { + DbgPshPrintf( "\n-----PixelShader Def Contents-----\n" ); + + if(XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES)) + { + DWORD dwPSTexMode0 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 0 ) & 0x1F; + DWORD dwPSTexMode1 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 5 ) & 0x1F; + DWORD dwPSTexMode2 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 10 ) & 0x1F; + DWORD dwPSTexMode3 = (XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSTEXTUREMODES) >> 15 ) & 0x1F; + + DbgPshPrintf( "PSTextureModes ->\n" ); + DbgPshPrintf( "Stage 0: %s\n", PS_TextureModesStr[dwPSTexMode0] ); + DbgPshPrintf( "Stage 1: %s\n", PS_TextureModesStr[dwPSTexMode1] ); + DbgPshPrintf( "Stage 2: %s\n", PS_TextureModesStr[dwPSTexMode2] ); + DbgPshPrintf( "Stage 3: %s\n", PS_TextureModesStr[dwPSTexMode3] ); + } + + if( pPSDef->PSDotMapping ) + { + DWORD dwPSDMStage1 = ( pPSDef->PSDotMapping >> 0 ) & 0x7; + DWORD dwPSDMStage2 = ( pPSDef->PSDotMapping >> 4 ) & 0x7; + DWORD dwPSDMStage3 = ( pPSDef->PSDotMapping >> 8 ) & 0x7; + + DbgPshPrintf( "PSDotMapping ->\n" ); + DbgPshPrintf( "Stage 1: %s\n", PS_DotMappingStr[dwPSDMStage1] ); + DbgPshPrintf( "Stage 2: %s\n", PS_DotMappingStr[dwPSDMStage2] ); + DbgPshPrintf( "Stage 3: %s\n", PS_DotMappingStr[dwPSDMStage3] ); + } + + if( pPSDef->PSCompareMode ) + { + DWORD dwPSCMStage0 = ( pPSDef->PSCompareMode >> 0 ) & 0xF; + DWORD dwPSCMStage1 = ( pPSDef->PSCompareMode >> 4 ) & 0xF; + DWORD dwPSCMStage2 = ( pPSDef->PSCompareMode >> 8 ) & 0xF; + DWORD dwPSCMStage3 = ( pPSDef->PSCompareMode >> 12 ) & 0xF; + + DbgPshPrintf( "PSCompareMode ->\n" ); + DbgPshPrintf( "Stage 0: %s\n", PS_TextureModesStr[dwPSCMStage0 == 0 ? 0 : 1] ); + DbgPshPrintf( "Stage 1: %s\n", PS_TextureModesStr[dwPSCMStage1 == 0 ? 2 : 3] ); + DbgPshPrintf( "Stage 2: %s\n", PS_TextureModesStr[dwPSCMStage2 == 0 ? 4 : 5] ); + DbgPshPrintf( "Stage 3: %s\n", PS_TextureModesStr[dwPSCMStage3 == 0 ? 6 : 7] ); + } + + if( pPSDef->PSInputTexture ) + { + DWORD dwPSITStage2 = ( pPSDef->PSInputTexture >> 16 ) & 0x1; + DWORD dwPSITStage3 = ( pPSDef->PSInputTexture >> 20 ) & 0x3; + + DbgPshPrintf( "PSInputTexture ->\n" ); + DbgPshPrintf( "Stage 2: %s\n", PS_TextureModesStr[dwPSITStage2] ); + DbgPshPrintf( "Stage 3: %s\n", PS_TextureModesStr[dwPSITStage3] ); + } + + if( pPSDef->PSCombinerCount ) + { + DWORD dwPSCCNumCombiners = ( pPSDef->PSCombinerCount >> 0 ) & 0xF; + DWORD dwPSCCMux = ( pPSDef->PSCombinerCount >> 8 ) & 0x1; + DWORD dwPSCCC0 = ( pPSDef->PSCombinerCount >> 12 ) & 0x1; + DWORD dwPSCCC1 = ( pPSDef->PSCombinerCount >> 16 ) & 0x1; + + DbgPshPrintf( "PSCombinerCount ->\n" ); + DbgPshPrintf( "Combiners: %d\n", dwPSCCNumCombiners ); + DbgPshPrintf( "Mux: %s\n", PS_CombinerCountFlagsStr[dwPSCCMux] ); + DbgPshPrintf( "C0: %s\n", PS_CombinerCountFlagsStr[dwPSCCC0 == 0 ? 2 : 3] ); + DbgPshPrintf( "C1: %s\n", PS_CombinerCountFlagsStr[dwPSCCC1 == 0 ? 4 : 5] ); + } + + /*for( int i = 0; i > 7; i++ ) + { + if( pPSDef->PSRGBInputs[i] ) + {*/ + } +} diff --git a/src/core/hle/D3D8/XbPixelShader.h b/src/core/hle/D3D8/XbPixelShader.h index 856f96f03..bbed4ee7b 100644 --- a/src/core/hle/D3D8/XbPixelShader.h +++ b/src/core/hle/D3D8/XbPixelShader.h @@ -1,40 +1,40 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef XBPIXELSHADER_H -#define XBPIXELSHADER_H - -#include "Cxbx.h" - -#include "core\hle\D3D8\XbD3D8Types.h" - -// dump pixel shader definition to file -void DumpPixelShaderDefToFile( XTL::X_D3DPIXELSHADERDEF* pPSDef, const char* pszCode ); -// print relevant contents to the debug console -void PrintPixelShaderDefContents(XTL::X_D3DPIXELSHADERDEF* pDSDef ); - -// PatrickvL's Dxbx pixel shader translation -VOID DxbxUpdateActivePixelShader(); // NOPATCH - -#endif // PIXELSHADER_H +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef XBPIXELSHADER_H +#define XBPIXELSHADER_H + +#include "Cxbx.h" + +#include "core\hle\D3D8\XbD3D8Types.h" + +// dump pixel shader definition to file +void DumpPixelShaderDefToFile( XTL::X_D3DPIXELSHADERDEF* pPSDef, const char* pszCode ); +// print relevant contents to the debug console +void PrintPixelShaderDefContents(XTL::X_D3DPIXELSHADERDEF* pDSDef ); + +// PatrickvL's Dxbx pixel shader translation +VOID DxbxUpdateActivePixelShader(); // NOPATCH + +#endif // PIXELSHADER_H diff --git a/src/core/hle/D3D8/XbPushBuffer.cpp b/src/core/hle/D3D8/XbPushBuffer.cpp index adcee89af..bfa05f66c 100644 --- a/src/core/hle/D3D8/XbPushBuffer.cpp +++ b/src/core/hle/D3D8/XbPushBuffer.cpp @@ -31,9 +31,9 @@ #include "core\kernel\support\Emu.h" #include "core\hle\D3D8\XbD3D8Types.h" // For X_D3DFORMAT -#include "core\hle\D3D8\ResourceTracker.h" +#include "core\hle\D3D8\ResourceTracker.h" #include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle -#include "core\hle\D3D8\XbPushBuffer.h" +#include "core\hle\D3D8\XbPushBuffer.h" #include "core\hle\D3D8\XbConvert.h" #include "devices/video/nv2a.h" // For g_NV2A, PGRAPHState #include "devices/video/nv2a_int.h" // For NV** defines diff --git a/src/core/hle/D3D8/XbPushBuffer.h b/src/core/hle/D3D8/XbPushBuffer.h index ca2ac5111..9680909c7 100644 --- a/src/core/hle/D3D8/XbPushBuffer.h +++ b/src/core/hle/D3D8/XbPushBuffer.h @@ -24,8 +24,8 @@ // ****************************************************************** #ifndef XBPUSHBUFFER_H #define XBPUSHBUFFER_H - -#include "core/hle/D3D8/XbVertexBuffer.h" // for CxbxDrawContext + +#include "core/hle/D3D8/XbVertexBuffer.h" // for CxbxDrawContext extern int DxbxFVF_GetNumberOfTextureCoordinates(DWORD dwFVF, int aTextureIndex); extern UINT DxbxFVFToVertexSizeInBytes(DWORD dwFVF, BOOL bIncludeTextures); @@ -45,4 +45,4 @@ extern void EmuExecutePushBufferRaw uint32_t uSizeInBytes ); -#endif +#endif diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index a3fdc1b69..fcefb05d5 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -32,10 +32,10 @@ #include "common\util\hasher.h" #include "core\kernel\support\Emu.h" #include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice -#include "core\hle\D3D8\Direct3D9\WalkIndexBuffer.h" // for WalkIndexBuffer -#include "core\hle\D3D8\ResourceTracker.h" +#include "core\hle\D3D8\Direct3D9\WalkIndexBuffer.h" // for WalkIndexBuffer +#include "core\hle\D3D8\ResourceTracker.h" #include "core\hle\D3D8\XbPushBuffer.h" // for DxbxFVF_GetNumberOfTextureCoordinates -#include "core\hle\D3D8\XbVertexBuffer.h" +#include "core\hle\D3D8\XbVertexBuffer.h" #include "core\hle\D3D8\XbConvert.h" #include @@ -60,13 +60,13 @@ extern DWORD g_dwPrimPerFrame = 0; XTL::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS] = { 0 }; // Note : .Offset member is never set (so always 0) extern XTL::X_D3DSurface* g_pXbox_RenderTarget; -extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; -extern XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType; +extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; +extern XTL::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType; void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource); bool GetHostRenderTargetDimensions(DWORD* pHostWidth, DWORD* pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr); uint32_t GetPixelContainerWidth(XTL::X_D3DPixelContainer* pPixelContainer); -uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer* pPixelContainer); +uint32_t GetPixelContainerHeight(XTL::X_D3DPixelContainer* pPixelContainer); void ApplyXboxMultiSampleOffsetAndScale(float& x, float& y); void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT uiStream) const @@ -620,10 +620,10 @@ void CxbxVertexBufferConverter::ConvertStream pHostVertexAsFloat[3] = pXboxVertexAsFloat[2]; break; } - case XTL::X_D3DVSDT_NONE: { // 0x02: - // Test-case : WWE RAW2 + case XTL::X_D3DVSDT_NONE: { // 0x02: + // Test-case : WWE RAW2 // Test-case : PetitCopter - LOG_TEST_CASE("X_D3DVSDT_NONE"); + LOG_TEST_CASE("X_D3DVSDT_NONE"); // No host element data (but Xbox size can be above zero, when used for X_D3DVSD_MASK_SKIP* break; } @@ -689,8 +689,8 @@ void CxbxVertexBufferConverter::ConvertStream // Transforming always breaks render to non-upscaled textures: Only surfaces are upscaled, intentionally so if (bNeedRHWTransform) { pVertexDataAsFloat[0] *= g_RenderScaleFactor; - pVertexDataAsFloat[1] *= g_RenderScaleFactor; - + pVertexDataAsFloat[1] *= g_RenderScaleFactor; + ApplyXboxMultiSampleOffsetAndScale(pVertexDataAsFloat[0], pVertexDataAsFloat[1]); } @@ -777,22 +777,22 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext) m_pCxbxVertexDeclaration = &(GetCxbxVertexShader(g_Xbox_VertexShader_Handle)->Declaration); } - // If we are drawing from an offset, we know that the vertex count must have + // If we are drawing from an offset, we know that the vertex count must have // 'offset' vertices before the first drawn vertices - pDrawContext->VerticesInBuffer = pDrawContext->dwStartVertex + pDrawContext->dwVertexCount; - // When this is an indexed draw, take the index buffer into account - if (pDrawContext->pXboxIndexData) { - // Is the highest index in this buffer not set yet? - if (pDrawContext->HighIndex == 0) { - // TODO : Instead of calling WalkIndexBuffer here, set LowIndex and HighIndex + pDrawContext->VerticesInBuffer = pDrawContext->dwStartVertex + pDrawContext->dwVertexCount; + // When this is an indexed draw, take the index buffer into account + if (pDrawContext->pXboxIndexData) { + // Is the highest index in this buffer not set yet? + if (pDrawContext->HighIndex == 0) { + // TODO : Instead of calling WalkIndexBuffer here, set LowIndex and HighIndex // in all callers that end up here (since they might be able to avoid the call) - LOG_TEST_CASE("HighIndex == 0"); // TODO : If this is never hit, replace entire block by assert(pDrawContext->HighIndex > 0); - WalkIndexBuffer(pDrawContext->LowIndex, pDrawContext->HighIndex, pDrawContext->pXboxIndexData, pDrawContext->dwVertexCount); - } - // Convert highest index (including the base offset) into a count - DWORD dwHighestVertexCount = pDrawContext->dwBaseVertexIndex + pDrawContext->HighIndex + 1; - // Use the biggest vertex count that can be reached - if (pDrawContext->VerticesInBuffer < dwHighestVertexCount) + LOG_TEST_CASE("HighIndex == 0"); // TODO : If this is never hit, replace entire block by assert(pDrawContext->HighIndex > 0); + WalkIndexBuffer(pDrawContext->LowIndex, pDrawContext->HighIndex, pDrawContext->pXboxIndexData, pDrawContext->dwVertexCount); + } + // Convert highest index (including the base offset) into a count + DWORD dwHighestVertexCount = pDrawContext->dwBaseVertexIndex + pDrawContext->HighIndex + 1; + // Use the biggest vertex count that can be reached + if (pDrawContext->VerticesInBuffer < dwHighestVertexCount) pDrawContext->VerticesInBuffer = dwHighestVertexCount; } diff --git a/src/core/hle/D3D8/XbVertexBuffer.h b/src/core/hle/D3D8/XbVertexBuffer.h index 683a05558..61931cdd6 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.h +++ b/src/core/hle/D3D8/XbVertexBuffer.h @@ -24,9 +24,9 @@ // ****************************************************************** #ifndef XBVERTEXBUFFER_H #define XBVERTEXBUFFER_H - -#include -#include + +#include +#include #include "Cxbx.h" @@ -37,8 +37,8 @@ typedef struct _CxbxDrawContext IN XTL::X_D3DPRIMITIVETYPE XboxPrimitiveType; IN DWORD dwVertexCount; IN DWORD dwStartVertex; // Only D3DDevice_DrawVertices sets this (potentially higher than default 0) - IN PWORD pXboxIndexData; // Set by D3DDevice_DrawIndexedVertices, D3DDevice_DrawIndexedVerticesUP and HLE_draw_inline_elements - IN DWORD dwBaseVertexIndex; // Set to g_Xbox_BaseVertexIndex in D3DDevice_DrawIndexedVertices + IN PWORD pXboxIndexData; // Set by D3DDevice_DrawIndexedVertices, D3DDevice_DrawIndexedVerticesUP and HLE_draw_inline_elements + IN DWORD dwBaseVertexIndex; // Set to g_Xbox_BaseVertexIndex in D3DDevice_DrawIndexedVertices IN INDEX16 LowIndex, HighIndex; // Set when pXboxIndexData is set IN size_t VerticesInBuffer; // Set by CxbxVertexBufferConverter::Apply // Data if Draw...UP call diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index 42afa1075..749fbb5de 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -1,1730 +1,1730 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2004 Aaron Robinson -// * Kingofc -// * -// * All rights reserved -// * -// ****************************************************************** -#define LOG_PREFIX CXBXR_MODULE::VTXSH - -//#define _DEBUG_TRACK_VS - -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\support\Emu.h" -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle -#include "core\hle\D3D8\Direct3D9\VertexShaderSource.h" // For g_VertexShaderSource -#include "core\hle\D3D8\XbVertexShader.h" -#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT -#include "common\Logging.h" // For LOG_INIT - -#include "XbD3D8Types.h" // For X_D3DVSDE_* -#include -#include -#include -#include - -#define DbgVshPrintf \ - LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \ - if(g_bPrintfOn) printf - -// **************************************************************************** -// * Vertex shader function recompiler -// **************************************************************************** - -class XboxVertexShaderDecoder -{ -private: - // Xbox Vertex SHader microcode types - - enum VSH_OUTPUT_TYPE { - OUTPUT_C = 0, - OUTPUT_O - }; - - enum VSH_OUTPUT_MUX { - OMUX_MAC = 0, - OMUX_ILU - }; - - // Host intermediate vertex shader types - - enum VSH_FIELD_NAME { - FLD_ILU = 0, - FLD_MAC, - FLD_CONST, - FLD_V, - // Input A - FLD_A_NEG, - FLD_A_SWZ_X, - FLD_A_SWZ_Y, - FLD_A_SWZ_Z, - FLD_A_SWZ_W, - FLD_A_R, - FLD_A_MUX, - // Input B - FLD_B_NEG, - FLD_B_SWZ_X, - FLD_B_SWZ_Y, - FLD_B_SWZ_Z, - FLD_B_SWZ_W, - FLD_B_R, - FLD_B_MUX, - // Input C - FLD_C_NEG, - FLD_C_SWZ_X, - FLD_C_SWZ_Y, - FLD_C_SWZ_Z, - FLD_C_SWZ_W, - FLD_C_R_HIGH, - FLD_C_R_LOW, - FLD_C_MUX, - // Output - FLD_OUT_MAC_MASK, - FLD_OUT_R, - FLD_OUT_ILU_MASK, - FLD_OUT_O_MASK, - FLD_OUT_ORB, - FLD_OUT_ADDRESS, - FLD_OUT_MUX, - // Relative addressing - FLD_A0X, - // Final instruction - FLD_FINAL - }; - - // Retrieves a number of bits in the instruction token - static inline uint32_t VshGetFromToken( - uint32_t* pShaderToken, - uint8_t SubToken, - uint8_t StartBit, - uint8_t BitLength) - { - return (pShaderToken[SubToken] >> StartBit) & ~(0xFFFFFFFF << BitLength); - } - - static uint8_t VshGetField( - uint32_t* pShaderToken, - VSH_FIELD_NAME FieldName) - { - // Used for xvu spec definition - static const struct { - uint8_t SubToken; - uint8_t StartBit; - uint8_t BitLength; - } FieldMapping[/*VSH_FIELD_NAME*/] = { - // SubToken BitPos BitSize - { 1, 25, 3 }, // FLD_ILU, - { 1, 21, 4 }, // FLD_MAC, - { 1, 13, 8 }, // FLD_CONST, - { 1, 9, 4 }, // FLD_V, - // Input A - { 1, 8, 1 }, // FLD_A_NEG, - { 1, 6, 2 }, // FLD_A_SWZ_X, - { 1, 4, 2 }, // FLD_A_SWZ_Y, - { 1, 2, 2 }, // FLD_A_SWZ_Z, - { 1, 0, 2 }, // FLD_A_SWZ_W, - { 2, 28, 4 }, // FLD_A_R, - { 2, 26, 2 }, // FLD_A_MUX, - // Input B - { 2, 25, 1 }, // FLD_B_NEG, - { 2, 23, 2 }, // FLD_B_SWZ_X, - { 2, 21, 2 }, // FLD_B_SWZ_Y, - { 2, 19, 2 }, // FLD_B_SWZ_Z, - { 2, 17, 2 }, // FLD_B_SWZ_W, - { 2, 13, 4 }, // FLD_B_R, - { 2, 11, 2 }, // FLD_B_MUX, - // Input C - { 2, 10, 1 }, // FLD_C_NEG, - { 2, 8, 2 }, // FLD_C_SWZ_X, - { 2, 6, 2 }, // FLD_C_SWZ_Y, - { 2, 4, 2 }, // FLD_C_SWZ_Z, - { 2, 2, 2 }, // FLD_C_SWZ_W, - { 2, 0, 2 }, // FLD_C_R_HIGH, - { 3, 30, 2 }, // FLD_C_R_LOW, - { 3, 28, 2 }, // FLD_C_MUX, - // Output - { 3, 24, 4 }, // FLD_OUT_MAC_MASK, - { 3, 20, 4 }, // FLD_OUT_R, - { 3, 16, 4 }, // FLD_OUT_ILU_MASK, - { 3, 12, 4 }, // FLD_OUT_O_MASK, - { 3, 11, 1 }, // FLD_OUT_ORB, - { 3, 3, 8 }, // FLD_OUT_ADDRESS, - { 3, 2, 1 }, // FLD_OUT_MUX, - // Relative addressing - { 3, 1, 1 }, // FLD_A0X, - // Final instruction - { 3, 0, 1 } // FLD_FINAL, - }; - - return (uint8_t)(VshGetFromToken(pShaderToken, - FieldMapping[FieldName].SubToken, - FieldMapping[FieldName].StartBit, - FieldMapping[FieldName].BitLength)); - } - - // Converts the C register address to disassembly format - static inline int16_t ConvertCRegister(const int16_t CReg) - { - return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31); - } - - static void VshConvertIntermediateParam(VSH_IMD_PARAMETER& Param, - uint32_t* pShaderToken, - VSH_FIELD_NAME FLD_MUX, - VSH_FIELD_NAME FLD_NEG, - uint16_t R, - uint16_t V, - uint16_t C) - { - Param.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX); - switch (Param.ParameterType) { - case PARAM_R: - Param.Address = R; - break; - case PARAM_V: - Param.Address = V; - break; - case PARAM_C: - Param.Address = C; - break; - default: - LOG_TEST_CASE("parameter type unknown"); - } - - int d = FLD_NEG - FLD_A_NEG; - Param.Neg = VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_NEG)) > 0; - Param.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_X)); - Param.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Y)); - Param.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Z)); - Param.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_W)); - } - - void VshAddIntermediateInstruction( - uint32_t* pShaderToken, - IntermediateVertexShader* pShader, - VSH_MAC MAC, - VSH_ILU ILU, - VSH_IMD_OUTPUT_TYPE output_type, - int16_t output_address, - int8_t output_mask) - { - // Is the output mask set? - if (output_mask == 0) { - return; - } - - if (pShader->Instructions.size() >= VSH_MAX_INTERMEDIATE_COUNT) { - CxbxKrnlCleanup("Shader exceeds conversion buffer!"); - } - - VSH_INTERMEDIATE_FORMAT intermediate; - intermediate.MAC = MAC; - intermediate.ILU = ILU; - intermediate.Output.Type = output_type; - intermediate.Output.Address = output_address; - intermediate.Output.Mask = output_mask; - // Get a0.x indirect constant addressing - intermediate.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0; // Applies to PARAM_C parameter reads - - int16_t R; - int16_t V = VshGetField(pShaderToken, FLD_V); - int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); - intermediate.ParamCount = 0; - if (MAC >= MAC_MOV) { - // Get parameter A - R = VshGetField(pShaderToken, FLD_A_R); - VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_A_MUX, FLD_A_NEG, R, V, C); - } - - if ((MAC == MAC_MUL) || ((MAC >= MAC_MAD) && (MAC <= MAC_SGE))) { - // Get parameter B - R = VshGetField(pShaderToken, FLD_B_R); - VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_B_MUX, FLD_B_NEG, R, V, C); - } - - if ((ILU >= ILU_MOV) || (MAC == MAC_ADD) || (MAC == MAC_MAD)) { - // Get parameter C - R = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW); - VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_C_MUX, FLD_C_NEG, R, V, C); - } - - // Add the instruction to the shader - pShader->Instructions.push_back(intermediate); - } - -public: - bool VshConvertToIntermediate(uint32_t* pShaderToken, IntermediateVertexShader* pShader) - { - // First get the instruction(s). - VSH_ILU ILU = (VSH_ILU)VshGetField(pShaderToken, FLD_ILU); - VSH_MAC MAC = (VSH_MAC)VshGetField(pShaderToken, FLD_MAC); - if (MAC > MAC_ARL) LOG_TEST_CASE("Unknown MAC"); - - // Output register - VSH_OUTPUT_MUX OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX); - int16_t OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS); - VSH_IMD_OUTPUT_TYPE OutputType; - if ((VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB) == OUTPUT_C) { - OutputType = IMD_OUTPUT_C; - OutputAddress = ConvertCRegister(OutputAddress); - } else { // OUTPUT_O: - OutputType = IMD_OUTPUT_O; - OutputAddress = OutputAddress & 0xF; - } - - // MAC,ILU output R register - int16_t RAddress = VshGetField(pShaderToken, FLD_OUT_R); - - // Test for paired opcodes - bool bIsPaired = (MAC != MAC_NOP) && (ILU != ILU_NOP); - - // Check if there's a MAC opcode - if (MAC > MAC_NOP && MAC <= MAC_ARL) { - if (bIsPaired && RAddress == 1) { - // Ignore paired MAC opcodes that write to R1 - } else { - if (MAC == MAC_ARL) { - VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_A0X, 0, MASK_X); - } else { - VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_R, RAddress, VshGetField(pShaderToken, FLD_OUT_MAC_MASK)); - } - } - - // Check if we must add a muxed MAC opcode as well - if (OutputMux == OMUX_MAC) { - VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK)); - } - } - - // Check if there's an ILU opcode - if (ILU != ILU_NOP) { - // Paired ILU opcodes will only write to R1 - VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, IMD_OUTPUT_R, bIsPaired ? 1 : RAddress, VshGetField(pShaderToken, FLD_OUT_ILU_MASK)); - // Check if we must add a muxed ILU opcode as well - if (OutputMux == OMUX_ILU) { - VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK)); - } - } - - return VshGetField(pShaderToken, FLD_FINAL) == 0; - } - -}; - -// **************************************************************************** -// * Vertex shader declaration recompiler -// **************************************************************************** - -extern D3DCAPS g_D3DCaps; - -class XboxVertexDeclarationConverter -{ -protected: - // Internal variables - CxbxVertexDeclaration* pVertexDeclarationToSet; - CxbxVertexShaderStreamInfo* pCurrentVertexShaderStreamInfo = nullptr; - bool IsFixedFunction; - D3DVERTEXELEMENT* pRecompiled; - std::array RegVIsPresentInDeclaration; - -public: - // Output - DWORD XboxDeclarationCount; - -private: - #define D3DDECLUSAGE_UNSUPPORTED ((D3DDECLUSAGE)-1) - - static D3DDECLUSAGE Xb2PCRegisterType - ( - DWORD VertexRegister, - BYTE& PCUsageIndex - ) - { - D3DDECLUSAGE PCRegisterType; - PCUsageIndex = 0; - - switch (VertexRegister) - { - case (DWORD)XTL::X_D3DVSDE_VERTEX: // -1 - PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; - break; - case XTL::X_D3DVSDE_POSITION: // 0 - PCRegisterType = D3DDECLUSAGE_POSITION; - break; - case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 - PCRegisterType = D3DDECLUSAGE_BLENDWEIGHT; - break; - case XTL::X_D3DVSDE_NORMAL: // 2 - PCRegisterType = D3DDECLUSAGE_NORMAL; - break; - case XTL::X_D3DVSDE_DIFFUSE: // 3 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 0; - break; - case XTL::X_D3DVSDE_SPECULAR: // 4 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 1; - break; - case XTL::X_D3DVSDE_FOG: // 5 - PCRegisterType = D3DDECLUSAGE_FOG; - break; - case XTL::X_D3DVSDE_POINTSIZE: // 6 - PCRegisterType = D3DDECLUSAGE_PSIZE; - break; - case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 2; - break; - case XTL::X_D3DVSDE_BACKSPECULAR: // 8 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 3; - break; - case XTL::X_D3DVSDE_TEXCOORD0: // 9 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 0; - break; - case XTL::X_D3DVSDE_TEXCOORD1: // 10 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 1; - break; - case XTL::X_D3DVSDE_TEXCOORD2: // 11 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 2; - break; - case XTL::X_D3DVSDE_TEXCOORD3: // 12 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 3; - break; - default: - PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; - break; - } - - return PCRegisterType; - } - - static char* XboxVertexRegisterAsString(DWORD VertexRegister) - { - switch (VertexRegister) - { - case (DWORD)XTL::X_D3DVSDE_VERTEX: // -1 - return "D3DVSDE_VERTEX /* xbox ext. */"; - case XTL::X_D3DVSDE_POSITION: // 0 - return "D3DVSDE_POSITION"; - case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 - return "D3DVSDE_BLENDWEIGHT"; - case XTL::X_D3DVSDE_NORMAL: // 2 - return "D3DVSDE_NORMAL"; - case XTL::X_D3DVSDE_DIFFUSE: // 3 - return "D3DVSDE_DIFFUSE"; - case XTL::X_D3DVSDE_SPECULAR: // 4 - return "D3DVSDE_SPECULAR"; - case XTL::X_D3DVSDE_FOG: // 5 - return "D3DVSDE_FOG"; - case XTL::X_D3DVSDE_POINTSIZE: // 6 - return "D3DVDSE_POINTSIZE"; - case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 - return "D3DVSDE_BACKDIFFUSE /* xbox ext. */"; - case XTL::X_D3DVSDE_BACKSPECULAR: // 8 - return "D3DVSDE_BACKSPECULAR /* xbox ext. */"; - case XTL::X_D3DVSDE_TEXCOORD0: // 9 - return "D3DVSDE_TEXCOORD0"; - case XTL::X_D3DVSDE_TEXCOORD1: // 10 - return "D3DVSDE_TEXCOORD1"; - case XTL::X_D3DVSDE_TEXCOORD2: // 11 - return "D3DVSDE_TEXCOORD2"; - case XTL::X_D3DVSDE_TEXCOORD3: // 12 - return "D3DVSDE_TEXCOORD3"; - case 13: - return "13 /* unknown register */"; - case 14: - return "14 /* unknown register */"; - case 15: - return "15 /* unknown register */"; - default: - return "16 /* or higher, unknown register */"; - } - } - - // VERTEX SHADER - - static DWORD VshGetDeclarationCount(DWORD *pXboxDeclaration) - { - DWORD Pos = 0; - while (pXboxDeclaration[Pos] != X_D3DVSD_END()) - { - Pos++; - } - - return Pos + 1; - } - - static inline DWORD VshGetTokenType(DWORD XboxToken) - { - return (XboxToken & X_D3DVSD_TOKENTYPEMASK) >> X_D3DVSD_TOKENTYPESHIFT; - } - - static inline WORD VshGetVertexStream(DWORD XboxToken) - { - return (XboxToken & X_D3DVSD_STREAMNUMBERMASK) >> X_D3DVSD_STREAMNUMBERSHIFT; - } - - static inline DWORD VshGetVertexRegister(DWORD XboxToken) - { - DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGMASK) >> X_D3DVSD_VERTEXREGSHIFT; - return regNum; - } - - static inline DWORD VshGetVertexRegisterIn(DWORD XboxToken) - { - DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGINMASK) >> X_D3DVSD_VERTEXREGINSHIFT; - return regNum; - } - - void VshDumpXboxDeclaration(DWORD* pXboxDeclaration) - { - DbgVshPrintf("DWORD dwVSHDecl[] =\n{\n"); - unsigned iNumberOfVertexStreams = 0; - bool bStreamNeedsPatching = false; - auto pXboxToken = pXboxDeclaration; - while (*pXboxToken != X_D3DVSD_END()) // X_D3DVSD_TOKEN_END - { - DWORD Step = 1; - - switch (VshGetTokenType(*pXboxToken)) { - case XTL::X_D3DVSD_TOKEN_NOP: { - DbgVshPrintf("\tD3DVSD_NOP(),\n"); - break; - } - case XTL::X_D3DVSD_TOKEN_STREAM: { - if (*pXboxToken & X_D3DVSD_STREAMTESSMASK) { - DbgVshPrintf("\tD3DVSD_STREAM_TESS(),\n"); - } else { - if (iNumberOfVertexStreams > 0) { - DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); - } - DWORD StreamNumber = VshGetVertexStream(*pXboxToken); - DbgVshPrintf("\tD3DVSD_STREAM(%u),\n", StreamNumber); - iNumberOfVertexStreams++; - bStreamNeedsPatching = false; - } - break; - } - case XTL::X_D3DVSD_TOKEN_STREAMDATA: { - if (*pXboxToken & X_D3DVSD_MASK_SKIP) { - WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; - if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { - DbgVshPrintf("\tD3DVSD_SKIPBYTES(%d), /* xbox ext. */\n", SkipCount); - } else { - DbgVshPrintf("\tD3DVSD_SKIP(%d),\n", SkipCount); - } - } else { - DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); - if (IsFixedFunction) { - DbgVshPrintf("\t\tD3DVSD_REG(%s, ", XboxVertexRegisterAsString(VertexRegister)); - } else { - DbgVshPrintf("\t\tD3DVSD_REG(%d, ", (BYTE)VertexRegister); - } - - DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; - switch (XboxVertexElementDataType) { - case XTL::X_D3DVSDT_FLOAT1: // 0x12: - DbgVshPrintf("D3DVSDT_FLOAT1"); - break; - case XTL::X_D3DVSDT_FLOAT2: // 0x22: - DbgVshPrintf("D3DVSDT_FLOAT2"); - break; - case XTL::X_D3DVSDT_FLOAT3: // 0x32: - DbgVshPrintf("D3DVSDT_FLOAT3"); - break; - case XTL::X_D3DVSDT_FLOAT4: // 0x42: - DbgVshPrintf("D3DVSDT_FLOAT4"); - break; - case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: - DbgVshPrintf("D3DVSDT_D3DCOLOR"); - break; - case XTL::X_D3DVSDT_SHORT2: // 0x25: - DbgVshPrintf("D3DVSDT_SHORT2"); - break; - case XTL::X_D3DVSDT_SHORT4: // 0x45: - DbgVshPrintf("D3DVSDT_SHORT4"); - break; - case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: - DbgVshPrintf("D3DVSDT_NORMSHORT1 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { - DbgVshPrintf("D3DVSDT_NORMSHORT2"); - } else { - DbgVshPrintf("D3DVSDT_NORMSHORT2 /* xbox ext. */"); - bStreamNeedsPatching = true; - } - break; - case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: - DbgVshPrintf("D3DVSDT_NORMSHORT3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { - DbgVshPrintf("D3DVSDT_NORMSHORT4"); - // No need for patching in D3D9 - } else { - DbgVshPrintf("D3DVSDT_NORMSHORT4 /* xbox ext. */"); - bStreamNeedsPatching = true; - } - break; - case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: - DbgVshPrintf("D3DVSDT_NORMPACKED3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_SHORT1: // 0x15: - DbgVshPrintf("D3DVSDT_SHORT1 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_SHORT3: // 0x35: - DbgVshPrintf("D3DVSDT_SHORT3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE1: // 0x14: - DbgVshPrintf("D3DVSDT_PBYTE1 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE2: // 0x24: - DbgVshPrintf("D3DVSDT_PBYTE2 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE3: // 0x34: - DbgVshPrintf("D3DVSDT_PBYTE3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE4: // 0x44: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - DbgVshPrintf("D3DVSDT_PBYTE4"); - } else { - DbgVshPrintf("D3DVSDT_PBYTE4 /* xbox ext. */"); - bStreamNeedsPatching = true; - } - break; - case XTL::X_D3DVSDT_FLOAT2H: // 0x72: - DbgVshPrintf("D3DVSDT_FLOAT2H /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_NONE: // 0x02: - DbgVshPrintf("D3DVSDT_NONE /* xbox ext. */"); - break; - default: - DbgVshPrintf("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); - break; - } - - DbgVshPrintf("),\n"); - }; - break; - } - case XTL::X_D3DVSD_TOKEN_TESSELLATOR: { - DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); - if (*pXboxToken & X_D3DVSD_MASK_TESSUV) { - DbgVshPrintf("\tD3DVSD_TESSUV(%s),\n", XboxVertexRegisterAsString(VertexRegisterOut)); - } else { // D3DVSD_TESSNORMAL - DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); - DbgVshPrintf("\tD3DVSD_TESSNORMAL(%s, %s),\n", - XboxVertexRegisterAsString(VertexRegisterIn), - XboxVertexRegisterAsString(VertexRegisterOut)); - } - break; - } - case XTL::X_D3DVSD_TOKEN_CONSTMEM: { - DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; - DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; - DbgVshPrintf("\tD3DVSD_CONST(%d, %d),\n", ConstantAddress, Count); - LOG_TEST_CASE("X_D3DVSD_TOKEN_CONSTMEM"); - Step = Count * 4 + 1; - break; - } - case XTL::X_D3DVSD_TOKEN_EXT: { - DWORD ExtInfo = (*pXboxToken & X_D3DVSD_EXTINFOMASK) >> X_D3DVSD_EXTINFOSHIFT; - DWORD Count = (*pXboxToken & X_D3DVSD_EXTCOUNTMASK) >> X_D3DVSD_EXTCOUNTSHIFT; - DbgVshPrintf("\tD3DVSD_EXT(%d, %d),\n", ExtInfo, Count); - LOG_TEST_CASE("X_D3DVSD_TOKEN_EXT"); - Step = Count * 4 + 1; // TODO : Is this correct? - break; - } - default: - DbgVshPrintf("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); - break; - } - - pXboxToken += Step; - } - - if (iNumberOfVertexStreams > 0) { - DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); - } - - DbgVshPrintf("\tD3DVSD_END()\n};\n"); - - DbgVshPrintf("// NbrStreams: %d\n", iNumberOfVertexStreams); - } - - static void VshConvertToken_NOP(DWORD *pXboxToken) - { - if(*pXboxToken != X_D3DVSD_NOP()) - { - LOG_TEST_CASE("Token NOP found, but extra parameters are given!"); - } - } - - static DWORD VshConvertToken_CONSTMEM(DWORD *pXboxToken) - { - // DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; - DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; - LOG_TEST_CASE("CONST"); // TODO : Implement - return Count * 4 + 1; - } - - void VshConvertToken_TESSELATOR(DWORD *pXboxToken) - { - BYTE Index; - - if(*pXboxToken & X_D3DVSD_MASK_TESSUV) - { - DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); - DWORD NewVertexRegister = VertexRegister; - - NewVertexRegister = Xb2PCRegisterType(VertexRegister, Index); - // TODO : Expand on the setting of this TESSUV register element : - pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegister); - pRecompiled->UsageIndex = Index; - } - else // D3DVSD_TESSNORMAL - { - DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); - DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); - - DWORD NewVertexRegisterIn = VertexRegisterIn; - DWORD NewVertexRegisterOut = VertexRegisterOut; - - NewVertexRegisterIn = Xb2PCRegisterType(VertexRegisterIn, Index); - // TODO : Expand on the setting of this TESSNORMAL input register element : - pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterIn); - pRecompiled->UsageIndex = Index; - - NewVertexRegisterOut = Xb2PCRegisterType(VertexRegisterOut, Index); - // TODO : Expand on the setting of this TESSNORMAL output register element : - pRecompiled++; - pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterOut); - pRecompiled->UsageIndex = Index; - } - } - - void VshConvertToken_STREAM(DWORD *pXboxToken) - { - // D3DVSD_STREAM_TESS - if(*pXboxToken & X_D3DVSD_STREAMTESSMASK) - { - // TODO - } - else // D3DVSD_STREAM - { - DWORD StreamNumber = VshGetVertexStream(*pXboxToken); - - // new stream - pCurrentVertexShaderStreamInfo = &(pVertexDeclarationToSet->VertexStreams[StreamNumber]); - pCurrentVertexShaderStreamInfo->NeedPatch = FALSE; - pCurrentVertexShaderStreamInfo->DeclPosition = FALSE; - pCurrentVertexShaderStreamInfo->CurrentStreamNumber = 0; - pCurrentVertexShaderStreamInfo->HostVertexStride = 0; - pCurrentVertexShaderStreamInfo->NumberOfVertexElements = 0; - - // Dxbx note : Use Dophin(s), FieldRender, MatrixPaletteSkinning and PersistDisplay as a testcase - - pCurrentVertexShaderStreamInfo->CurrentStreamNumber = VshGetVertexStream(*pXboxToken); - pVertexDeclarationToSet->NumberOfVertexStreams++; - // TODO : Keep a bitmask for all StreamNumber's seen? - } - } - - void VshConvert_RegisterVertexElement( - UINT XboxVertexElementDataType, - UINT XboxVertexElementByteSize, - UINT HostVertexElementByteSize, - BOOL NeedPatching) - { - CxbxVertexShaderStreamElement* pCurrentElement = &(pCurrentVertexShaderStreamInfo->VertexElements[pCurrentVertexShaderStreamInfo->NumberOfVertexElements]); - pCurrentElement->XboxType = XboxVertexElementDataType; - pCurrentElement->XboxByteSize = XboxVertexElementByteSize; - pCurrentElement->HostByteSize = HostVertexElementByteSize; - pCurrentVertexShaderStreamInfo->NumberOfVertexElements++; - pCurrentVertexShaderStreamInfo->NeedPatch |= NeedPatching; - } - - void VshConvert_SkipBytes(int SkipBytesCount) - { - if (SkipBytesCount % sizeof(DWORD)) { - LOG_TEST_CASE("D3DVSD_SKIPBYTES not divisble by 4!"); - } -#if 0 // Potential optimization, for now disabled for simplicity : - else { - // Skip size is a whole multiple of 4 bytes; - // Is stream patching not needed up until this element? - if (!pCurrentVertexShaderStreamInfo->NeedPatch) { - // Then we can get away with increasing the host stride, - // which avoids otherwise needless vertex buffer patching : - pCurrentVertexShaderStreamInfo->HostVertexStride += SkipBytesCount; - return; - } - } -#endif - - // Register a 'skip' element, so that Xbox data will be skipped - // without increasing host stride - this does require patching : - VshConvert_RegisterVertexElement(XTL::X_D3DVSDT_NONE, SkipBytesCount, /*HostSize=*/0, /*NeedPatching=*/TRUE); - } - - void VshConvertToken_STREAMDATA_SKIP(DWORD *pXboxToken) - { - WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; - VshConvert_SkipBytes(SkipCount * sizeof(DWORD)); - } - - void VshConvertToken_STREAMDATA_SKIPBYTES(DWORD* pXboxToken) - { - WORD SkipBytesCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; - VshConvert_SkipBytes(SkipBytesCount); - } - - void VshConvertToken_STREAMDATA_REG(DWORD *pXboxToken) - { - DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); - BOOL NeedPatching = FALSE; - BYTE Index; - BYTE HostVertexRegisterType; - - if (IsFixedFunction) { - HostVertexRegisterType = Xb2PCRegisterType(VertexRegister, Index); - } else { - // D3DDECLUSAGE_TEXCOORD can be useds for any user-defined data - // We need this because there is no reliable way to detect the real usage - // Xbox has no concept of 'usage types', it only requires a list of attribute register numbers. - // So we treat them all as 'user-defined' with an Index of the Vertex Register Index - // this prevents information loss in shaders due to non-matching dcl types! - HostVertexRegisterType = D3DDECLUSAGE_TEXCOORD; - Index = (BYTE)VertexRegister; - } - - // Add this register to the list of declared registers - RegVIsPresentInDeclaration[VertexRegister] = true; - - DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; - WORD XboxVertexElementByteSize = 0; - BYTE HostVertexElementDataType = 0; - WORD HostVertexElementByteSize = 0; - - switch (XboxVertexElementDataType) - { - case XTL::X_D3DVSDT_FLOAT1: // 0x12: - HostVertexElementDataType = D3DDECLTYPE_FLOAT1; - HostVertexElementByteSize = 1 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_FLOAT2: // 0x22: - HostVertexElementDataType = D3DDECLTYPE_FLOAT2; - HostVertexElementByteSize = 2 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_FLOAT3: // 0x32: - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_FLOAT4: // 0x42: - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: - HostVertexElementDataType = D3DDECLTYPE_D3DCOLOR; - HostVertexElementByteSize = 1 * sizeof(D3DCOLOR); - break; - case XTL::X_D3DVSDT_SHORT2: // 0x25: - HostVertexElementDataType = D3DDECLTYPE_SHORT2; - HostVertexElementByteSize = 2 * sizeof(SHORT); - break; - case XTL::X_D3DVSDT_SHORT4: // 0x45: - HostVertexElementDataType = D3DDECLTYPE_SHORT4; - HostVertexElementByteSize = 4 * sizeof(SHORT); - break; - case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT2N; - HostVertexElementByteSize = 2 * sizeof(SHORT); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT1; - HostVertexElementByteSize = 1 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT2N; - HostVertexElementByteSize = 2 * sizeof(SHORT); - // No need for patching in D3D9 - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT2; - HostVertexElementByteSize = 2 * sizeof(FLOAT); - XboxVertexElementByteSize = 2 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - } - break; - case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT4N; - HostVertexElementByteSize = 4 * sizeof(SHORT); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT4N; - HostVertexElementByteSize = 4 * sizeof(SHORT); - // No need for patching in D3D9 - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - XboxVertexElementByteSize = 4 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - } - break; - case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - XboxVertexElementByteSize = 1 * sizeof(XTL::DWORD); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_SHORT1: // 0x15: - HostVertexElementDataType = D3DDECLTYPE_SHORT2; - HostVertexElementByteSize = 2 * sizeof(SHORT); - XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_SHORT3: // 0x35: - HostVertexElementDataType = D3DDECLTYPE_SHORT4; - HostVertexElementByteSize = 4 * sizeof(SHORT); - XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE1: // 0x14: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT1; - HostVertexElementByteSize = 1 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 1 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE2: // 0x24: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT2; - HostVertexElementByteSize = 2 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 2 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE3: // 0x34: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 3 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE4: // 0x44: - // Test-case : Panzer - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - // No need for patching when D3D9 supports D3DDECLTYPE_UBYTE4N - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - XboxVertexElementByteSize = 4 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - } - break; - case XTL::X_D3DVSDT_FLOAT2H: // 0x72: - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - XboxVertexElementByteSize = 3 * sizeof(FLOAT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_NONE: // 0x02: - // No host element data, so no patching - break; - default: - //LOG_TEST_CASE("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); - break; - } - - // On X_D3DVSDT_NONE skip this token - if (XboxVertexElementDataType == XTL::X_D3DVSDT_NONE) - { - // Xbox elements with X_D3DVSDT_NONE have size zero, so there's no need to register those. - // Note, that for skip tokens, we DO call VshConvert_RegisterVertexElement with a X_D3DVSDT_NONE! - return; - } - - // save patching information - VshConvert_RegisterVertexElement( - XboxVertexElementDataType, - NeedPatching ? XboxVertexElementByteSize : HostVertexElementByteSize, - HostVertexElementByteSize, - NeedPatching); - - pRecompiled->Stream = pCurrentVertexShaderStreamInfo->CurrentStreamNumber; - pRecompiled->Offset = pCurrentVertexShaderStreamInfo->HostVertexStride; - pRecompiled->Type = HostVertexElementDataType; - pRecompiled->Method = D3DDECLMETHOD_DEFAULT; - pRecompiled->Usage = HostVertexRegisterType; - pRecompiled->UsageIndex = Index; - - pRecompiled++; - - pCurrentVertexShaderStreamInfo->HostVertexStride += HostVertexElementByteSize; - } - - void VshConvertToken_STREAMDATA(DWORD *pXboxToken) - { - if (*pXboxToken & X_D3DVSD_MASK_SKIP) - { - // For D3D9, use D3DDECLTYPE_UNUSED ? - if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { - VshConvertToken_STREAMDATA_SKIPBYTES(pXboxToken); - } else { - VshConvertToken_STREAMDATA_SKIP(pXboxToken); - } - } - else // D3DVSD_REG - { - VshConvertToken_STREAMDATA_REG(pXboxToken); - } - } - - DWORD VshRecompileToken(DWORD *pXboxToken) - { - DWORD Step = 1; - - switch(VshGetTokenType(*pXboxToken)) - { - case XTL::X_D3DVSD_TOKEN_NOP: - VshConvertToken_NOP(pXboxToken); - break; - case XTL::X_D3DVSD_TOKEN_STREAM: - { - VshConvertToken_STREAM(pXboxToken); - break; - } - case XTL::X_D3DVSD_TOKEN_STREAMDATA: - { - VshConvertToken_STREAMDATA(pXboxToken); - break; - } - case XTL::X_D3DVSD_TOKEN_TESSELLATOR: - { - VshConvertToken_TESSELATOR(pXboxToken); - break; - } - case XTL::X_D3DVSD_TOKEN_CONSTMEM: - { - Step = VshConvertToken_CONSTMEM(pXboxToken); - break; - } - default: - //LOG_TEST_CASE("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); - break; - } - - return Step; - } - - static DWORD* RemoveXboxDeclarationRedefinition(DWORD* pXboxDeclaration) - { - // Detect and remove register redefinitions by preprocessing the Xbox Vertex Declaration - // Test Case: King Kong - - // Find the last token - DWORD* pXboxToken = pXboxDeclaration; - while (*pXboxToken != X_D3DVSD_END()){ - pXboxToken++; - } - - // Operate on a copy of the Xbox declaration, rather than messing with the Xbox's memory - auto declarationBytes = sizeof(DWORD) * (pXboxToken - pXboxDeclaration + 1); - auto pXboxDeclarationCopy = (DWORD*)malloc(declarationBytes); - memcpy(pXboxDeclarationCopy, pXboxDeclaration, declarationBytes); - pXboxToken = pXboxDeclarationCopy + (pXboxToken - pXboxDeclaration); // Move to end of the copy - - // Remember if we've seen a given output register - std::bitset<16> seen; - - // We want to keep later definitions, and remove earlier ones - // Scan back from the end of the declaration, and replace redefinitions with nops - while (pXboxToken > pXboxDeclarationCopy) { - auto type = VshGetTokenType(*pXboxToken); - if (type == XTL::X_D3DVSD_TOKEN_STREAMDATA && !(*pXboxToken & X_D3DVSD_MASK_SKIP) || - type == XTL::X_D3DVSD_TOKEN_TESSELLATOR) - { - auto outputRegister = VshGetVertexRegister(*pXboxToken); - if (seen[outputRegister]) - { - // Blank out tokens for mapped registers - *pXboxToken = X_D3DVSD_NOP(); - EmuLog(LOG_LEVEL::DEBUG, "Replacing duplicate definition of register %d with D3DVSD_NOP", outputRegister); - } - else - { - // Mark register as seen - seen[outputRegister] = true; - } - } - - pXboxToken--; - } - - return pXboxDeclarationCopy; - } - -public: - D3DVERTEXELEMENT *Convert(DWORD* pXboxDeclaration, bool bIsFixedFunction, CxbxVertexDeclaration* pCxbxVertexDeclaration) - { - // Get a preprocessed copy of the original Xbox Vertex Declaration - auto pXboxVertexDeclarationCopy = RemoveXboxDeclarationRedefinition(pXboxDeclaration); - - pVertexDeclarationToSet = pCxbxVertexDeclaration; - - IsFixedFunction = bIsFixedFunction; - - RegVIsPresentInDeclaration.fill(false); - - // First of all some info: - // We have to figure out which flags are set and then - // we have to patch their params - - // some token values - // 0xFFFFFFFF - end of the declaration - // 0x00000000 - nop (means that this value is ignored) - - // Calculate size of declaration - XboxDeclarationCount = VshGetDeclarationCount(pXboxVertexDeclarationCopy); - // For Direct3D9, we need to reserve at least twice the number of elements, as one token can generate two registers (in and out) : - unsigned HostDeclarationSize = XboxDeclarationCount * sizeof(D3DVERTEXELEMENT) * 2; - - D3DVERTEXELEMENT *Result = (D3DVERTEXELEMENT *)calloc(1, HostDeclarationSize); - pRecompiled = Result; - uint8_t *pRecompiledBufferOverflow = ((uint8_t*)pRecompiled) + HostDeclarationSize; - - VshDumpXboxDeclaration(pXboxDeclaration); - - auto pXboxToken = pXboxVertexDeclarationCopy; - while (*pXboxToken != X_D3DVSD_END()) - { - if ((uint8_t*)pRecompiled >= pRecompiledBufferOverflow) { - DbgVshPrintf("Detected buffer-overflow, breaking out...\n"); - break; - } - - DWORD Step = VshRecompileToken(pXboxToken); - pXboxToken += Step; - } - - *pRecompiled = D3DDECL_END(); - - // Ensure valid ordering of the vertex declaration (http://doc.51windows.net/Directx9_SDK/graphics/programmingguide/gettingstarted/vertexdeclaration/vertexdeclaration.htm) - // In particular "All vertex elements for a stream must be consecutive and sorted by offset" - // Test case: King Kong (due to register redefinition) - std::sort(Result, pRecompiled, [] (const auto& x, const auto& y) - { return std::tie(x.Stream, x.Method, x.Offset) < std::tie(y.Stream, y.Method, y.Offset); }); - - // Free the preprocessed declaration copy - free(pXboxVertexDeclarationCopy); - - // Record which registers are in the vertex declaration - for (size_t i = 0; i < RegVIsPresentInDeclaration.size(); i++) { - pCxbxVertexDeclaration->vRegisterInDeclaration[i] = RegVIsPresentInDeclaration[i]; - } - - return Result; - } -}; - -D3DVERTEXELEMENT *EmuRecompileVshDeclaration -( - DWORD *pXboxDeclaration, - bool bIsFixedFunction, - DWORD *pXboxDeclarationCount, - CxbxVertexDeclaration *pCxbxVertexDeclaration -) -{ - XboxVertexDeclarationConverter Converter; - - D3DVERTEXELEMENT* pHostVertexElements = Converter.Convert(pXboxDeclaration, bIsFixedFunction, pCxbxVertexDeclaration); - - *pXboxDeclarationCount = Converter.XboxDeclarationCount; - - return pHostVertexElements; -} - -extern void FreeVertexDynamicPatch(CxbxVertexShader *pVertexShader) -{ - pVertexShader->Declaration.NumberOfVertexStreams = 0; -} - -// Checks for failed vertex shaders, and shaders that would need patching -boolean VshHandleIsValidShader(DWORD XboxVertexShaderHandle) -{ -#if 0 - //printf( "VS = 0x%.08X\n", XboxVertexShaderHandle ); - - CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); - if (pCxbxVertexShader) { - if (pCxbxVertexShader->XboxStatus != 0) - { - return FALSE; - } - /* - for (uint32 i = 0; i < pCxbxVertexShader->VertexShaderInfo.NumberOfVertexStreams; i++) - { - if (pCxbxVertexShader->VertexShaderInfo.VertexStreams[i].NeedPatch) - { - // Just for caching purposes - pCxbxVertexShader->XboxStatus = 0x80000001; - return FALSE; - } - } - */ - } -#endif - return TRUE; -} - -extern boolean IsValidCurrentShader(void) -{ - // Dxbx addition : There's no need to call - // XTL_EmuIDirect3DDevice_GetVertexShader, just check g_Xbox_VertexShader_Handle : - return VshHandleIsValidShader(g_Xbox_VertexShader_Handle); -} - -// Vertex shader state -static DWORD g_CxbxVertexShaderSlotAddress = 0; -static DWORD g_CxbxVertexShaderSlots[X_VSH_MAX_INSTRUCTION_COUNT * X_VSH_INSTRUCTION_SIZE] = { 0 }; - -DWORD* GetCxbxVertexShaderSlotPtr(const DWORD SlotIndexAddress) -{ - if (SlotIndexAddress < X_VSH_MAX_INSTRUCTION_COUNT) { - return &g_CxbxVertexShaderSlots[SlotIndexAddress * X_VSH_INSTRUCTION_SIZE]; - } else { - LOG_TEST_CASE("SlotIndexAddress out of range"); // FIXME : extend with value (once supported by LOG_TEST_CASE) - return nullptr; - } -} - -CxbxVertexDeclaration *GetCxbxVertexDeclaration(DWORD XboxVertexShaderHandle) -{ - CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); - - for (uint32_t i = 0; i < pCxbxVertexShader->Declaration.NumberOfVertexStreams; i++) - { - if (pCxbxVertexShader->Declaration.VertexStreams[i].NeedPatch) - { - return &pCxbxVertexShader->Declaration; - } - } - return nullptr; -} - -std::unordered_map g_CxbxVertexShaders; - -CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle) -{ - if (VshHandleIsVertexShader(XboxVertexShaderHandle)) { - auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); - if (it != g_CxbxVertexShaders.end()) { - return it->second; - } - } - - return nullptr; -} - -void RegisterCxbxVertexShader(DWORD XboxVertexShaderHandle, CxbxVertexShader* shader) -{ - auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); - if (it != g_CxbxVertexShaders.end() && it->second != nullptr && shader != nullptr) { - LOG_TEST_CASE("Overwriting existing Vertex Shader"); - } - - g_CxbxVertexShaders[XboxVertexShaderHandle] = shader; -} - -void SetCxbxVertexDeclaration(CxbxVertexDeclaration& pCxbxVertexDeclaration) { - LOG_INIT - - HRESULT hRet; - - // Set vertex declaration - hRet = g_pD3DDevice->SetVertexDeclaration(pCxbxVertexDeclaration.pHostVertexDeclaration); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexDeclaration"); - - // Titles can specify default values for registers via calls like SetVertexData4f - // HLSL shaders need to know whether to use vertex data or default vertex shader values - // Any register not in the vertex declaration should be set to the default value - float vertexDefaultFlags[X_VSH_MAX_ATTRIBUTES]; - for (int i = 0; i < X_VSH_MAX_ATTRIBUTES; i++) { - vertexDefaultFlags[i] = pCxbxVertexDeclaration.vRegisterInDeclaration[i] ? 0.0f : 1.0f; - } - g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE, vertexDefaultFlags, 4); -} - -// TODO Call this when state is dirty in UpdateNativeD3DResources -// Rather than every time state changes -void SetVertexShaderFromSlots() { - LOG_INIT - - auto pTokens = GetCxbxVertexShaderSlotPtr(g_CxbxVertexShaderSlotAddress); - if (pTokens) { - // Create a vertex shader from the tokens - DWORD shaderSize; - auto shaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize); - HRESULT hRet = g_pD3DDevice->SetVertexShader(g_VertexShaderSource.GetShader(shaderKey)); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); - } -} - -void SetCxbxVertexShaderHandle(CxbxVertexShader* pCxbxVertexShader) -{ - LOG_INIT - - HRESULT hRet; - - // Get vertex shader if we have a key - auto pHostShader = pCxbxVertexShader->VertexShaderKey - ? g_VertexShaderSource.GetShader(pCxbxVertexShader->VertexShaderKey) - : nullptr; - - // Set vertex shader - hRet = g_pD3DDevice->SetVertexShader(pHostShader); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); - - SetCxbxVertexDeclaration(pCxbxVertexShader->Declaration); -} - -void CxbxImpl_SetVertexShaderInput -( - DWORD Handle, - UINT StreamCount, - XTL::X_STREAMINPUT* pStreamInputs -) -{ - LOG_INIT - - // If Handle is NULL, all VertexShader input state is cleared. - // Otherwise, Handle is the address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) - // (Thus, a FVF handle is an invalid argument.) - // - - LOG_UNIMPLEMENTED(); -} - -void CxbxImpl_SelectVertexShaderDirect -( - XTL::X_VERTEXATTRIBUTEFORMAT* pVAF, - DWORD Address -) -{ - LOG_INIT; - - // When pVAF is non-null, this vertex attribute format takes precedence over the the one - LOG_UNIMPLEMENTED(); -} - -void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address) -{ - // Address always indicates a previously loaded vertex shader slot (from where the program is used). - // Handle can be null if the current Xbox VertexShader is assigned - // Handle can be an address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) - // If Handle is assigned, it becomes the new current Xbox VertexShader, - // which resets a bit of state (nv2a execution mode, viewport, ?) - // Either way, the given address slot is selected as the start of the current vertex shader program - g_Xbox_VertexShader_Handle = Handle; - g_CxbxVertexShaderSlotAddress = Address; - - if (VshHandleIsVertexShader(Handle)) - { - auto pCxbxVertexShader = GetCxbxVertexShader(Handle); - if (pCxbxVertexShader == nullptr) { - LOG_TEST_CASE("Shader handle has not been created"); - } - else { - // Set the shader handle declaration - SetCxbxVertexDeclaration(pCxbxVertexShader->Declaration); - } - } - - SetVertexShaderFromSlots(); -} - -void CxbxImpl_LoadVertexShaderProgram(CONST DWORD* pFunction, DWORD Address) -{ - // D3DDevice_LoadVertexShaderProgram splits the given function buffer into batch-wise pushes to the NV2A - - // Copy shader instructions to shader slots - auto CxbxVertexShaderSlotPtr = GetCxbxVertexShaderSlotPtr(Address); - if (CxbxVertexShaderSlotPtr == nullptr) - return; - - auto shaderHeader = *((XTL::X_VSH_SHADER_HEADER*) pFunction); - auto tokens = &pFunction[1]; - memcpy(CxbxVertexShaderSlotPtr, tokens, shaderHeader.NumInst * X_VSH_INSTRUCTION_SIZE_BYTES); - - SetVertexShaderFromSlots(); -} - -void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address) -{ - // Handle is always address of an X_D3DVertexShader struct, thus always or-ed with 1 (X_D3DFVF_RESERVED0) - // Address is the slot (offset) from which the program must be written onwards (as whole DWORDS) - // D3DDevice_LoadVertexShader pushes the program contained in the Xbox VertexShader struct to the NV2A - auto CxbxVertexShaderSlotPtr = GetCxbxVertexShaderSlotPtr(Address); - if (CxbxVertexShaderSlotPtr) { - CxbxVertexShader* pCxbxVertexShader = GetCxbxVertexShader(Handle); - if (pCxbxVertexShader) { - // Make sure there is a shader function to load - // from the shader handle - if (pCxbxVertexShader->pXboxFunctionCopy == nullptr) { - LOG_TEST_CASE("LoadVertexShader with FVF shader handle"); - return; - } - - int upToSlot = Address + pCxbxVertexShader->XboxNrAddressSlots; - if (upToSlot > X_VSH_MAX_INSTRUCTION_COUNT) { - LOG_TEST_CASE("Shader does not fit in vertex shader slots"); - return; - } - - // Skip the header DWORD at the beginning - auto pTokens = &pCxbxVertexShader->pXboxFunctionCopy[1]; - memcpy(CxbxVertexShaderSlotPtr, pTokens, pCxbxVertexShader->XboxNrAddressSlots * X_VSH_INSTRUCTION_SIZE_BYTES); - } - else { - LOG_TEST_CASE("LoadVertexShader called with unrecognized handle"); // FIXME : extend with value (once supported by LOG_TEST_CASE) - } - } -} - -void CxbxImpl_SetVertexShader(DWORD Handle) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - // Checks if the Handle has bit 0 set - if not, it's a FVF - // which is converted to a global Xbox Vertex Shader struct - // Otherwise bit 0 is cleared and the resulting address is - // validated to be a valid Xbox Vertex Shader - // D3D state fields are updated. - // If the shader contains a program, the handle is passed to - // D3DDevice_LoadVertexShader and D3DDevice_SelectVertexShader. - // Otherwise the shader is send using push buffer commands. - - HRESULT hRet = D3D_OK; - - g_Xbox_VertexShader_Handle = Handle; - - if (VshHandleIsVertexShader(Handle)) { - CxbxVertexShader* pCxbxVertexShader = GetCxbxVertexShader(Handle); - if (pCxbxVertexShader) { - SetCxbxVertexShaderHandle(pCxbxVertexShader); - - // If the shader handle has a shader function - // copy it to the shader slots - if (pCxbxVertexShader->pXboxFunctionCopy != nullptr) { - g_CxbxVertexShaderSlotAddress = 0; - auto CxbxVertexShaderSlotPtr = GetCxbxVertexShaderSlotPtr(g_CxbxVertexShaderSlotAddress); - if (CxbxVertexShaderSlotPtr) { - // Skip the header DWORD at the beginning - auto pTokens = &pCxbxVertexShader->pXboxFunctionCopy[1]; - memcpy(CxbxVertexShaderSlotPtr, pTokens, pCxbxVertexShader->XboxNrAddressSlots * X_VSH_INSTRUCTION_SIZE_BYTES); - } - } - } - else { - EmuLog(LOG_LEVEL::DEBUG, "SetVertexShader with shader handle that has not been created"); - } - } - else { - hRet = g_pD3DDevice->SetVertexShader(nullptr); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); - hRet = g_pD3DDevice->SetFVF(Handle); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetFVF"); - } -} - -HRESULT CxbxImpl_CreateVertexShader(CONST DWORD *pDeclaration, CONST DWORD *pFunction, DWORD *pHandle, DWORD Usage) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - HRESULT hRet = D3D_OK; - - if (g_pD3DDevice == nullptr) { - LOG_TEST_CASE("D3DDevice_CreateVertexShader called before Direct3D_CreateDevice"); - // We lie to allow the game to continue for now, but it probably won't work well - return 0; // == STATUS_SUCCESS - } - - // HACK: TODO: support this situation - if (pDeclaration == nullptr) { - LOG_TEST_CASE("Vertex shader without declaration"); - *pHandle = xbnull; - return D3D_OK; - } - - // Now, we can create the host vertex shader - DWORD XboxDeclarationCount = 0; - CxbxVertexShader* pCxbxVertexShader = (CxbxVertexShader*)calloc(1, sizeof(CxbxVertexShader)); - D3DVERTEXELEMENT* pRecompiledDeclaration = nullptr; - - pRecompiledDeclaration = EmuRecompileVshDeclaration((DWORD*)pDeclaration, - /*bIsFixedFunction=*/pFunction == xbnullptr, - &XboxDeclarationCount, - &pCxbxVertexShader->Declaration); - - // Create the vertex declaration - hRet = g_pD3DDevice->CreateVertexDeclaration(pRecompiledDeclaration, &pCxbxVertexShader->Declaration.pHostVertexDeclaration); - free(pRecompiledDeclaration); - - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateVertexDeclaration"); - - if (FAILED(hRet)) { - // NOTE: This is a fatal error because it ALWAYS triggers a crash within DrawVertices if not set - CxbxKrnlCleanup("Failed to create Vertex Declaration"); - } -#if 0 // Creating a vertex shader doesn't imply activating it! - hRet = g_pD3DDevice->SetVertexDeclaration(pCxbxVertexShader->Declaration.pHostVertexDeclaration); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexDeclaration"); - if (FAILED(hRet)) { - CxbxKrnlCleanup("Failed to set Vertex Declaration"); - } -#endif - - // Set handle declaration properties - pCxbxVertexShader->Declaration.pXboxDeclarationCopy = (DWORD*)malloc(XboxDeclarationCount * sizeof(DWORD)); - memcpy(pCxbxVertexShader->Declaration.pXboxDeclarationCopy, pDeclaration, XboxDeclarationCount * sizeof(DWORD)); - pCxbxVertexShader->Declaration.XboxDeclarationCount = XboxDeclarationCount; - - if (pFunction != xbnullptr) - { - // Parse and compile the shader - DWORD xboxFunctionSize = 0; - pCxbxVertexShader->VertexShaderKey = g_VertexShaderSource.CreateShader(pFunction, &xboxFunctionSize); - - // Set handle shader function properties - pCxbxVertexShader->XboxFunctionSize = xboxFunctionSize; - pCxbxVertexShader->pXboxFunctionCopy = (DWORD*)malloc(xboxFunctionSize); - memcpy(pCxbxVertexShader->pXboxFunctionCopy, pFunction, xboxFunctionSize); - pCxbxVertexShader->XboxNrAddressSlots = (xboxFunctionSize - sizeof(XTL::X_VSH_SHADER_HEADER)) / X_VSH_INSTRUCTION_SIZE_BYTES; - pCxbxVertexShader->XboxVertexShaderType = X_VST_NORMAL; // TODO : This can vary - } - - // Save the status, to remove things later - // pCxbxVertexShader->XboxStatus = hRet; // Not even used by VshHandleIsValidShader() - - RegisterCxbxVertexShader(*pHandle, pCxbxVertexShader); - - if (FAILED(hRet)) - { -#ifdef _DEBUG_TRACK_VS - if (pFunction) - { - char pFileName[30]; - static int FailedShaderCount = 0; - XTL::X_VSH_SHADER_HEADER* pHeader = (XTL::X_VSH_SHADER_HEADER*)pFunction; - EmuLog(LOG_LEVEL::WARNING, "Couldn't create vertex shader!"); - sprintf(pFileName, "failed%05d.xvu", FailedShaderCount); - FILE* f = fopen(pFileName, "wb"); - if (f) - { - fwrite(pFunction, sizeof(XTL::X_VSH_SHADER_HEADER) + pHeader->NumInst * 16, 1, f); - fclose(f); - } - FailedShaderCount++; - } -#endif // _DEBUG_TRACK_VS - //hRet = D3D_OK; - } - - return hRet; -} - -void CxbxImpl_DeleteVertexShader(DWORD Handle) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - - // Handle is always address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) - // It's reference count is lowered. If it reaches zero (0), the struct is freed. - - if (VshHandleIsVertexShader(Handle)) - { - CxbxVertexShader* pCxbxVertexShader = GetCxbxVertexShader(Handle); // Fetch from cache - if (pCxbxVertexShader == nullptr) { - return; // Avoid crash if no shader was cached yet - } - - RegisterCxbxVertexShader(Handle, nullptr); // Remove from cache - - if (pCxbxVertexShader->Declaration.pHostVertexDeclaration) { - HRESULT hRet = pCxbxVertexShader->Declaration.pHostVertexDeclaration->Release(); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DeleteVertexShader(pHostVertexDeclaration)"); - } - - // Release the host vertex shader - g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->VertexShaderKey); - - if (pCxbxVertexShader->Declaration.pXboxDeclarationCopy) - { - free(pCxbxVertexShader->Declaration.pXboxDeclarationCopy); - } - - if (pCxbxVertexShader->pXboxFunctionCopy) - { - free(pCxbxVertexShader->pXboxFunctionCopy); - } - - FreeVertexDynamicPatch(pCxbxVertexShader); - - free(pCxbxVertexShader); - } -} - -void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount) -{ - LOG_INIT // Allows use of DEBUG_D3DRESULT - -/*#ifdef _DEBUG_TRACK_VS_CONST - for (uint32_t i = 0; i < ConstantCount; i++) - { - printf("SetVertexShaderConstant, c%d = { %f, %f, %f, %f }\n", - Register + i, - *((float*)pConstantData + 4 * i), - *((float*)pConstantData + 4 * i + 1), - *((float*)pConstantData + 4 * i + 2), - *((float*)pConstantData + 4 * i + 3)); - } -#endif*/ // _DEBUG_TRACK_VS_CONST - -// Xbox vertex shader constants range from -96 to 95 -// The host does not support negative, so we adjust to 0..191 - Register += X_D3DSCM_CORRECTION; - - if (Register < 0) LOG_TEST_CASE("Register < 0"); - if (Register + ConstantCount > X_D3DVS_CONSTREG_COUNT) LOG_TEST_CASE("Register + ConstantCount > X_D3DVS_CONSTREG_COUNT"); - HRESULT hRet; - hRet = g_pD3DDevice->SetVertexShaderConstantF( - Register, - (float*)pConstantData, - ConstantCount - ); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShaderConstant"); - - if (FAILED(hRet)) - { - EmuLog(LOG_LEVEL::WARNING, "We're lying about setting a vertex shader constant!"); - hRet = D3D_OK; - } -} -// parse xbox vertex shader function into an intermediate format -extern void EmuParseVshFunction -( - DWORD* pXboxFunction, - DWORD* pXboxFunctionSize, - IntermediateVertexShader* pShader -) -{ - auto VshDecoder = XboxVertexShaderDecoder(); - - *pXboxFunctionSize = 0; - - // FIXME tidy handling of the header vs headerless cases - // Normally, pXboxFunction has a shader header before the shader tokens - // But we can also load shader tokens directly from the Xbox vertex shader slots too - - bool headerless = pXboxFunction[0] == 0; // if its a token instead of a header, first DWORD is unused - auto headerSize = headerless ? 0 : sizeof(XTL::X_VSH_SHADER_HEADER); - - // Decode the vertex shader program tokens into an intermediate representation - uint32_t* pCurToken = (uint32_t*)((uintptr_t)pXboxFunction + headerSize); - - if (headerless) { - // We've been fed shader slots. Make up a header... - pShader->Header.Version = VERSION_XVS; - pShader->Header.NumInst = (uint16_t)pShader->Instructions.size(); - - // Decode until we hit a token marked final - while (VshDecoder.VshConvertToIntermediate(pCurToken, pShader)) { - pCurToken += X_VSH_INSTRUCTION_SIZE; - } - } - else { - pShader->Header = *(XTL::X_VSH_SHADER_HEADER*)pXboxFunction; - // Decode only up to the number of instructions in the header - // The last instruction may not be marked final: - // Test case: Multiple Vertex Shaders sample - for (int i = 0; i < pShader->Header.NumInst; i++) { - if (!VshDecoder.VshConvertToIntermediate(pCurToken, pShader)) { - if (i < pShader->Header.NumInst - 1) { - LOG_TEST_CASE("Shader instructions after final instruction"); - } - break; - } - pCurToken += X_VSH_INSTRUCTION_SIZE; - } - } - - // The size of the shader is - pCurToken += X_VSH_INSTRUCTION_SIZE; // always at least one token - *pXboxFunctionSize = (intptr_t)pCurToken - (intptr_t)pXboxFunction; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2004 Aaron Robinson +// * Kingofc +// * +// * All rights reserved +// * +// ****************************************************************** +#define LOG_PREFIX CXBXR_MODULE::VTXSH + +//#define _DEBUG_TRACK_VS + +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\Emu.h" +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle +#include "core\hle\D3D8\Direct3D9\VertexShaderSource.h" // For g_VertexShaderSource +#include "core\hle\D3D8\XbVertexShader.h" +#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT +#include "common\Logging.h" // For LOG_INIT + +#include "XbD3D8Types.h" // For X_D3DVSDE_* +#include +#include +#include +#include + +#define DbgVshPrintf \ + LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \ + if(g_bPrintfOn) printf + +// **************************************************************************** +// * Vertex shader function recompiler +// **************************************************************************** + +class XboxVertexShaderDecoder +{ +private: + // Xbox Vertex SHader microcode types + + enum VSH_OUTPUT_TYPE { + OUTPUT_C = 0, + OUTPUT_O + }; + + enum VSH_OUTPUT_MUX { + OMUX_MAC = 0, + OMUX_ILU + }; + + // Host intermediate vertex shader types + + enum VSH_FIELD_NAME { + FLD_ILU = 0, + FLD_MAC, + FLD_CONST, + FLD_V, + // Input A + FLD_A_NEG, + FLD_A_SWZ_X, + FLD_A_SWZ_Y, + FLD_A_SWZ_Z, + FLD_A_SWZ_W, + FLD_A_R, + FLD_A_MUX, + // Input B + FLD_B_NEG, + FLD_B_SWZ_X, + FLD_B_SWZ_Y, + FLD_B_SWZ_Z, + FLD_B_SWZ_W, + FLD_B_R, + FLD_B_MUX, + // Input C + FLD_C_NEG, + FLD_C_SWZ_X, + FLD_C_SWZ_Y, + FLD_C_SWZ_Z, + FLD_C_SWZ_W, + FLD_C_R_HIGH, + FLD_C_R_LOW, + FLD_C_MUX, + // Output + FLD_OUT_MAC_MASK, + FLD_OUT_R, + FLD_OUT_ILU_MASK, + FLD_OUT_O_MASK, + FLD_OUT_ORB, + FLD_OUT_ADDRESS, + FLD_OUT_MUX, + // Relative addressing + FLD_A0X, + // Final instruction + FLD_FINAL + }; + + // Retrieves a number of bits in the instruction token + static inline uint32_t VshGetFromToken( + uint32_t* pShaderToken, + uint8_t SubToken, + uint8_t StartBit, + uint8_t BitLength) + { + return (pShaderToken[SubToken] >> StartBit) & ~(0xFFFFFFFF << BitLength); + } + + static uint8_t VshGetField( + uint32_t* pShaderToken, + VSH_FIELD_NAME FieldName) + { + // Used for xvu spec definition + static const struct { + uint8_t SubToken; + uint8_t StartBit; + uint8_t BitLength; + } FieldMapping[/*VSH_FIELD_NAME*/] = { + // SubToken BitPos BitSize + { 1, 25, 3 }, // FLD_ILU, + { 1, 21, 4 }, // FLD_MAC, + { 1, 13, 8 }, // FLD_CONST, + { 1, 9, 4 }, // FLD_V, + // Input A + { 1, 8, 1 }, // FLD_A_NEG, + { 1, 6, 2 }, // FLD_A_SWZ_X, + { 1, 4, 2 }, // FLD_A_SWZ_Y, + { 1, 2, 2 }, // FLD_A_SWZ_Z, + { 1, 0, 2 }, // FLD_A_SWZ_W, + { 2, 28, 4 }, // FLD_A_R, + { 2, 26, 2 }, // FLD_A_MUX, + // Input B + { 2, 25, 1 }, // FLD_B_NEG, + { 2, 23, 2 }, // FLD_B_SWZ_X, + { 2, 21, 2 }, // FLD_B_SWZ_Y, + { 2, 19, 2 }, // FLD_B_SWZ_Z, + { 2, 17, 2 }, // FLD_B_SWZ_W, + { 2, 13, 4 }, // FLD_B_R, + { 2, 11, 2 }, // FLD_B_MUX, + // Input C + { 2, 10, 1 }, // FLD_C_NEG, + { 2, 8, 2 }, // FLD_C_SWZ_X, + { 2, 6, 2 }, // FLD_C_SWZ_Y, + { 2, 4, 2 }, // FLD_C_SWZ_Z, + { 2, 2, 2 }, // FLD_C_SWZ_W, + { 2, 0, 2 }, // FLD_C_R_HIGH, + { 3, 30, 2 }, // FLD_C_R_LOW, + { 3, 28, 2 }, // FLD_C_MUX, + // Output + { 3, 24, 4 }, // FLD_OUT_MAC_MASK, + { 3, 20, 4 }, // FLD_OUT_R, + { 3, 16, 4 }, // FLD_OUT_ILU_MASK, + { 3, 12, 4 }, // FLD_OUT_O_MASK, + { 3, 11, 1 }, // FLD_OUT_ORB, + { 3, 3, 8 }, // FLD_OUT_ADDRESS, + { 3, 2, 1 }, // FLD_OUT_MUX, + // Relative addressing + { 3, 1, 1 }, // FLD_A0X, + // Final instruction + { 3, 0, 1 } // FLD_FINAL, + }; + + return (uint8_t)(VshGetFromToken(pShaderToken, + FieldMapping[FieldName].SubToken, + FieldMapping[FieldName].StartBit, + FieldMapping[FieldName].BitLength)); + } + + // Converts the C register address to disassembly format + static inline int16_t ConvertCRegister(const int16_t CReg) + { + return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31); + } + + static void VshConvertIntermediateParam(VSH_IMD_PARAMETER& Param, + uint32_t* pShaderToken, + VSH_FIELD_NAME FLD_MUX, + VSH_FIELD_NAME FLD_NEG, + uint16_t R, + uint16_t V, + uint16_t C) + { + Param.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_MUX); + switch (Param.ParameterType) { + case PARAM_R: + Param.Address = R; + break; + case PARAM_V: + Param.Address = V; + break; + case PARAM_C: + Param.Address = C; + break; + default: + LOG_TEST_CASE("parameter type unknown"); + } + + int d = FLD_NEG - FLD_A_NEG; + Param.Neg = VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_NEG)) > 0; + Param.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_X)); + Param.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Y)); + Param.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_Z)); + Param.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, (VSH_FIELD_NAME)(d + FLD_A_SWZ_W)); + } + + void VshAddIntermediateInstruction( + uint32_t* pShaderToken, + IntermediateVertexShader* pShader, + VSH_MAC MAC, + VSH_ILU ILU, + VSH_IMD_OUTPUT_TYPE output_type, + int16_t output_address, + int8_t output_mask) + { + // Is the output mask set? + if (output_mask == 0) { + return; + } + + if (pShader->Instructions.size() >= VSH_MAX_INTERMEDIATE_COUNT) { + CxbxKrnlCleanup("Shader exceeds conversion buffer!"); + } + + VSH_INTERMEDIATE_FORMAT intermediate; + intermediate.MAC = MAC; + intermediate.ILU = ILU; + intermediate.Output.Type = output_type; + intermediate.Output.Address = output_address; + intermediate.Output.Mask = output_mask; + // Get a0.x indirect constant addressing + intermediate.IndexesWithA0_X = VshGetField(pShaderToken, FLD_A0X) > 0; // Applies to PARAM_C parameter reads + + int16_t R; + int16_t V = VshGetField(pShaderToken, FLD_V); + int16_t C = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); + intermediate.ParamCount = 0; + if (MAC >= MAC_MOV) { + // Get parameter A + R = VshGetField(pShaderToken, FLD_A_R); + VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_A_MUX, FLD_A_NEG, R, V, C); + } + + if ((MAC == MAC_MUL) || ((MAC >= MAC_MAD) && (MAC <= MAC_SGE))) { + // Get parameter B + R = VshGetField(pShaderToken, FLD_B_R); + VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_B_MUX, FLD_B_NEG, R, V, C); + } + + if ((ILU >= ILU_MOV) || (MAC == MAC_ADD) || (MAC == MAC_MAD)) { + // Get parameter C + R = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | VshGetField(pShaderToken, FLD_C_R_LOW); + VshConvertIntermediateParam(intermediate.Parameters[intermediate.ParamCount++], pShaderToken, FLD_C_MUX, FLD_C_NEG, R, V, C); + } + + // Add the instruction to the shader + pShader->Instructions.push_back(intermediate); + } + +public: + bool VshConvertToIntermediate(uint32_t* pShaderToken, IntermediateVertexShader* pShader) + { + // First get the instruction(s). + VSH_ILU ILU = (VSH_ILU)VshGetField(pShaderToken, FLD_ILU); + VSH_MAC MAC = (VSH_MAC)VshGetField(pShaderToken, FLD_MAC); + if (MAC > MAC_ARL) LOG_TEST_CASE("Unknown MAC"); + + // Output register + VSH_OUTPUT_MUX OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX); + int16_t OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS); + VSH_IMD_OUTPUT_TYPE OutputType; + if ((VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB) == OUTPUT_C) { + OutputType = IMD_OUTPUT_C; + OutputAddress = ConvertCRegister(OutputAddress); + } else { // OUTPUT_O: + OutputType = IMD_OUTPUT_O; + OutputAddress = OutputAddress & 0xF; + } + + // MAC,ILU output R register + int16_t RAddress = VshGetField(pShaderToken, FLD_OUT_R); + + // Test for paired opcodes + bool bIsPaired = (MAC != MAC_NOP) && (ILU != ILU_NOP); + + // Check if there's a MAC opcode + if (MAC > MAC_NOP && MAC <= MAC_ARL) { + if (bIsPaired && RAddress == 1) { + // Ignore paired MAC opcodes that write to R1 + } else { + if (MAC == MAC_ARL) { + VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_A0X, 0, MASK_X); + } else { + VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, IMD_OUTPUT_R, RAddress, VshGetField(pShaderToken, FLD_OUT_MAC_MASK)); + } + } + + // Check if we must add a muxed MAC opcode as well + if (OutputMux == OMUX_MAC) { + VshAddIntermediateInstruction(pShaderToken, pShader, MAC, ILU_NOP, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK)); + } + } + + // Check if there's an ILU opcode + if (ILU != ILU_NOP) { + // Paired ILU opcodes will only write to R1 + VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, IMD_OUTPUT_R, bIsPaired ? 1 : RAddress, VshGetField(pShaderToken, FLD_OUT_ILU_MASK)); + // Check if we must add a muxed ILU opcode as well + if (OutputMux == OMUX_ILU) { + VshAddIntermediateInstruction(pShaderToken, pShader, MAC_NOP, ILU, OutputType, OutputAddress, VshGetField(pShaderToken, FLD_OUT_O_MASK)); + } + } + + return VshGetField(pShaderToken, FLD_FINAL) == 0; + } + +}; + +// **************************************************************************** +// * Vertex shader declaration recompiler +// **************************************************************************** + +extern D3DCAPS g_D3DCaps; + +class XboxVertexDeclarationConverter +{ +protected: + // Internal variables + CxbxVertexDeclaration* pVertexDeclarationToSet; + CxbxVertexShaderStreamInfo* pCurrentVertexShaderStreamInfo = nullptr; + bool IsFixedFunction; + D3DVERTEXELEMENT* pRecompiled; + std::array RegVIsPresentInDeclaration; + +public: + // Output + DWORD XboxDeclarationCount; + +private: + #define D3DDECLUSAGE_UNSUPPORTED ((D3DDECLUSAGE)-1) + + static D3DDECLUSAGE Xb2PCRegisterType + ( + DWORD VertexRegister, + BYTE& PCUsageIndex + ) + { + D3DDECLUSAGE PCRegisterType; + PCUsageIndex = 0; + + switch (VertexRegister) + { + case (DWORD)XTL::X_D3DVSDE_VERTEX: // -1 + PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; + break; + case XTL::X_D3DVSDE_POSITION: // 0 + PCRegisterType = D3DDECLUSAGE_POSITION; + break; + case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 + PCRegisterType = D3DDECLUSAGE_BLENDWEIGHT; + break; + case XTL::X_D3DVSDE_NORMAL: // 2 + PCRegisterType = D3DDECLUSAGE_NORMAL; + break; + case XTL::X_D3DVSDE_DIFFUSE: // 3 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 0; + break; + case XTL::X_D3DVSDE_SPECULAR: // 4 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 1; + break; + case XTL::X_D3DVSDE_FOG: // 5 + PCRegisterType = D3DDECLUSAGE_FOG; + break; + case XTL::X_D3DVSDE_POINTSIZE: // 6 + PCRegisterType = D3DDECLUSAGE_PSIZE; + break; + case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 2; + break; + case XTL::X_D3DVSDE_BACKSPECULAR: // 8 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 3; + break; + case XTL::X_D3DVSDE_TEXCOORD0: // 9 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 0; + break; + case XTL::X_D3DVSDE_TEXCOORD1: // 10 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 1; + break; + case XTL::X_D3DVSDE_TEXCOORD2: // 11 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 2; + break; + case XTL::X_D3DVSDE_TEXCOORD3: // 12 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 3; + break; + default: + PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; + break; + } + + return PCRegisterType; + } + + static char* XboxVertexRegisterAsString(DWORD VertexRegister) + { + switch (VertexRegister) + { + case (DWORD)XTL::X_D3DVSDE_VERTEX: // -1 + return "D3DVSDE_VERTEX /* xbox ext. */"; + case XTL::X_D3DVSDE_POSITION: // 0 + return "D3DVSDE_POSITION"; + case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 + return "D3DVSDE_BLENDWEIGHT"; + case XTL::X_D3DVSDE_NORMAL: // 2 + return "D3DVSDE_NORMAL"; + case XTL::X_D3DVSDE_DIFFUSE: // 3 + return "D3DVSDE_DIFFUSE"; + case XTL::X_D3DVSDE_SPECULAR: // 4 + return "D3DVSDE_SPECULAR"; + case XTL::X_D3DVSDE_FOG: // 5 + return "D3DVSDE_FOG"; + case XTL::X_D3DVSDE_POINTSIZE: // 6 + return "D3DVDSE_POINTSIZE"; + case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 + return "D3DVSDE_BACKDIFFUSE /* xbox ext. */"; + case XTL::X_D3DVSDE_BACKSPECULAR: // 8 + return "D3DVSDE_BACKSPECULAR /* xbox ext. */"; + case XTL::X_D3DVSDE_TEXCOORD0: // 9 + return "D3DVSDE_TEXCOORD0"; + case XTL::X_D3DVSDE_TEXCOORD1: // 10 + return "D3DVSDE_TEXCOORD1"; + case XTL::X_D3DVSDE_TEXCOORD2: // 11 + return "D3DVSDE_TEXCOORD2"; + case XTL::X_D3DVSDE_TEXCOORD3: // 12 + return "D3DVSDE_TEXCOORD3"; + case 13: + return "13 /* unknown register */"; + case 14: + return "14 /* unknown register */"; + case 15: + return "15 /* unknown register */"; + default: + return "16 /* or higher, unknown register */"; + } + } + + // VERTEX SHADER + + static DWORD VshGetDeclarationCount(DWORD *pXboxDeclaration) + { + DWORD Pos = 0; + while (pXboxDeclaration[Pos] != X_D3DVSD_END()) + { + Pos++; + } + + return Pos + 1; + } + + static inline DWORD VshGetTokenType(DWORD XboxToken) + { + return (XboxToken & X_D3DVSD_TOKENTYPEMASK) >> X_D3DVSD_TOKENTYPESHIFT; + } + + static inline WORD VshGetVertexStream(DWORD XboxToken) + { + return (XboxToken & X_D3DVSD_STREAMNUMBERMASK) >> X_D3DVSD_STREAMNUMBERSHIFT; + } + + static inline DWORD VshGetVertexRegister(DWORD XboxToken) + { + DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGMASK) >> X_D3DVSD_VERTEXREGSHIFT; + return regNum; + } + + static inline DWORD VshGetVertexRegisterIn(DWORD XboxToken) + { + DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGINMASK) >> X_D3DVSD_VERTEXREGINSHIFT; + return regNum; + } + + void VshDumpXboxDeclaration(DWORD* pXboxDeclaration) + { + DbgVshPrintf("DWORD dwVSHDecl[] =\n{\n"); + unsigned iNumberOfVertexStreams = 0; + bool bStreamNeedsPatching = false; + auto pXboxToken = pXboxDeclaration; + while (*pXboxToken != X_D3DVSD_END()) // X_D3DVSD_TOKEN_END + { + DWORD Step = 1; + + switch (VshGetTokenType(*pXboxToken)) { + case XTL::X_D3DVSD_TOKEN_NOP: { + DbgVshPrintf("\tD3DVSD_NOP(),\n"); + break; + } + case XTL::X_D3DVSD_TOKEN_STREAM: { + if (*pXboxToken & X_D3DVSD_STREAMTESSMASK) { + DbgVshPrintf("\tD3DVSD_STREAM_TESS(),\n"); + } else { + if (iNumberOfVertexStreams > 0) { + DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); + } + DWORD StreamNumber = VshGetVertexStream(*pXboxToken); + DbgVshPrintf("\tD3DVSD_STREAM(%u),\n", StreamNumber); + iNumberOfVertexStreams++; + bStreamNeedsPatching = false; + } + break; + } + case XTL::X_D3DVSD_TOKEN_STREAMDATA: { + if (*pXboxToken & X_D3DVSD_MASK_SKIP) { + WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; + if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { + DbgVshPrintf("\tD3DVSD_SKIPBYTES(%d), /* xbox ext. */\n", SkipCount); + } else { + DbgVshPrintf("\tD3DVSD_SKIP(%d),\n", SkipCount); + } + } else { + DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); + if (IsFixedFunction) { + DbgVshPrintf("\t\tD3DVSD_REG(%s, ", XboxVertexRegisterAsString(VertexRegister)); + } else { + DbgVshPrintf("\t\tD3DVSD_REG(%d, ", (BYTE)VertexRegister); + } + + DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; + switch (XboxVertexElementDataType) { + case XTL::X_D3DVSDT_FLOAT1: // 0x12: + DbgVshPrintf("D3DVSDT_FLOAT1"); + break; + case XTL::X_D3DVSDT_FLOAT2: // 0x22: + DbgVshPrintf("D3DVSDT_FLOAT2"); + break; + case XTL::X_D3DVSDT_FLOAT3: // 0x32: + DbgVshPrintf("D3DVSDT_FLOAT3"); + break; + case XTL::X_D3DVSDT_FLOAT4: // 0x42: + DbgVshPrintf("D3DVSDT_FLOAT4"); + break; + case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: + DbgVshPrintf("D3DVSDT_D3DCOLOR"); + break; + case XTL::X_D3DVSDT_SHORT2: // 0x25: + DbgVshPrintf("D3DVSDT_SHORT2"); + break; + case XTL::X_D3DVSDT_SHORT4: // 0x45: + DbgVshPrintf("D3DVSDT_SHORT4"); + break; + case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: + DbgVshPrintf("D3DVSDT_NORMSHORT1 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { + DbgVshPrintf("D3DVSDT_NORMSHORT2"); + } else { + DbgVshPrintf("D3DVSDT_NORMSHORT2 /* xbox ext. */"); + bStreamNeedsPatching = true; + } + break; + case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: + DbgVshPrintf("D3DVSDT_NORMSHORT3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { + DbgVshPrintf("D3DVSDT_NORMSHORT4"); + // No need for patching in D3D9 + } else { + DbgVshPrintf("D3DVSDT_NORMSHORT4 /* xbox ext. */"); + bStreamNeedsPatching = true; + } + break; + case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: + DbgVshPrintf("D3DVSDT_NORMPACKED3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_SHORT1: // 0x15: + DbgVshPrintf("D3DVSDT_SHORT1 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_SHORT3: // 0x35: + DbgVshPrintf("D3DVSDT_SHORT3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE1: // 0x14: + DbgVshPrintf("D3DVSDT_PBYTE1 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE2: // 0x24: + DbgVshPrintf("D3DVSDT_PBYTE2 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE3: // 0x34: + DbgVshPrintf("D3DVSDT_PBYTE3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE4: // 0x44: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + DbgVshPrintf("D3DVSDT_PBYTE4"); + } else { + DbgVshPrintf("D3DVSDT_PBYTE4 /* xbox ext. */"); + bStreamNeedsPatching = true; + } + break; + case XTL::X_D3DVSDT_FLOAT2H: // 0x72: + DbgVshPrintf("D3DVSDT_FLOAT2H /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_NONE: // 0x02: + DbgVshPrintf("D3DVSDT_NONE /* xbox ext. */"); + break; + default: + DbgVshPrintf("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); + break; + } + + DbgVshPrintf("),\n"); + }; + break; + } + case XTL::X_D3DVSD_TOKEN_TESSELLATOR: { + DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); + if (*pXboxToken & X_D3DVSD_MASK_TESSUV) { + DbgVshPrintf("\tD3DVSD_TESSUV(%s),\n", XboxVertexRegisterAsString(VertexRegisterOut)); + } else { // D3DVSD_TESSNORMAL + DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); + DbgVshPrintf("\tD3DVSD_TESSNORMAL(%s, %s),\n", + XboxVertexRegisterAsString(VertexRegisterIn), + XboxVertexRegisterAsString(VertexRegisterOut)); + } + break; + } + case XTL::X_D3DVSD_TOKEN_CONSTMEM: { + DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; + DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; + DbgVshPrintf("\tD3DVSD_CONST(%d, %d),\n", ConstantAddress, Count); + LOG_TEST_CASE("X_D3DVSD_TOKEN_CONSTMEM"); + Step = Count * 4 + 1; + break; + } + case XTL::X_D3DVSD_TOKEN_EXT: { + DWORD ExtInfo = (*pXboxToken & X_D3DVSD_EXTINFOMASK) >> X_D3DVSD_EXTINFOSHIFT; + DWORD Count = (*pXboxToken & X_D3DVSD_EXTCOUNTMASK) >> X_D3DVSD_EXTCOUNTSHIFT; + DbgVshPrintf("\tD3DVSD_EXT(%d, %d),\n", ExtInfo, Count); + LOG_TEST_CASE("X_D3DVSD_TOKEN_EXT"); + Step = Count * 4 + 1; // TODO : Is this correct? + break; + } + default: + DbgVshPrintf("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); + break; + } + + pXboxToken += Step; + } + + if (iNumberOfVertexStreams > 0) { + DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); + } + + DbgVshPrintf("\tD3DVSD_END()\n};\n"); + + DbgVshPrintf("// NbrStreams: %d\n", iNumberOfVertexStreams); + } + + static void VshConvertToken_NOP(DWORD *pXboxToken) + { + if(*pXboxToken != X_D3DVSD_NOP()) + { + LOG_TEST_CASE("Token NOP found, but extra parameters are given!"); + } + } + + static DWORD VshConvertToken_CONSTMEM(DWORD *pXboxToken) + { + // DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; + DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; + LOG_TEST_CASE("CONST"); // TODO : Implement + return Count * 4 + 1; + } + + void VshConvertToken_TESSELATOR(DWORD *pXboxToken) + { + BYTE Index; + + if(*pXboxToken & X_D3DVSD_MASK_TESSUV) + { + DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); + DWORD NewVertexRegister = VertexRegister; + + NewVertexRegister = Xb2PCRegisterType(VertexRegister, Index); + // TODO : Expand on the setting of this TESSUV register element : + pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegister); + pRecompiled->UsageIndex = Index; + } + else // D3DVSD_TESSNORMAL + { + DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); + DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); + + DWORD NewVertexRegisterIn = VertexRegisterIn; + DWORD NewVertexRegisterOut = VertexRegisterOut; + + NewVertexRegisterIn = Xb2PCRegisterType(VertexRegisterIn, Index); + // TODO : Expand on the setting of this TESSNORMAL input register element : + pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterIn); + pRecompiled->UsageIndex = Index; + + NewVertexRegisterOut = Xb2PCRegisterType(VertexRegisterOut, Index); + // TODO : Expand on the setting of this TESSNORMAL output register element : + pRecompiled++; + pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterOut); + pRecompiled->UsageIndex = Index; + } + } + + void VshConvertToken_STREAM(DWORD *pXboxToken) + { + // D3DVSD_STREAM_TESS + if(*pXboxToken & X_D3DVSD_STREAMTESSMASK) + { + // TODO + } + else // D3DVSD_STREAM + { + DWORD StreamNumber = VshGetVertexStream(*pXboxToken); + + // new stream + pCurrentVertexShaderStreamInfo = &(pVertexDeclarationToSet->VertexStreams[StreamNumber]); + pCurrentVertexShaderStreamInfo->NeedPatch = FALSE; + pCurrentVertexShaderStreamInfo->DeclPosition = FALSE; + pCurrentVertexShaderStreamInfo->CurrentStreamNumber = 0; + pCurrentVertexShaderStreamInfo->HostVertexStride = 0; + pCurrentVertexShaderStreamInfo->NumberOfVertexElements = 0; + + // Dxbx note : Use Dophin(s), FieldRender, MatrixPaletteSkinning and PersistDisplay as a testcase + + pCurrentVertexShaderStreamInfo->CurrentStreamNumber = VshGetVertexStream(*pXboxToken); + pVertexDeclarationToSet->NumberOfVertexStreams++; + // TODO : Keep a bitmask for all StreamNumber's seen? + } + } + + void VshConvert_RegisterVertexElement( + UINT XboxVertexElementDataType, + UINT XboxVertexElementByteSize, + UINT HostVertexElementByteSize, + BOOL NeedPatching) + { + CxbxVertexShaderStreamElement* pCurrentElement = &(pCurrentVertexShaderStreamInfo->VertexElements[pCurrentVertexShaderStreamInfo->NumberOfVertexElements]); + pCurrentElement->XboxType = XboxVertexElementDataType; + pCurrentElement->XboxByteSize = XboxVertexElementByteSize; + pCurrentElement->HostByteSize = HostVertexElementByteSize; + pCurrentVertexShaderStreamInfo->NumberOfVertexElements++; + pCurrentVertexShaderStreamInfo->NeedPatch |= NeedPatching; + } + + void VshConvert_SkipBytes(int SkipBytesCount) + { + if (SkipBytesCount % sizeof(DWORD)) { + LOG_TEST_CASE("D3DVSD_SKIPBYTES not divisble by 4!"); + } +#if 0 // Potential optimization, for now disabled for simplicity : + else { + // Skip size is a whole multiple of 4 bytes; + // Is stream patching not needed up until this element? + if (!pCurrentVertexShaderStreamInfo->NeedPatch) { + // Then we can get away with increasing the host stride, + // which avoids otherwise needless vertex buffer patching : + pCurrentVertexShaderStreamInfo->HostVertexStride += SkipBytesCount; + return; + } + } +#endif + + // Register a 'skip' element, so that Xbox data will be skipped + // without increasing host stride - this does require patching : + VshConvert_RegisterVertexElement(XTL::X_D3DVSDT_NONE, SkipBytesCount, /*HostSize=*/0, /*NeedPatching=*/TRUE); + } + + void VshConvertToken_STREAMDATA_SKIP(DWORD *pXboxToken) + { + WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; + VshConvert_SkipBytes(SkipCount * sizeof(DWORD)); + } + + void VshConvertToken_STREAMDATA_SKIPBYTES(DWORD* pXboxToken) + { + WORD SkipBytesCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; + VshConvert_SkipBytes(SkipBytesCount); + } + + void VshConvertToken_STREAMDATA_REG(DWORD *pXboxToken) + { + DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); + BOOL NeedPatching = FALSE; + BYTE Index; + BYTE HostVertexRegisterType; + + if (IsFixedFunction) { + HostVertexRegisterType = Xb2PCRegisterType(VertexRegister, Index); + } else { + // D3DDECLUSAGE_TEXCOORD can be useds for any user-defined data + // We need this because there is no reliable way to detect the real usage + // Xbox has no concept of 'usage types', it only requires a list of attribute register numbers. + // So we treat them all as 'user-defined' with an Index of the Vertex Register Index + // this prevents information loss in shaders due to non-matching dcl types! + HostVertexRegisterType = D3DDECLUSAGE_TEXCOORD; + Index = (BYTE)VertexRegister; + } + + // Add this register to the list of declared registers + RegVIsPresentInDeclaration[VertexRegister] = true; + + DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; + WORD XboxVertexElementByteSize = 0; + BYTE HostVertexElementDataType = 0; + WORD HostVertexElementByteSize = 0; + + switch (XboxVertexElementDataType) + { + case XTL::X_D3DVSDT_FLOAT1: // 0x12: + HostVertexElementDataType = D3DDECLTYPE_FLOAT1; + HostVertexElementByteSize = 1 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_FLOAT2: // 0x22: + HostVertexElementDataType = D3DDECLTYPE_FLOAT2; + HostVertexElementByteSize = 2 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_FLOAT3: // 0x32: + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_FLOAT4: // 0x42: + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: + HostVertexElementDataType = D3DDECLTYPE_D3DCOLOR; + HostVertexElementByteSize = 1 * sizeof(D3DCOLOR); + break; + case XTL::X_D3DVSDT_SHORT2: // 0x25: + HostVertexElementDataType = D3DDECLTYPE_SHORT2; + HostVertexElementByteSize = 2 * sizeof(SHORT); + break; + case XTL::X_D3DVSDT_SHORT4: // 0x45: + HostVertexElementDataType = D3DDECLTYPE_SHORT4; + HostVertexElementByteSize = 4 * sizeof(SHORT); + break; + case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT2N; + HostVertexElementByteSize = 2 * sizeof(SHORT); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT1; + HostVertexElementByteSize = 1 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT2N; + HostVertexElementByteSize = 2 * sizeof(SHORT); + // No need for patching in D3D9 + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT2; + HostVertexElementByteSize = 2 * sizeof(FLOAT); + XboxVertexElementByteSize = 2 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + } + break; + case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT4N; + HostVertexElementByteSize = 4 * sizeof(SHORT); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT4N; + HostVertexElementByteSize = 4 * sizeof(SHORT); + // No need for patching in D3D9 + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + XboxVertexElementByteSize = 4 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + } + break; + case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + XboxVertexElementByteSize = 1 * sizeof(XTL::DWORD); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_SHORT1: // 0x15: + HostVertexElementDataType = D3DDECLTYPE_SHORT2; + HostVertexElementByteSize = 2 * sizeof(SHORT); + XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_SHORT3: // 0x35: + HostVertexElementDataType = D3DDECLTYPE_SHORT4; + HostVertexElementByteSize = 4 * sizeof(SHORT); + XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE1: // 0x14: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT1; + HostVertexElementByteSize = 1 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 1 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE2: // 0x24: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT2; + HostVertexElementByteSize = 2 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 2 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE3: // 0x34: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 3 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE4: // 0x44: + // Test-case : Panzer + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + // No need for patching when D3D9 supports D3DDECLTYPE_UBYTE4N + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + XboxVertexElementByteSize = 4 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + } + break; + case XTL::X_D3DVSDT_FLOAT2H: // 0x72: + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + XboxVertexElementByteSize = 3 * sizeof(FLOAT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_NONE: // 0x02: + // No host element data, so no patching + break; + default: + //LOG_TEST_CASE("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); + break; + } + + // On X_D3DVSDT_NONE skip this token + if (XboxVertexElementDataType == XTL::X_D3DVSDT_NONE) + { + // Xbox elements with X_D3DVSDT_NONE have size zero, so there's no need to register those. + // Note, that for skip tokens, we DO call VshConvert_RegisterVertexElement with a X_D3DVSDT_NONE! + return; + } + + // save patching information + VshConvert_RegisterVertexElement( + XboxVertexElementDataType, + NeedPatching ? XboxVertexElementByteSize : HostVertexElementByteSize, + HostVertexElementByteSize, + NeedPatching); + + pRecompiled->Stream = pCurrentVertexShaderStreamInfo->CurrentStreamNumber; + pRecompiled->Offset = pCurrentVertexShaderStreamInfo->HostVertexStride; + pRecompiled->Type = HostVertexElementDataType; + pRecompiled->Method = D3DDECLMETHOD_DEFAULT; + pRecompiled->Usage = HostVertexRegisterType; + pRecompiled->UsageIndex = Index; + + pRecompiled++; + + pCurrentVertexShaderStreamInfo->HostVertexStride += HostVertexElementByteSize; + } + + void VshConvertToken_STREAMDATA(DWORD *pXboxToken) + { + if (*pXboxToken & X_D3DVSD_MASK_SKIP) + { + // For D3D9, use D3DDECLTYPE_UNUSED ? + if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { + VshConvertToken_STREAMDATA_SKIPBYTES(pXboxToken); + } else { + VshConvertToken_STREAMDATA_SKIP(pXboxToken); + } + } + else // D3DVSD_REG + { + VshConvertToken_STREAMDATA_REG(pXboxToken); + } + } + + DWORD VshRecompileToken(DWORD *pXboxToken) + { + DWORD Step = 1; + + switch(VshGetTokenType(*pXboxToken)) + { + case XTL::X_D3DVSD_TOKEN_NOP: + VshConvertToken_NOP(pXboxToken); + break; + case XTL::X_D3DVSD_TOKEN_STREAM: + { + VshConvertToken_STREAM(pXboxToken); + break; + } + case XTL::X_D3DVSD_TOKEN_STREAMDATA: + { + VshConvertToken_STREAMDATA(pXboxToken); + break; + } + case XTL::X_D3DVSD_TOKEN_TESSELLATOR: + { + VshConvertToken_TESSELATOR(pXboxToken); + break; + } + case XTL::X_D3DVSD_TOKEN_CONSTMEM: + { + Step = VshConvertToken_CONSTMEM(pXboxToken); + break; + } + default: + //LOG_TEST_CASE("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); + break; + } + + return Step; + } + + static DWORD* RemoveXboxDeclarationRedefinition(DWORD* pXboxDeclaration) + { + // Detect and remove register redefinitions by preprocessing the Xbox Vertex Declaration + // Test Case: King Kong + + // Find the last token + DWORD* pXboxToken = pXboxDeclaration; + while (*pXboxToken != X_D3DVSD_END()){ + pXboxToken++; + } + + // Operate on a copy of the Xbox declaration, rather than messing with the Xbox's memory + auto declarationBytes = sizeof(DWORD) * (pXboxToken - pXboxDeclaration + 1); + auto pXboxDeclarationCopy = (DWORD*)malloc(declarationBytes); + memcpy(pXboxDeclarationCopy, pXboxDeclaration, declarationBytes); + pXboxToken = pXboxDeclarationCopy + (pXboxToken - pXboxDeclaration); // Move to end of the copy + + // Remember if we've seen a given output register + std::bitset<16> seen; + + // We want to keep later definitions, and remove earlier ones + // Scan back from the end of the declaration, and replace redefinitions with nops + while (pXboxToken > pXboxDeclarationCopy) { + auto type = VshGetTokenType(*pXboxToken); + if (type == XTL::X_D3DVSD_TOKEN_STREAMDATA && !(*pXboxToken & X_D3DVSD_MASK_SKIP) || + type == XTL::X_D3DVSD_TOKEN_TESSELLATOR) + { + auto outputRegister = VshGetVertexRegister(*pXboxToken); + if (seen[outputRegister]) + { + // Blank out tokens for mapped registers + *pXboxToken = X_D3DVSD_NOP(); + EmuLog(LOG_LEVEL::DEBUG, "Replacing duplicate definition of register %d with D3DVSD_NOP", outputRegister); + } + else + { + // Mark register as seen + seen[outputRegister] = true; + } + } + + pXboxToken--; + } + + return pXboxDeclarationCopy; + } + +public: + D3DVERTEXELEMENT *Convert(DWORD* pXboxDeclaration, bool bIsFixedFunction, CxbxVertexDeclaration* pCxbxVertexDeclaration) + { + // Get a preprocessed copy of the original Xbox Vertex Declaration + auto pXboxVertexDeclarationCopy = RemoveXboxDeclarationRedefinition(pXboxDeclaration); + + pVertexDeclarationToSet = pCxbxVertexDeclaration; + + IsFixedFunction = bIsFixedFunction; + + RegVIsPresentInDeclaration.fill(false); + + // First of all some info: + // We have to figure out which flags are set and then + // we have to patch their params + + // some token values + // 0xFFFFFFFF - end of the declaration + // 0x00000000 - nop (means that this value is ignored) + + // Calculate size of declaration + XboxDeclarationCount = VshGetDeclarationCount(pXboxVertexDeclarationCopy); + // For Direct3D9, we need to reserve at least twice the number of elements, as one token can generate two registers (in and out) : + unsigned HostDeclarationSize = XboxDeclarationCount * sizeof(D3DVERTEXELEMENT) * 2; + + D3DVERTEXELEMENT *Result = (D3DVERTEXELEMENT *)calloc(1, HostDeclarationSize); + pRecompiled = Result; + uint8_t *pRecompiledBufferOverflow = ((uint8_t*)pRecompiled) + HostDeclarationSize; + + VshDumpXboxDeclaration(pXboxDeclaration); + + auto pXboxToken = pXboxVertexDeclarationCopy; + while (*pXboxToken != X_D3DVSD_END()) + { + if ((uint8_t*)pRecompiled >= pRecompiledBufferOverflow) { + DbgVshPrintf("Detected buffer-overflow, breaking out...\n"); + break; + } + + DWORD Step = VshRecompileToken(pXboxToken); + pXboxToken += Step; + } + + *pRecompiled = D3DDECL_END(); + + // Ensure valid ordering of the vertex declaration (http://doc.51windows.net/Directx9_SDK/graphics/programmingguide/gettingstarted/vertexdeclaration/vertexdeclaration.htm) + // In particular "All vertex elements for a stream must be consecutive and sorted by offset" + // Test case: King Kong (due to register redefinition) + std::sort(Result, pRecompiled, [] (const auto& x, const auto& y) + { return std::tie(x.Stream, x.Method, x.Offset) < std::tie(y.Stream, y.Method, y.Offset); }); + + // Free the preprocessed declaration copy + free(pXboxVertexDeclarationCopy); + + // Record which registers are in the vertex declaration + for (size_t i = 0; i < RegVIsPresentInDeclaration.size(); i++) { + pCxbxVertexDeclaration->vRegisterInDeclaration[i] = RegVIsPresentInDeclaration[i]; + } + + return Result; + } +}; + +D3DVERTEXELEMENT *EmuRecompileVshDeclaration +( + DWORD *pXboxDeclaration, + bool bIsFixedFunction, + DWORD *pXboxDeclarationCount, + CxbxVertexDeclaration *pCxbxVertexDeclaration +) +{ + XboxVertexDeclarationConverter Converter; + + D3DVERTEXELEMENT* pHostVertexElements = Converter.Convert(pXboxDeclaration, bIsFixedFunction, pCxbxVertexDeclaration); + + *pXboxDeclarationCount = Converter.XboxDeclarationCount; + + return pHostVertexElements; +} + +extern void FreeVertexDynamicPatch(CxbxVertexShader *pVertexShader) +{ + pVertexShader->Declaration.NumberOfVertexStreams = 0; +} + +// Checks for failed vertex shaders, and shaders that would need patching +boolean VshHandleIsValidShader(DWORD XboxVertexShaderHandle) +{ +#if 0 + //printf( "VS = 0x%.08X\n", XboxVertexShaderHandle ); + + CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); + if (pCxbxVertexShader) { + if (pCxbxVertexShader->XboxStatus != 0) + { + return FALSE; + } + /* + for (uint32 i = 0; i < pCxbxVertexShader->VertexShaderInfo.NumberOfVertexStreams; i++) + { + if (pCxbxVertexShader->VertexShaderInfo.VertexStreams[i].NeedPatch) + { + // Just for caching purposes + pCxbxVertexShader->XboxStatus = 0x80000001; + return FALSE; + } + } + */ + } +#endif + return TRUE; +} + +extern boolean IsValidCurrentShader(void) +{ + // Dxbx addition : There's no need to call + // XTL_EmuIDirect3DDevice_GetVertexShader, just check g_Xbox_VertexShader_Handle : + return VshHandleIsValidShader(g_Xbox_VertexShader_Handle); +} + +// Vertex shader state +static DWORD g_CxbxVertexShaderSlotAddress = 0; +static DWORD g_CxbxVertexShaderSlots[X_VSH_MAX_INSTRUCTION_COUNT * X_VSH_INSTRUCTION_SIZE] = { 0 }; + +DWORD* GetCxbxVertexShaderSlotPtr(const DWORD SlotIndexAddress) +{ + if (SlotIndexAddress < X_VSH_MAX_INSTRUCTION_COUNT) { + return &g_CxbxVertexShaderSlots[SlotIndexAddress * X_VSH_INSTRUCTION_SIZE]; + } else { + LOG_TEST_CASE("SlotIndexAddress out of range"); // FIXME : extend with value (once supported by LOG_TEST_CASE) + return nullptr; + } +} + +CxbxVertexDeclaration *GetCxbxVertexDeclaration(DWORD XboxVertexShaderHandle) +{ + CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); + + for (uint32_t i = 0; i < pCxbxVertexShader->Declaration.NumberOfVertexStreams; i++) + { + if (pCxbxVertexShader->Declaration.VertexStreams[i].NeedPatch) + { + return &pCxbxVertexShader->Declaration; + } + } + return nullptr; +} + +std::unordered_map g_CxbxVertexShaders; + +CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle) +{ + if (VshHandleIsVertexShader(XboxVertexShaderHandle)) { + auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); + if (it != g_CxbxVertexShaders.end()) { + return it->second; + } + } + + return nullptr; +} + +void RegisterCxbxVertexShader(DWORD XboxVertexShaderHandle, CxbxVertexShader* shader) +{ + auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); + if (it != g_CxbxVertexShaders.end() && it->second != nullptr && shader != nullptr) { + LOG_TEST_CASE("Overwriting existing Vertex Shader"); + } + + g_CxbxVertexShaders[XboxVertexShaderHandle] = shader; +} + +void SetCxbxVertexDeclaration(CxbxVertexDeclaration& pCxbxVertexDeclaration) { + LOG_INIT + + HRESULT hRet; + + // Set vertex declaration + hRet = g_pD3DDevice->SetVertexDeclaration(pCxbxVertexDeclaration.pHostVertexDeclaration); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexDeclaration"); + + // Titles can specify default values for registers via calls like SetVertexData4f + // HLSL shaders need to know whether to use vertex data or default vertex shader values + // Any register not in the vertex declaration should be set to the default value + float vertexDefaultFlags[X_VSH_MAX_ATTRIBUTES]; + for (int i = 0; i < X_VSH_MAX_ATTRIBUTES; i++) { + vertexDefaultFlags[i] = pCxbxVertexDeclaration.vRegisterInDeclaration[i] ? 0.0f : 1.0f; + } + g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE, vertexDefaultFlags, 4); +} + +// TODO Call this when state is dirty in UpdateNativeD3DResources +// Rather than every time state changes +void SetVertexShaderFromSlots() { + LOG_INIT + + auto pTokens = GetCxbxVertexShaderSlotPtr(g_CxbxVertexShaderSlotAddress); + if (pTokens) { + // Create a vertex shader from the tokens + DWORD shaderSize; + auto shaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize); + HRESULT hRet = g_pD3DDevice->SetVertexShader(g_VertexShaderSource.GetShader(shaderKey)); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); + } +} + +void SetCxbxVertexShaderHandle(CxbxVertexShader* pCxbxVertexShader) +{ + LOG_INIT + + HRESULT hRet; + + // Get vertex shader if we have a key + auto pHostShader = pCxbxVertexShader->VertexShaderKey + ? g_VertexShaderSource.GetShader(pCxbxVertexShader->VertexShaderKey) + : nullptr; + + // Set vertex shader + hRet = g_pD3DDevice->SetVertexShader(pHostShader); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); + + SetCxbxVertexDeclaration(pCxbxVertexShader->Declaration); +} + +void CxbxImpl_SetVertexShaderInput +( + DWORD Handle, + UINT StreamCount, + XTL::X_STREAMINPUT* pStreamInputs +) +{ + LOG_INIT + + // If Handle is NULL, all VertexShader input state is cleared. + // Otherwise, Handle is the address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // (Thus, a FVF handle is an invalid argument.) + // + + LOG_UNIMPLEMENTED(); +} + +void CxbxImpl_SelectVertexShaderDirect +( + XTL::X_VERTEXATTRIBUTEFORMAT* pVAF, + DWORD Address +) +{ + LOG_INIT; + + // When pVAF is non-null, this vertex attribute format takes precedence over the the one + LOG_UNIMPLEMENTED(); +} + +void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address) +{ + // Address always indicates a previously loaded vertex shader slot (from where the program is used). + // Handle can be null if the current Xbox VertexShader is assigned + // Handle can be an address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // If Handle is assigned, it becomes the new current Xbox VertexShader, + // which resets a bit of state (nv2a execution mode, viewport, ?) + // Either way, the given address slot is selected as the start of the current vertex shader program + g_Xbox_VertexShader_Handle = Handle; + g_CxbxVertexShaderSlotAddress = Address; + + if (VshHandleIsVertexShader(Handle)) + { + auto pCxbxVertexShader = GetCxbxVertexShader(Handle); + if (pCxbxVertexShader == nullptr) { + LOG_TEST_CASE("Shader handle has not been created"); + } + else { + // Set the shader handle declaration + SetCxbxVertexDeclaration(pCxbxVertexShader->Declaration); + } + } + + SetVertexShaderFromSlots(); +} + +void CxbxImpl_LoadVertexShaderProgram(CONST DWORD* pFunction, DWORD Address) +{ + // D3DDevice_LoadVertexShaderProgram splits the given function buffer into batch-wise pushes to the NV2A + + // Copy shader instructions to shader slots + auto CxbxVertexShaderSlotPtr = GetCxbxVertexShaderSlotPtr(Address); + if (CxbxVertexShaderSlotPtr == nullptr) + return; + + auto shaderHeader = *((XTL::X_VSH_SHADER_HEADER*) pFunction); + auto tokens = &pFunction[1]; + memcpy(CxbxVertexShaderSlotPtr, tokens, shaderHeader.NumInst * X_VSH_INSTRUCTION_SIZE_BYTES); + + SetVertexShaderFromSlots(); +} + +void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address) +{ + // Handle is always address of an X_D3DVertexShader struct, thus always or-ed with 1 (X_D3DFVF_RESERVED0) + // Address is the slot (offset) from which the program must be written onwards (as whole DWORDS) + // D3DDevice_LoadVertexShader pushes the program contained in the Xbox VertexShader struct to the NV2A + auto CxbxVertexShaderSlotPtr = GetCxbxVertexShaderSlotPtr(Address); + if (CxbxVertexShaderSlotPtr) { + CxbxVertexShader* pCxbxVertexShader = GetCxbxVertexShader(Handle); + if (pCxbxVertexShader) { + // Make sure there is a shader function to load + // from the shader handle + if (pCxbxVertexShader->pXboxFunctionCopy == nullptr) { + LOG_TEST_CASE("LoadVertexShader with FVF shader handle"); + return; + } + + int upToSlot = Address + pCxbxVertexShader->XboxNrAddressSlots; + if (upToSlot > X_VSH_MAX_INSTRUCTION_COUNT) { + LOG_TEST_CASE("Shader does not fit in vertex shader slots"); + return; + } + + // Skip the header DWORD at the beginning + auto pTokens = &pCxbxVertexShader->pXboxFunctionCopy[1]; + memcpy(CxbxVertexShaderSlotPtr, pTokens, pCxbxVertexShader->XboxNrAddressSlots * X_VSH_INSTRUCTION_SIZE_BYTES); + } + else { + LOG_TEST_CASE("LoadVertexShader called with unrecognized handle"); // FIXME : extend with value (once supported by LOG_TEST_CASE) + } + } +} + +void CxbxImpl_SetVertexShader(DWORD Handle) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + // Checks if the Handle has bit 0 set - if not, it's a FVF + // which is converted to a global Xbox Vertex Shader struct + // Otherwise bit 0 is cleared and the resulting address is + // validated to be a valid Xbox Vertex Shader + // D3D state fields are updated. + // If the shader contains a program, the handle is passed to + // D3DDevice_LoadVertexShader and D3DDevice_SelectVertexShader. + // Otherwise the shader is send using push buffer commands. + + HRESULT hRet = D3D_OK; + + g_Xbox_VertexShader_Handle = Handle; + + if (VshHandleIsVertexShader(Handle)) { + CxbxVertexShader* pCxbxVertexShader = GetCxbxVertexShader(Handle); + if (pCxbxVertexShader) { + SetCxbxVertexShaderHandle(pCxbxVertexShader); + + // If the shader handle has a shader function + // copy it to the shader slots + if (pCxbxVertexShader->pXboxFunctionCopy != nullptr) { + g_CxbxVertexShaderSlotAddress = 0; + auto CxbxVertexShaderSlotPtr = GetCxbxVertexShaderSlotPtr(g_CxbxVertexShaderSlotAddress); + if (CxbxVertexShaderSlotPtr) { + // Skip the header DWORD at the beginning + auto pTokens = &pCxbxVertexShader->pXboxFunctionCopy[1]; + memcpy(CxbxVertexShaderSlotPtr, pTokens, pCxbxVertexShader->XboxNrAddressSlots * X_VSH_INSTRUCTION_SIZE_BYTES); + } + } + } + else { + EmuLog(LOG_LEVEL::DEBUG, "SetVertexShader with shader handle that has not been created"); + } + } + else { + hRet = g_pD3DDevice->SetVertexShader(nullptr); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); + hRet = g_pD3DDevice->SetFVF(Handle); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetFVF"); + } +} + +HRESULT CxbxImpl_CreateVertexShader(CONST DWORD *pDeclaration, CONST DWORD *pFunction, DWORD *pHandle, DWORD Usage) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + HRESULT hRet = D3D_OK; + + if (g_pD3DDevice == nullptr) { + LOG_TEST_CASE("D3DDevice_CreateVertexShader called before Direct3D_CreateDevice"); + // We lie to allow the game to continue for now, but it probably won't work well + return 0; // == STATUS_SUCCESS + } + + // HACK: TODO: support this situation + if (pDeclaration == nullptr) { + LOG_TEST_CASE("Vertex shader without declaration"); + *pHandle = xbnull; + return D3D_OK; + } + + // Now, we can create the host vertex shader + DWORD XboxDeclarationCount = 0; + CxbxVertexShader* pCxbxVertexShader = (CxbxVertexShader*)calloc(1, sizeof(CxbxVertexShader)); + D3DVERTEXELEMENT* pRecompiledDeclaration = nullptr; + + pRecompiledDeclaration = EmuRecompileVshDeclaration((DWORD*)pDeclaration, + /*bIsFixedFunction=*/pFunction == xbnullptr, + &XboxDeclarationCount, + &pCxbxVertexShader->Declaration); + + // Create the vertex declaration + hRet = g_pD3DDevice->CreateVertexDeclaration(pRecompiledDeclaration, &pCxbxVertexShader->Declaration.pHostVertexDeclaration); + free(pRecompiledDeclaration); + + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateVertexDeclaration"); + + if (FAILED(hRet)) { + // NOTE: This is a fatal error because it ALWAYS triggers a crash within DrawVertices if not set + CxbxKrnlCleanup("Failed to create Vertex Declaration"); + } +#if 0 // Creating a vertex shader doesn't imply activating it! + hRet = g_pD3DDevice->SetVertexDeclaration(pCxbxVertexShader->Declaration.pHostVertexDeclaration); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexDeclaration"); + if (FAILED(hRet)) { + CxbxKrnlCleanup("Failed to set Vertex Declaration"); + } +#endif + + // Set handle declaration properties + pCxbxVertexShader->Declaration.pXboxDeclarationCopy = (DWORD*)malloc(XboxDeclarationCount * sizeof(DWORD)); + memcpy(pCxbxVertexShader->Declaration.pXboxDeclarationCopy, pDeclaration, XboxDeclarationCount * sizeof(DWORD)); + pCxbxVertexShader->Declaration.XboxDeclarationCount = XboxDeclarationCount; + + if (pFunction != xbnullptr) + { + // Parse and compile the shader + DWORD xboxFunctionSize = 0; + pCxbxVertexShader->VertexShaderKey = g_VertexShaderSource.CreateShader(pFunction, &xboxFunctionSize); + + // Set handle shader function properties + pCxbxVertexShader->XboxFunctionSize = xboxFunctionSize; + pCxbxVertexShader->pXboxFunctionCopy = (DWORD*)malloc(xboxFunctionSize); + memcpy(pCxbxVertexShader->pXboxFunctionCopy, pFunction, xboxFunctionSize); + pCxbxVertexShader->XboxNrAddressSlots = (xboxFunctionSize - sizeof(XTL::X_VSH_SHADER_HEADER)) / X_VSH_INSTRUCTION_SIZE_BYTES; + pCxbxVertexShader->XboxVertexShaderType = X_VST_NORMAL; // TODO : This can vary + } + + // Save the status, to remove things later + // pCxbxVertexShader->XboxStatus = hRet; // Not even used by VshHandleIsValidShader() + + RegisterCxbxVertexShader(*pHandle, pCxbxVertexShader); + + if (FAILED(hRet)) + { +#ifdef _DEBUG_TRACK_VS + if (pFunction) + { + char pFileName[30]; + static int FailedShaderCount = 0; + XTL::X_VSH_SHADER_HEADER* pHeader = (XTL::X_VSH_SHADER_HEADER*)pFunction; + EmuLog(LOG_LEVEL::WARNING, "Couldn't create vertex shader!"); + sprintf(pFileName, "failed%05d.xvu", FailedShaderCount); + FILE* f = fopen(pFileName, "wb"); + if (f) + { + fwrite(pFunction, sizeof(XTL::X_VSH_SHADER_HEADER) + pHeader->NumInst * 16, 1, f); + fclose(f); + } + FailedShaderCount++; + } +#endif // _DEBUG_TRACK_VS + //hRet = D3D_OK; + } + + return hRet; +} + +void CxbxImpl_DeleteVertexShader(DWORD Handle) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + + // Handle is always address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // It's reference count is lowered. If it reaches zero (0), the struct is freed. + + if (VshHandleIsVertexShader(Handle)) + { + CxbxVertexShader* pCxbxVertexShader = GetCxbxVertexShader(Handle); // Fetch from cache + if (pCxbxVertexShader == nullptr) { + return; // Avoid crash if no shader was cached yet + } + + RegisterCxbxVertexShader(Handle, nullptr); // Remove from cache + + if (pCxbxVertexShader->Declaration.pHostVertexDeclaration) { + HRESULT hRet = pCxbxVertexShader->Declaration.pHostVertexDeclaration->Release(); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->DeleteVertexShader(pHostVertexDeclaration)"); + } + + // Release the host vertex shader + g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->VertexShaderKey); + + if (pCxbxVertexShader->Declaration.pXboxDeclarationCopy) + { + free(pCxbxVertexShader->Declaration.pXboxDeclarationCopy); + } + + if (pCxbxVertexShader->pXboxFunctionCopy) + { + free(pCxbxVertexShader->pXboxFunctionCopy); + } + + FreeVertexDynamicPatch(pCxbxVertexShader); + + free(pCxbxVertexShader); + } +} + +void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount) +{ + LOG_INIT // Allows use of DEBUG_D3DRESULT + +/*#ifdef _DEBUG_TRACK_VS_CONST + for (uint32_t i = 0; i < ConstantCount; i++) + { + printf("SetVertexShaderConstant, c%d = { %f, %f, %f, %f }\n", + Register + i, + *((float*)pConstantData + 4 * i), + *((float*)pConstantData + 4 * i + 1), + *((float*)pConstantData + 4 * i + 2), + *((float*)pConstantData + 4 * i + 3)); + } +#endif*/ // _DEBUG_TRACK_VS_CONST + +// Xbox vertex shader constants range from -96 to 95 +// The host does not support negative, so we adjust to 0..191 + Register += X_D3DSCM_CORRECTION; + + if (Register < 0) LOG_TEST_CASE("Register < 0"); + if (Register + ConstantCount > X_D3DVS_CONSTREG_COUNT) LOG_TEST_CASE("Register + ConstantCount > X_D3DVS_CONSTREG_COUNT"); + HRESULT hRet; + hRet = g_pD3DDevice->SetVertexShaderConstantF( + Register, + (float*)pConstantData, + ConstantCount + ); + DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShaderConstant"); + + if (FAILED(hRet)) + { + EmuLog(LOG_LEVEL::WARNING, "We're lying about setting a vertex shader constant!"); + hRet = D3D_OK; + } +} +// parse xbox vertex shader function into an intermediate format +extern void EmuParseVshFunction +( + DWORD* pXboxFunction, + DWORD* pXboxFunctionSize, + IntermediateVertexShader* pShader +) +{ + auto VshDecoder = XboxVertexShaderDecoder(); + + *pXboxFunctionSize = 0; + + // FIXME tidy handling of the header vs headerless cases + // Normally, pXboxFunction has a shader header before the shader tokens + // But we can also load shader tokens directly from the Xbox vertex shader slots too + + bool headerless = pXboxFunction[0] == 0; // if its a token instead of a header, first DWORD is unused + auto headerSize = headerless ? 0 : sizeof(XTL::X_VSH_SHADER_HEADER); + + // Decode the vertex shader program tokens into an intermediate representation + uint32_t* pCurToken = (uint32_t*)((uintptr_t)pXboxFunction + headerSize); + + if (headerless) { + // We've been fed shader slots. Make up a header... + pShader->Header.Version = VERSION_XVS; + pShader->Header.NumInst = (uint16_t)pShader->Instructions.size(); + + // Decode until we hit a token marked final + while (VshDecoder.VshConvertToIntermediate(pCurToken, pShader)) { + pCurToken += X_VSH_INSTRUCTION_SIZE; + } + } + else { + pShader->Header = *(XTL::X_VSH_SHADER_HEADER*)pXboxFunction; + // Decode only up to the number of instructions in the header + // The last instruction may not be marked final: + // Test case: Multiple Vertex Shaders sample + for (int i = 0; i < pShader->Header.NumInst; i++) { + if (!VshDecoder.VshConvertToIntermediate(pCurToken, pShader)) { + if (i < pShader->Header.NumInst - 1) { + LOG_TEST_CASE("Shader instructions after final instruction"); + } + break; + } + pCurToken += X_VSH_INSTRUCTION_SIZE; + } + } + + // The size of the shader is + pCurToken += X_VSH_INSTRUCTION_SIZE; // always at least one token + *pXboxFunctionSize = (intptr_t)pCurToken - (intptr_t)pXboxFunction; +} diff --git a/src/core/hle/D3D8/XbVertexShader.h b/src/core/hle/D3D8/XbVertexShader.h index 310e5b1eb..496efc083 100644 --- a/src/core/hle/D3D8/XbVertexShader.h +++ b/src/core/hle/D3D8/XbVertexShader.h @@ -73,7 +73,7 @@ typedef struct _CxbxVertexDeclaration CxbxVertexShaderStreamInfo VertexStreams[X_VSH_MAX_STREAMS]; IDirect3DVertexDeclaration* pHostVertexDeclaration; DWORD* pXboxDeclarationCopy; - DWORD XboxDeclarationCount; // Xbox's number of DWORD-sized X_D3DVSD* tokens + DWORD XboxDeclarationCount; // Xbox's number of DWORD-sized X_D3DVSD* tokens UINT NumberOfVertexStreams; // The number of streams the vertex shader uses bool vRegisterInDeclaration[X_VSH_MAX_ATTRIBUTES]; } @@ -232,17 +232,17 @@ extern boolean IsValidCurrentShader(void); inline boolean VshHandleIsVertexShader(DWORD Handle) { return (Handle & X_D3DFVF_RESERVED0) ? TRUE : FALSE; } inline boolean VshHandleIsFVF(DWORD Handle) { return !VshHandleIsVertexShader(Handle); } inline XTL::X_D3DVertexShader *VshHandleToXboxVertexShader(DWORD Handle) { return (XTL::X_D3DVertexShader *)(Handle & ~X_D3DFVF_RESERVED0);} - -extern CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle); - + +extern CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle); + extern void CxbxImpl_LoadVertexShaderProgram(CONST DWORD* pFunction, DWORD Address); -extern void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address); -extern void CxbxImpl_SetVertexShader(DWORD Handle); -extern void CxbxImpl_SelectVertexShaderDirect(XTL::X_VERTEXATTRIBUTEFORMAT* pVAF, DWORD Address); -extern void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address); -extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, XTL::X_STREAMINPUT* pStreamInputs); -extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount); -extern HRESULT CxbxImpl_CreateVertexShader(CONST DWORD *pDeclaration, CONST DWORD *pFunction, DWORD *pHandle, DWORD Usage); -extern void CxbxImpl_DeleteVertexShader(DWORD Handle); +extern void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address); +extern void CxbxImpl_SetVertexShader(DWORD Handle); +extern void CxbxImpl_SelectVertexShaderDirect(XTL::X_VERTEXATTRIBUTEFORMAT* pVAF, DWORD Address); +extern void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address); +extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, XTL::X_STREAMINPUT* pStreamInputs); +extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount); +extern HRESULT CxbxImpl_CreateVertexShader(CONST DWORD *pDeclaration, CONST DWORD *pFunction, DWORD *pHandle, DWORD Usage); +extern void CxbxImpl_DeleteVertexShader(DWORD Handle); #endif diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp index c5f3be1cd..886ed6e1b 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp @@ -25,13 +25,13 @@ // ****************************************************************** #ifndef EMUDSOUND_H #define EMUDSOUND_H - -#include "core\kernel\init\CxbxKrnl.h" -#include "core\hle\DSOUND\XbDSoundTypes.h" -#include "core\hle\DSOUND\common\XbInternalStruct.hpp" - -typedef struct IDirectSound3DListener8* LPDIRECTSOUND3DLISTENER8; -typedef struct IDirectSound3DBuffer8* LPDIRECTSOUND3DBUFFER8; + +#include "core\kernel\init\CxbxKrnl.h" +#include "core\hle\DSOUND\XbDSoundTypes.h" +#include "core\hle\DSOUND\common\XbInternalStruct.hpp" + +typedef struct IDirectSound3DListener8* LPDIRECTSOUND3DLISTENER8; +typedef struct IDirectSound3DBuffer8* LPDIRECTSOUND3DBUFFER8; #ifdef __cplusplus extern "C" { @@ -46,7 +46,7 @@ void CxbxInitAudio(); namespace XTL { #undef FIELD_OFFSET // prevent macro redefinition warnings -#include // TODO: FIXME after global namespace XTL issue is resolved. +#include // TODO: FIXME after global namespace XTL issue is resolved. // ****************************************************************** // * X_CDirectSound @@ -107,26 +107,26 @@ struct EmuDirectSoundBuffer DSoundBuffer_Lock X_lock; REFERENCE_TIME Xb_rtPauseEx; REFERENCE_TIME Xb_rtStopEx; - LONG Xb_VolumeMixbin; + LONG Xb_VolumeMixbin; X_DSENVOLOPEDESC Xb_EnvolopeDesc; - X_DSVOICEPROPS Xb_VoiceProperties; - DWORD Xb_Flags; -}; - -struct XbHybridDSBuffer : DSBUFFER_S::DSBUFFER_I { - EmuDirectSoundBuffer* emuDSBuffer; -}; - -struct SharedDSBuffer : DSBUFFER_S { - SharedDSBuffer(bool is3D) : DSBUFFER_S(is3D) { - emuDSBuffer = new EmuDirectSoundBuffer(); - } - EmuDirectSoundBuffer* emuDSBuffer; - - virtual ~SharedDSBuffer() { - delete emuDSBuffer; - } -}; + X_DSVOICEPROPS Xb_VoiceProperties; + DWORD Xb_Flags; +}; + +struct XbHybridDSBuffer : DSBUFFER_S::DSBUFFER_I { + EmuDirectSoundBuffer* emuDSBuffer; +}; + +struct SharedDSBuffer : DSBUFFER_S { + SharedDSBuffer(bool is3D) : DSBUFFER_S(is3D) { + emuDSBuffer = new EmuDirectSoundBuffer(); + } + EmuDirectSoundBuffer* emuDSBuffer; + + virtual ~SharedDSBuffer() { + delete emuDSBuffer; + } +}; //Custom flags (4 bytes support up to 31 shifts,starting from 0) #define DSE_FLAG_PCM (1 << 0) @@ -278,10 +278,10 @@ class X_CDirectSoundStream LPVOID Xb_lpvContext; REFERENCE_TIME Xb_rtFlushEx; REFERENCE_TIME Xb_rtPauseEx; - LONG Xb_VolumeMixbin; + LONG Xb_VolumeMixbin; X_DSENVOLOPEDESC Xb_EnvolopeDesc; - X_DSVOICEPROPS Xb_VoiceProperties; - DWORD Host_dwLastWritePos; + X_DSVOICEPROPS Xb_VoiceProperties; + DWORD Host_dwLastWritePos; DWORD Xb_Flags; DWORD Xb_Status; }; @@ -1682,55 +1682,55 @@ HRESULT WINAPI EMUPATCH(IDirectSoundStream_SetFrequency) HRESULT WINAPI EMUPATCH(IDirectSoundStream_SetMixBins) ( X_CDirectSoundStream* pThis, - DWORD dwMixBinMask); - + DWORD dwMixBinMask); + // ****************************************************************** // * patch: CDirectSound3DCalculator_Calculate3D // ****************************************************************** VOID WINAPI EMUPATCH(CDirectSound3DCalculator_Calculate3D) ( DWORD a1, - DWORD a2); - + DWORD a2); + // ****************************************************************** // * patch: CDirectSound3DCalculator_GetVoiceData // ****************************************************************** VOID WINAPI EMUPATCH(CDirectSound3DCalculator_GetVoiceData) ( DWORD a1, - DWORD a2, - DWORD a3, - DWORD a4, - DWORD a5); - + DWORD a2, + DWORD a3, + DWORD a4, + DWORD a5); + // ****************************************************************** // * patch: IDirectSoundBuffer_Set3DVoiceData // ****************************************************************** HRESULT WINAPI EMUPATCH(IDirectSoundBuffer_Set3DVoiceData) ( XbHybridDSBuffer* pHybridThis, - DWORD a2); - + DWORD a2); + // ****************************************************************** // * patch: IDirectSoundStream_Set3DVoiceData // ****************************************************************** HRESULT WINAPI EMUPATCH(IDirectSoundStream_Set3DVoiceData) ( X_CDirectSoundStream* pThis, - DWORD a2 -); - + DWORD a2 +); + // ****************************************************************** // * patch: IDirectSoundStrea,_Use3DVoiceData // ****************************************************************** HRESULT WINAPI EMUPATCH(IDirectSoundStream_Use3DVoiceData) ( X_CDirectSoundStream* pThis, - DWORD a2 -); - -} // end of extern "C" + DWORD a2 +); -} // end of namespace XTL - -#endif +} // end of extern "C" + +} // end of namespace XTL + +#endif diff --git a/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.cpp b/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.cpp index 9235839b5..66401a617 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.cpp @@ -1,224 +1,224 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// * -// * (c) 2019 RadWolfie -// * -// * All rights reserved -// * -// ****************************************************************** - -#include "Logging.h" -#include "DirectSoundLogging.hpp" -#include -#include "..\XbDSoundTypes.h" - -// For MiNGW -#ifndef DSBCAPS_TRUEPLAYPOSITION -#define DSBCAPS_TRUEPLAYPOSITION 0x00080000 -#endif - -FLAGS2STR_START(DS_BCAPS) - FLAG2STR(DSBCAPS_PRIMARYBUFFER) - FLAG2STR(DSBCAPS_STATIC) - FLAG2STR(DSBCAPS_LOCHARDWARE) - FLAG2STR(DSBCAPS_LOCSOFTWARE) - FLAG2STR(DSBCAPS_CTRL3D) - FLAG2STR(DSBCAPS_CTRLFREQUENCY) - FLAG2STR(DSBCAPS_CTRLPAN) - FLAG2STR(DSBCAPS_CTRLVOLUME) - FLAG2STR(DSBCAPS_CTRLPOSITIONNOTIFY) - FLAG2STR(DSBCAPS_CTRLFX) - FLAG2STR(DSBCAPS_STICKYFOCUS) - FLAG2STR(DSBCAPS_GLOBALFOCUS) - FLAG2STR(DSBCAPS_GETCURRENTPOSITION2) - FLAG2STR(DSBCAPS_MUTE3DATMAXDISTANCE) - FLAG2STR(DSBCAPS_LOCDEFER) - FLAG2STR(DSBCAPS_TRUEPLAYPOSITION) -FLAGS2STR_END_and_LOGRENDER(DS_BCAPS) - -ENUM2STR_START(DS_RESULT) - ENUM2STR_CASE(DS_OK) - ENUM2STR_CASE(DS_NO_VIRTUALIZATION) - ENUM2STR_CASE(DSERR_ALLOCATED) - ENUM2STR_CASE(DSERR_CONTROLUNAVAIL) - ENUM2STR_CASE(DSERR_INVALIDPARAM) - ENUM2STR_CASE(DSERR_INVALIDCALL) - ENUM2STR_CASE(DSERR_GENERIC) - ENUM2STR_CASE(DSERR_PRIOLEVELNEEDED) - ENUM2STR_CASE(DSERR_OUTOFMEMORY) - ENUM2STR_CASE(DSERR_BADFORMAT) - ENUM2STR_CASE(DSERR_UNSUPPORTED) - ENUM2STR_CASE(DSERR_NODRIVER) - ENUM2STR_CASE(DSERR_ALREADYINITIALIZED) - ENUM2STR_CASE(DSERR_NOAGGREGATION) - ENUM2STR_CASE(DSERR_BUFFERLOST) - ENUM2STR_CASE(DSERR_OTHERAPPHASPRIO) - ENUM2STR_CASE(DSERR_UNINITIALIZED) - ENUM2STR_CASE(DSERR_NOINTERFACE) - ENUM2STR_CASE(DSERR_ACCESSDENIED) - ENUM2STR_CASE(DSERR_BUFFERTOOSMALL) - ENUM2STR_CASE(DSERR_DS8_REQUIRED) - ENUM2STR_CASE(DSERR_SENDLOOP) - ENUM2STR_CASE(DSERR_BADSENDBUFFERGUID) - ENUM2STR_CASE(DSERR_OBJECTNOTFOUND) - ENUM2STR_CASE(DSERR_FXUNAVAILABLE) -ENUM2STR_END_and_LOGRENDER(DS_RESULT) - -ENUM2STR_START(WAVEFORMAT_TAG) - ENUM2STR_CASE(0) - ENUM2STR_CASE(WAVE_FORMAT_PCM) - ENUM2STR_CASE(WAVE_FORMAT_ADPCM) - ENUM2STR_CASE(WAVE_FORMAT_XBOX_ADPCM) - ENUM2STR_CASE(WAVE_FORMAT_EXTENSIBLE) -ENUM2STR_END_and_LOGRENDER(WAVEFORMAT_TAG) - -#define WFC_MONO 1 -#define WFC_STEREO 2 -#define WFC_2POINT1 3 -#define WFC_QUAD 4 -#define WFC_5POINT0 5 -#define WFC_5POINT1 6 - -ENUM2STR_START(WAVEFORMAT_CHANNEL) - ENUM2STR_CASE(WFC_MONO) - ENUM2STR_CASE(WFC_STEREO) - ENUM2STR_CASE(WFC_2POINT1) - ENUM2STR_CASE(WFC_QUAD) - ENUM2STR_CASE(WFC_5POINT0) - ENUM2STR_CASE(WFC_5POINT1) -ENUM2STR_END_and_LOGRENDER(WAVEFORMAT_CHANNEL) - -LOGRENDER(WAVEFORMATEX) -{ - return os - LOGRENDER_MEMBER_TYPE(WAVEFORMAT_TAG, wFormatTag) - LOGRENDER_MEMBER_TYPE(WAVEFORMAT_CHANNEL, nChannels) - LOGRENDER_MEMBER(nSamplesPerSec) - LOGRENDER_MEMBER(nAvgBytesPerSec) - LOGRENDER_MEMBER(nBlockAlign) - LOGRENDER_MEMBER(wBitsPerSample) - LOGRENDER_MEMBER(cbSize) - ; -} - -LOGRENDER(GUID) -{ - return os - LOGRENDER_MEMBER(Data1) - LOGRENDER_MEMBER(Data2) - LOGRENDER_MEMBER(Data3) - LOGRENDER_MEMBER(Data4) - ; -} - -LOGRENDER(WAVEFORMATEXTENSIBLE) -{ - if (value.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - return os - LOGRENDER_MEMBER_TYPE(WAVEFORMATEX, Format) - LOGRENDER_MEMBER(Samples.wValidBitsPerSample) - LOGRENDER_MEMBER(dwChannelMask) - LOGRENDER_MEMBER_TYPE(GUID, SubFormat) - ; - } - else { - return os - LOGRENDER_MEMBER_TYPE(WAVEFORMATEX, Format) - ; - } -} - -LOGRENDER(DSBUFFERDESC) -{ - return os - LOGRENDER_MEMBER(dwSize) - LOGRENDER_MEMBER_TYPE(DS_BCAPS, dwFlags) - LOGRENDER_MEMBER(dwBufferBytes) - LOGRENDER_MEMBER(dwReserved) - LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) - ; -} - -LOGRENDER(D3DVECTOR) -{ - return os - LOGRENDER_MEMBER(x) - LOGRENDER_MEMBER(y) - LOGRENDER_MEMBER(z) - ; -} - -std::string DirectSoundErrorString(HRESULT hResult) -{ - switch (hResult) { - case DS_OK: - return ""; - case DS_NO_VIRTUALIZATION: - return "The call succeeded, but we had to substitute the 3D algorithm"; - case DSERR_ALLOCATED: - return "The call failed because resources(such as a priority level) were already being used by another caller"; - case DSERR_CONTROLUNAVAIL: - return "The control (vol, pan, etc.) requested by the caller is not available"; - case DSERR_INVALIDPARAM: - return "An invalid parameter was passed to the returning function"; - case DSERR_INVALIDCALL: - return "This call is not valid for the current state of this object"; - case DSERR_GENERIC: - return "An undetermined error occurred inside the DirectSound subsystem"; - case DSERR_PRIOLEVELNEEDED: - return "The caller does not have the priority level required for the function to succeed"; - case DSERR_OUTOFMEMORY: - return "Not enough free memory is available to complete the operation"; - case DSERR_BADFORMAT: - return "The specified WAVE format is not supported"; - case DSERR_UNSUPPORTED: - return "The function called is not supported at this time"; - case DSERR_NODRIVER: - return "No sound driver is available for use"; - case DSERR_ALREADYINITIALIZED: - return "This object is already initialized"; - case DSERR_NOAGGREGATION: - return "This object does not support aggregation"; - case DSERR_BUFFERLOST: - return "The buffer memory has been lost, and must be restored"; - case DSERR_OTHERAPPHASPRIO: - return "Another app has a higher priority level, preventing this call from succeeding"; - case DSERR_UNINITIALIZED: - return "This object has not been initialized"; - case DSERR_NOINTERFACE: - return "The requested COM interface is not available"; - case DSERR_ACCESSDENIED: - return "Access is denied"; - case DSERR_BUFFERTOOSMALL: - return "Tried to create a DSBCAPS_CTRLFX buffer shorter than DSBSIZE_FX_MIN milliseconds"; - case DSERR_DS8_REQUIRED: - return "Attempt to use DirectSound 8 functionality on an older DirectSound object"; - case DSERR_SENDLOOP: - return "A circular loop of send effects was detected"; - case DSERR_BADSENDBUFFERGUID: - return "The GUID specified in an audiopath file does not match a valid MIXIN buffer"; - case DSERR_OBJECTNOTFOUND: - return "The object requested was not found (numerically equal to DMUS_E_NOT_FOUND)"; - case DSERR_FXUNAVAILABLE: - return "The effects requested could not be found on the system, or they were found" - "but in the wrong order, or in the wrong hardware/software locations."; - default: - return "Unknown error"; - } -} +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** + +#include "Logging.h" +#include "DirectSoundLogging.hpp" +#include +#include "..\XbDSoundTypes.h" + +// For MiNGW +#ifndef DSBCAPS_TRUEPLAYPOSITION +#define DSBCAPS_TRUEPLAYPOSITION 0x00080000 +#endif + +FLAGS2STR_START(DS_BCAPS) + FLAG2STR(DSBCAPS_PRIMARYBUFFER) + FLAG2STR(DSBCAPS_STATIC) + FLAG2STR(DSBCAPS_LOCHARDWARE) + FLAG2STR(DSBCAPS_LOCSOFTWARE) + FLAG2STR(DSBCAPS_CTRL3D) + FLAG2STR(DSBCAPS_CTRLFREQUENCY) + FLAG2STR(DSBCAPS_CTRLPAN) + FLAG2STR(DSBCAPS_CTRLVOLUME) + FLAG2STR(DSBCAPS_CTRLPOSITIONNOTIFY) + FLAG2STR(DSBCAPS_CTRLFX) + FLAG2STR(DSBCAPS_STICKYFOCUS) + FLAG2STR(DSBCAPS_GLOBALFOCUS) + FLAG2STR(DSBCAPS_GETCURRENTPOSITION2) + FLAG2STR(DSBCAPS_MUTE3DATMAXDISTANCE) + FLAG2STR(DSBCAPS_LOCDEFER) + FLAG2STR(DSBCAPS_TRUEPLAYPOSITION) +FLAGS2STR_END_and_LOGRENDER(DS_BCAPS) + +ENUM2STR_START(DS_RESULT) + ENUM2STR_CASE(DS_OK) + ENUM2STR_CASE(DS_NO_VIRTUALIZATION) + ENUM2STR_CASE(DSERR_ALLOCATED) + ENUM2STR_CASE(DSERR_CONTROLUNAVAIL) + ENUM2STR_CASE(DSERR_INVALIDPARAM) + ENUM2STR_CASE(DSERR_INVALIDCALL) + ENUM2STR_CASE(DSERR_GENERIC) + ENUM2STR_CASE(DSERR_PRIOLEVELNEEDED) + ENUM2STR_CASE(DSERR_OUTOFMEMORY) + ENUM2STR_CASE(DSERR_BADFORMAT) + ENUM2STR_CASE(DSERR_UNSUPPORTED) + ENUM2STR_CASE(DSERR_NODRIVER) + ENUM2STR_CASE(DSERR_ALREADYINITIALIZED) + ENUM2STR_CASE(DSERR_NOAGGREGATION) + ENUM2STR_CASE(DSERR_BUFFERLOST) + ENUM2STR_CASE(DSERR_OTHERAPPHASPRIO) + ENUM2STR_CASE(DSERR_UNINITIALIZED) + ENUM2STR_CASE(DSERR_NOINTERFACE) + ENUM2STR_CASE(DSERR_ACCESSDENIED) + ENUM2STR_CASE(DSERR_BUFFERTOOSMALL) + ENUM2STR_CASE(DSERR_DS8_REQUIRED) + ENUM2STR_CASE(DSERR_SENDLOOP) + ENUM2STR_CASE(DSERR_BADSENDBUFFERGUID) + ENUM2STR_CASE(DSERR_OBJECTNOTFOUND) + ENUM2STR_CASE(DSERR_FXUNAVAILABLE) +ENUM2STR_END_and_LOGRENDER(DS_RESULT) + +ENUM2STR_START(WAVEFORMAT_TAG) + ENUM2STR_CASE(0) + ENUM2STR_CASE(WAVE_FORMAT_PCM) + ENUM2STR_CASE(WAVE_FORMAT_ADPCM) + ENUM2STR_CASE(WAVE_FORMAT_XBOX_ADPCM) + ENUM2STR_CASE(WAVE_FORMAT_EXTENSIBLE) +ENUM2STR_END_and_LOGRENDER(WAVEFORMAT_TAG) + +#define WFC_MONO 1 +#define WFC_STEREO 2 +#define WFC_2POINT1 3 +#define WFC_QUAD 4 +#define WFC_5POINT0 5 +#define WFC_5POINT1 6 + +ENUM2STR_START(WAVEFORMAT_CHANNEL) + ENUM2STR_CASE(WFC_MONO) + ENUM2STR_CASE(WFC_STEREO) + ENUM2STR_CASE(WFC_2POINT1) + ENUM2STR_CASE(WFC_QUAD) + ENUM2STR_CASE(WFC_5POINT0) + ENUM2STR_CASE(WFC_5POINT1) +ENUM2STR_END_and_LOGRENDER(WAVEFORMAT_CHANNEL) + +LOGRENDER(WAVEFORMATEX) +{ + return os + LOGRENDER_MEMBER_TYPE(WAVEFORMAT_TAG, wFormatTag) + LOGRENDER_MEMBER_TYPE(WAVEFORMAT_CHANNEL, nChannels) + LOGRENDER_MEMBER(nSamplesPerSec) + LOGRENDER_MEMBER(nAvgBytesPerSec) + LOGRENDER_MEMBER(nBlockAlign) + LOGRENDER_MEMBER(wBitsPerSample) + LOGRENDER_MEMBER(cbSize) + ; +} + +LOGRENDER(GUID) +{ + return os + LOGRENDER_MEMBER(Data1) + LOGRENDER_MEMBER(Data2) + LOGRENDER_MEMBER(Data3) + LOGRENDER_MEMBER(Data4) + ; +} + +LOGRENDER(WAVEFORMATEXTENSIBLE) +{ + if (value.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + return os + LOGRENDER_MEMBER_TYPE(WAVEFORMATEX, Format) + LOGRENDER_MEMBER(Samples.wValidBitsPerSample) + LOGRENDER_MEMBER(dwChannelMask) + LOGRENDER_MEMBER_TYPE(GUID, SubFormat) + ; + } + else { + return os + LOGRENDER_MEMBER_TYPE(WAVEFORMATEX, Format) + ; + } +} + +LOGRENDER(DSBUFFERDESC) +{ + return os + LOGRENDER_MEMBER(dwSize) + LOGRENDER_MEMBER_TYPE(DS_BCAPS, dwFlags) + LOGRENDER_MEMBER(dwBufferBytes) + LOGRENDER_MEMBER(dwReserved) + LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) + ; +} + +LOGRENDER(D3DVECTOR) +{ + return os + LOGRENDER_MEMBER(x) + LOGRENDER_MEMBER(y) + LOGRENDER_MEMBER(z) + ; +} + +std::string DirectSoundErrorString(HRESULT hResult) +{ + switch (hResult) { + case DS_OK: + return ""; + case DS_NO_VIRTUALIZATION: + return "The call succeeded, but we had to substitute the 3D algorithm"; + case DSERR_ALLOCATED: + return "The call failed because resources(such as a priority level) were already being used by another caller"; + case DSERR_CONTROLUNAVAIL: + return "The control (vol, pan, etc.) requested by the caller is not available"; + case DSERR_INVALIDPARAM: + return "An invalid parameter was passed to the returning function"; + case DSERR_INVALIDCALL: + return "This call is not valid for the current state of this object"; + case DSERR_GENERIC: + return "An undetermined error occurred inside the DirectSound subsystem"; + case DSERR_PRIOLEVELNEEDED: + return "The caller does not have the priority level required for the function to succeed"; + case DSERR_OUTOFMEMORY: + return "Not enough free memory is available to complete the operation"; + case DSERR_BADFORMAT: + return "The specified WAVE format is not supported"; + case DSERR_UNSUPPORTED: + return "The function called is not supported at this time"; + case DSERR_NODRIVER: + return "No sound driver is available for use"; + case DSERR_ALREADYINITIALIZED: + return "This object is already initialized"; + case DSERR_NOAGGREGATION: + return "This object does not support aggregation"; + case DSERR_BUFFERLOST: + return "The buffer memory has been lost, and must be restored"; + case DSERR_OTHERAPPHASPRIO: + return "Another app has a higher priority level, preventing this call from succeeding"; + case DSERR_UNINITIALIZED: + return "This object has not been initialized"; + case DSERR_NOINTERFACE: + return "The requested COM interface is not available"; + case DSERR_ACCESSDENIED: + return "Access is denied"; + case DSERR_BUFFERTOOSMALL: + return "Tried to create a DSBCAPS_CTRLFX buffer shorter than DSBSIZE_FX_MIN milliseconds"; + case DSERR_DS8_REQUIRED: + return "Attempt to use DirectSound 8 functionality on an older DirectSound object"; + case DSERR_SENDLOOP: + return "A circular loop of send effects was detected"; + case DSERR_BADSENDBUFFERGUID: + return "The GUID specified in an audiopath file does not match a valid MIXIN buffer"; + case DSERR_OBJECTNOTFOUND: + return "The object requested was not found (numerically equal to DMUS_E_NOT_FOUND)"; + case DSERR_FXUNAVAILABLE: + return "The effects requested could not be found on the system, or they were found" + "but in the wrong order, or in the wrong hardware/software locations."; + default: + return "Unknown error"; + } +} diff --git a/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.hpp b/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.hpp index b6b69ada3..a1dc76d62 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.hpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSoundLogging.hpp @@ -1,49 +1,49 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// * -// * (c) 2019 RadWolfie -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef DIRECTSOUNDLOGGING_H -#define DIRECTSOUNDLOGGING_H - -#include -#include -#include "Logging.h" - -enum DS_BCAPS : int; -enum DS_RESULT : int; -enum WAVEFORMAT_CHANNEL : int; -enum WAVEFORMAT_TAG : int; -FLAGS2STR_HEADER(DS_BCAPS) -ENUM2STR_HEADER(DS_RESULT) -ENUM2STR_HEADER(WAVEFORMAT_CHANNEL) -ENUM2STR_HEADER(WAVEFORMAT_TAG) - -LOGRENDER_HEADER(GUID) -LOGRENDER_HEADER(WAVEFORMATEX) -LOGRENDER_HEADER(WAVEFORMATEXTENSIBLE) -LOGRENDER_HEADER(DSBUFFERDESC) -LOGRENDER_HEADER(D3DVECTOR) - -std::string DirectSoundErrorString(HRESULT hResult); - -#endif +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef DIRECTSOUNDLOGGING_H +#define DIRECTSOUNDLOGGING_H + +#include +#include +#include "Logging.h" + +enum DS_BCAPS : int; +enum DS_RESULT : int; +enum WAVEFORMAT_CHANNEL : int; +enum WAVEFORMAT_TAG : int; +FLAGS2STR_HEADER(DS_BCAPS) +ENUM2STR_HEADER(DS_RESULT) +ENUM2STR_HEADER(WAVEFORMAT_CHANNEL) +ENUM2STR_HEADER(WAVEFORMAT_TAG) + +LOGRENDER_HEADER(GUID) +LOGRENDER_HEADER(WAVEFORMATEX) +LOGRENDER_HEADER(WAVEFORMATEXTENSIBLE) +LOGRENDER_HEADER(DSBUFFERDESC) +LOGRENDER_HEADER(D3DVECTOR) + +std::string DirectSoundErrorString(HRESULT hResult); + +#endif diff --git a/src/core/hle/DSOUND/XbDSoundLogging.cpp b/src/core/hle/DSOUND/XbDSoundLogging.cpp index fcbf85675..df768ff93 100644 --- a/src/core/hle/DSOUND/XbDSoundLogging.cpp +++ b/src/core/hle/DSOUND/XbDSoundLogging.cpp @@ -1,389 +1,389 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// * -// * (c) 2019 RadWolfie -// * -// * All rights reserved -// * -// ****************************************************************** - -#include // Temporary placeholder until XbDSoundTypes.h is cross-platform + filled in the blanks -#include "Logging.h" -#include "XbDSoundLogging.hpp" -#include "common/Settings.hpp" - -// For XTL::DSBUFFERDESC and XTL::DSSTREAMDESC temporary usage -extern LOGRENDER_HEADER(WAVEFORMATEX) -extern LOGRENDER_HEADER(D3DVECTOR) - -namespace XTL { - -// DSound class usage -ENUM2STR_START(DSMIXBIN_SPEAKER) - ENUM2STR_CASE(XDSMIXBIN_FRONT_LEFT) - ENUM2STR_CASE(XDSMIXBIN_FRONT_RIGHT) - ENUM2STR_CASE(XDSMIXBIN_FRONT_CENTER) - ENUM2STR_CASE(XDSMIXBIN_LOW_FREQUENCY) - ENUM2STR_CASE(XDSMIXBIN_BACK_LEFT) - ENUM2STR_CASE(XDSMIXBIN_BACK_RIGHT) - //ENUM2STR_CASE(XDSMIXBIN_SPEAKERS_MAX) // NOTE: Only used as a counter. -ENUM2STR_END_and_LOGRENDER(DSMIXBIN_SPEAKER) - -FLAGS2STR_START(DSSPEAKER_FLAG) - FLAG2STR(X_DSSPEAKER_STEREO) - FLAG2STR(X_DSSPEAKER_MONO) - FLAG2STR(X_DSSPEAKER_SURROUND) - FLAG2STR(X_DSSPEAKER_ENABLE_AC3) - FLAG2STR(X_DSSPEAKER_ENABLE_DTS) -FLAGS2STR_END_and_LOGRENDER(DSSPEAKER_FLAG) - -// DSound generic flag/enum -ENUM2STR_START(DSFILTER_MODE) - ENUM2STR_CASE(DSFILTER_MODE_BYPASS) - ENUM2STR_CASE(DSFILTER_MODE_DLS2) - ENUM2STR_CASE(DSFILTER_MODE_PARAMEQ) - ENUM2STR_CASE(DSFILTER_MODE_MULTI) -ENUM2STR_END_and_LOGRENDER(DSFILTER_MODE) - -// DSound Buffer flag/enum -FLAGS2STR_START(DSBCAPS_FLAG) - FLAG2STR(XTL_DSBCAPS_CTRL3D) - FLAG2STR(XTL_DSBCAPS_CTRLFREQUENCY) - FLAG2STR(XTL_DSBCAPS_CTRLVOLUME) - FLAG2STR(XTL_DSBCAPS_CTRLPOSITIONNOTIFY) - FLAG2STR(XTL_DSBCAPS_MIXIN) - FLAG2STR(XTL_DSBCAPS_MUTE3DATMAXDISTANCE) - FLAG2STR(XTL_DSBCAPS_LOCDEFER) - FLAG2STR(XTL_DSBCAPS_FXIN) - FLAG2STR(XTL_DSBCAPS_FXIN2) -FLAGS2STR_END_and_LOGRENDER(DSBCAPS_FLAG) - -FLAGS2STR_START(DSBPAUSE_FLAG) - FLAG2STR(X_DSBPAUSE_RESUME) - FLAG2STR(X_DSBPAUSE_PAUSE) - FLAG2STR(X_DSBPAUSE_SYNCHPLAYBACK) -FLAGS2STR_END_and_LOGRENDER(DSBPAUSE_FLAG) - -FLAGS2STR_START(DSBPLAY_FLAG) - FLAG2STR(X_DSBPLAY_LOOPING) - FLAG2STR(X_DSBPLAY_FROMSTART) - FLAG2STR(X_DSBPLAY_SYNCHPLAYBACK) -FLAGS2STR_END_and_LOGRENDER(DSBPLAY_FLAG) - -FLAGS2STR_START(DSBSTATUS_FLAG) - FLAG2STR(X_DSBSTATUS_PLAYING) - FLAG2STR(X_DSBSTATUS_PAUSED) - FLAG2STR(X_DSBSTATUS_LOOPING) -FLAGS2STR_END_and_LOGRENDER(DSBSTATUS_FLAG) - -FLAGS2STR_START(DSBSTOPEX_FLAG) - FLAG2STR(X_DSBSTOPEX_IMMEDIATE) - FLAG2STR(X_DSBSTOPEX_ENVELOPE) - FLAG2STR(X_DSBSTOPEX_RELEASEWAVEFORM) - //FLAG2STR(X_DSBSTOPEX_ALL) // NOTE: Only a combine of envelope and releasewaveform flags together. -FLAGS2STR_END_and_LOGRENDER(DSBSTOPEX_FLAG) - -// DSound Stream flag/enum -FLAGS2STR_START(DSSCAPS_FLAG) - FLAG2STR(XTL_DSSCAPS_CTRL3D) - FLAG2STR(XTL_DSSCAPS_CTRLFREQUENCY) - FLAG2STR(XTL_DSSCAPS_CTRLVOLUME) - FLAG2STR(XTL_DSSCAPS_MUTE3DATMAXDISTANCE) - FLAG2STR(XTL_DSSCAPS_LOCDEFER) - FLAG2STR(XTL_DSSCAPS_NOMERGE) - FLAG2STR(XTL_DSSCAPS_ACCURATENOTIFY) -FLAGS2STR_END_and_LOGRENDER(DSSCAPS_FLAG) - -FLAGS2STR_START(DSSFLUSHEX_FLAG) - FLAG2STR(X_DSSFLUSHEX_IMMEDIATE) - FLAG2STR(X_DSSFLUSHEX_ASYNC) - FLAG2STR(X_DSSFLUSHEX_ENVELOPE) - FLAG2STR(X_DSSFLUSHEX_ENVELOPE2) -FLAGS2STR_END_and_LOGRENDER(DSSFLUSHEX_FLAG) - -FLAGS2STR_START(DSSPAUSE_FLAG) - FLAG2STR(X_DSSPAUSE_RESUME) - FLAG2STR(X_DSSPAUSE_PAUSE) - FLAG2STR(X_DSSPAUSE_SYNCHPLAYBACK) - FLAG2STR(X_DSSPAUSE_PAUSENOACTIVATE) -FLAGS2STR_END_and_LOGRENDER(DSSPAUSE_FLAG) - -FLAGS2STR_START(DSSSTATUS_FLAG) - FLAG2STR(X_DSSSTATUS_READY) - FLAG2STR(X_DSSSTATUS_PLAYING) - FLAG2STR(X_DSSSTATUS_PAUSED) - FLAG2STR(X_DSSSTATUS_STARVED) - FLAG2STR(X_DSSSTATUS_ENVELOPECOMPLETE) -FLAGS2STR_END_and_LOGRENDER(DSSSTATUS_FLAG) - -// DSound XMedia flag/enum -ENUM2STR_START(XMP_STATUS) - ENUM2STR_CASE(XMP_STATUS_SUCCESS) - ENUM2STR_CASE(XMP_STATUS_PENDING) - ENUM2STR_CASE(XMP_STATUS_FLUSHED) - ENUM2STR_CASE(XMP_STATUS_FAILURE) - ENUM2STR_CASE((int)XMP_STATUS_RELEASE_CXBXR) // NOTE: Custom status for Cxbx-Reloaded. -ENUM2STR_END_and_LOGRENDER(XMP_STATUS) - -FLAGS2STR_START(XMO_STREAMF) - FLAG2STR(XMO_STREAMF_FIXED_SAMPLE_SIZE) - FLAG2STR(XMO_STREAMF_FIXED_PACKET_ALIGNMENT) - FLAG2STR(XMO_STREAMF_INPUT_ASYNC) - FLAG2STR(XMO_STREAMF_OUTPUT_ASYNC) - FLAG2STR(XMO_STREAMF_IN_PLACE) - FLAG2STR(XMO_STREAMF_MASK) -FLAGS2STR_END_and_LOGRENDER(XMO_STREAMF) - -// DSound class usage -LOGRENDER(X_DSCAPS) -{ - return os - LOGRENDER_MEMBER(dwFree2DBuffers) - LOGRENDER_MEMBER(dwFree3DBuffers) - LOGRENDER_MEMBER(dwFreeBufferSGEs) - LOGRENDER_MEMBER(dwMemoryAllocated) - ; -} - -LOGRENDER(X_DSI3DL2LISTENER) -{ - return os - LOGRENDER_MEMBER(lRoom) - LOGRENDER_MEMBER(lRoomHF) - LOGRENDER_MEMBER(flRoomRolloffFactor) - LOGRENDER_MEMBER(flDecayTime) - LOGRENDER_MEMBER(flDecayHFRatio) - LOGRENDER_MEMBER(lReflections) - LOGRENDER_MEMBER(flReflectionsDelay) - LOGRENDER_MEMBER(lReverb) - LOGRENDER_MEMBER(flReverbDelay) - LOGRENDER_MEMBER(flDiffusion) - LOGRENDER_MEMBER(flDensity) - LOGRENDER_MEMBER(flHFReference) - ; -} - -LOGRENDER(X_DSMIXBINS) -{ - return os - LOGRENDER_MEMBER(dwCount) - LOGRENDER_MEMBER_ARRAY_TYPE(X_DSMIXBINVOLUMEPAIR, lpMixBinVolumePairs, dwCount) - ; -} - -LOGRENDER(X_DSMIXBINVOLUMEPAIR) -{ - return os - LOGRENDER_MEMBER(dwMixBin) - LOGRENDER_MEMBER(lVolume) - ; -} - -LOGRENDER(X_DSOUTPUTLEVELS) -{ - return os - LOGRENDER_MEMBER(dwAnalogLeftTotalPeak) - LOGRENDER_MEMBER(dwAnalogRightTotalPeak) - LOGRENDER_MEMBER(dwAnalogLeftTotalRMS) - LOGRENDER_MEMBER(dwAnalogRightTotalRMS) - LOGRENDER_MEMBER(dwDigitalFrontLeftPeak) - LOGRENDER_MEMBER(dwDigitalFrontCenterPeak) - LOGRENDER_MEMBER(dwDigitalFrontRightPeak) - LOGRENDER_MEMBER(dwDigitalBackLeftPeak) - LOGRENDER_MEMBER(dwDigitalBackRightPeak) - LOGRENDER_MEMBER(dwDigitalLowFrequencyPeak) - LOGRENDER_MEMBER(dwDigitalFrontLeftRMS) - LOGRENDER_MEMBER(dwDigitalFrontCenterRMS) - LOGRENDER_MEMBER(dwDigitalFrontRightRMS) - LOGRENDER_MEMBER(dwDigitalBackLeftRMS) - LOGRENDER_MEMBER(dwDigitalBackRightRMS) - LOGRENDER_MEMBER(dwDigitalLowFrequencyRMS) - ; -} - -// DSound generic class usage -LOGRENDER(X_DS3DBUFFER) -{ - return os - LOGRENDER_MEMBER(dwSize) - LOGRENDER_MEMBER_TYPE(D3DVECTOR, vPosition) - LOGRENDER_MEMBER_TYPE(D3DVECTOR, vVelocity) - LOGRENDER_MEMBER(dwInsideConeAngle) - LOGRENDER_MEMBER(dwOutsideConeAngle) - LOGRENDER_MEMBER_TYPE(D3DVECTOR, vConeOrientation) - LOGRENDER_MEMBER(lConeOutsideVolume) - LOGRENDER_MEMBER(flMinDistance) - LOGRENDER_MEMBER(flMaxDistance) - LOGRENDER_MEMBER(dwMode) - LOGRENDER_MEMBER(flDistanceFactor) - LOGRENDER_MEMBER(flRolloffFactor) - LOGRENDER_MEMBER(flDopplerFactor) - ; -} - -LOGRENDER(X_DSENVOLOPEDESC) -{ - return os - LOGRENDER_MEMBER(dwEnvelopGenerator) - LOGRENDER_MEMBER(dwMode) - LOGRENDER_MEMBER(dwDelay) - LOGRENDER_MEMBER(dwAttack) - LOGRENDER_MEMBER(dwHold) - LOGRENDER_MEMBER(dwDecay) - LOGRENDER_MEMBER(dwRelease) - LOGRENDER_MEMBER(dwSustain) - LOGRENDER_MEMBER(lPitchScale) - LOGRENDER_MEMBER(lFilterCutOff) - ; -} - -LOGRENDER(X_DSFILTERDESC) -{ - return os - LOGRENDER_MEMBER_TYPE(DSFILTER_MODE, dwMode) - LOGRENDER_MEMBER(dwQCoefficient) - LOGRENDER_MEMBER(adwCoefficients) - ; -} - -LOGRENDER(X_DSI3DL2BUFFER) -{ - return os - LOGRENDER_MEMBER(lDirect) - LOGRENDER_MEMBER(lDirectHF) - LOGRENDER_MEMBER(lRoom) - LOGRENDER_MEMBER(lRoomHF) - LOGRENDER_MEMBER(flRoomRolloffFactor) - LOGRENDER_MEMBER_TYPE(X_DSI3DL2OBSTRUCTION, Obstruction) - LOGRENDER_MEMBER_TYPE(X_DSI3DL2OCCLUSION, Occlusion) - ; -} - -LOGRENDER(X_DSI3DL2OBSTRUCTION) -{ - return os - LOGRENDER_MEMBER(lHFLevel) - LOGRENDER_MEMBER(flLFRatio) - ; -} - -LOGRENDER(X_DSI3DL2OCCLUSION) -{ - return os - LOGRENDER_MEMBER(lHFLevel) - LOGRENDER_MEMBER(flLFRatio) - ; -} - -LOGRENDER(DSLFODESC) -{ - return os - LOGRENDER_MEMBER(dwLFO) - LOGRENDER_MEMBER(dwDelay) - LOGRENDER_MEMBER(dwDelta) - LOGRENDER_MEMBER(lPitchModulation) - LOGRENDER_MEMBER(lFilterCutOffRange) - LOGRENDER_MEMBER(lAmplitudeModulation) - ; -} - -LOGRENDER(XBOXADPCMWAVEFORMAT) -{ - return os - LOGRENDER_MEMBER_TYPE(WAVEFORMATEX, wfx) - LOGRENDER_MEMBER(wSamplesPerBlock) - ; -} - -// DSound Buffer class usage -LOGRENDER(X_DSBUFFERDESC) -{ - if (g_LibVersion_DSOUND < 4039) { - return os - LOGRENDER_MEMBER(dwSize) - LOGRENDER_MEMBER_TYPE(DSBCAPS_FLAG, dwFlags) - LOGRENDER_MEMBER(dwBufferBytes) - LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) - LOGRENDER_MEMBER_TYPE(DWORD, lpMixBinsOutput) - LOGRENDER_MEMBER(dwInputMixBin) - ; - } - else { - return os - LOGRENDER_MEMBER(dwSize) - LOGRENDER_MEMBER_TYPE(DSBCAPS_FLAG, dwFlags) - LOGRENDER_MEMBER(dwBufferBytes) - LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) - LOGRENDER_MEMBER(lpMixBinsOutput) - LOGRENDER_MEMBER(dwInputMixBin) - ; - } -} - -// DSound Stream class usage -LOGRENDER(X_DSSTREAMDESC) -{ - if (g_LibVersion_DSOUND < 4039) { - return os - LOGRENDER_MEMBER_TYPE(DSSCAPS_FLAG, dwFlags) - LOGRENDER_MEMBER(dwMaxAttachedPackets) - LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) - LOGRENDER_MEMBER_TYPE(void*, lpfnCallback) - LOGRENDER_MEMBER(lpvContext) - LOGRENDER_MEMBER_TYPE(DWORD, lpMixBinsOutput) - ; - } - else { - return os - LOGRENDER_MEMBER_TYPE(DSSCAPS_FLAG, dwFlags) - LOGRENDER_MEMBER(dwMaxAttachedPackets) - LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) - LOGRENDER_MEMBER_TYPE(void*, lpfnCallback) - LOGRENDER_MEMBER(lpvContext) - LOGRENDER_MEMBER(lpMixBinsOutput) - ; - } -} - -// DSound XMedia class usage -LOGRENDER(XMEDIAINFO) -{ - return os - LOGRENDER_MEMBER(dwFlags) - LOGRENDER_MEMBER(dwInputSize) - LOGRENDER_MEMBER(dwOutputSize) - LOGRENDER_MEMBER(dwMaxLookahead) - ; -} - -LOGRENDER(XMEDIAPACKET) -{ - return os - LOGRENDER_MEMBER(pvBuffer) - LOGRENDER_MEMBER(dwMaxSize) - LOGRENDER_MEMBER(pdwCompletedSize) - LOGRENDER_MEMBER_TYPE(XMO_STREAMF*, pdwStatus) - // NOTE: hCompletionEvent and pContext are a union. - //LOGRENDER_MEMBER(hCompletionEvent) - //LOGRENDER_MEMBER(pContext) - LOGRENDER_MEMBER(prtTimestamp) - ; -} - -} +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** + +#include // Temporary placeholder until XbDSoundTypes.h is cross-platform + filled in the blanks +#include "Logging.h" +#include "XbDSoundLogging.hpp" +#include "common/Settings.hpp" + +// For XTL::DSBUFFERDESC and XTL::DSSTREAMDESC temporary usage +extern LOGRENDER_HEADER(WAVEFORMATEX) +extern LOGRENDER_HEADER(D3DVECTOR) + +namespace XTL { + +// DSound class usage +ENUM2STR_START(DSMIXBIN_SPEAKER) + ENUM2STR_CASE(XDSMIXBIN_FRONT_LEFT) + ENUM2STR_CASE(XDSMIXBIN_FRONT_RIGHT) + ENUM2STR_CASE(XDSMIXBIN_FRONT_CENTER) + ENUM2STR_CASE(XDSMIXBIN_LOW_FREQUENCY) + ENUM2STR_CASE(XDSMIXBIN_BACK_LEFT) + ENUM2STR_CASE(XDSMIXBIN_BACK_RIGHT) + //ENUM2STR_CASE(XDSMIXBIN_SPEAKERS_MAX) // NOTE: Only used as a counter. +ENUM2STR_END_and_LOGRENDER(DSMIXBIN_SPEAKER) + +FLAGS2STR_START(DSSPEAKER_FLAG) + FLAG2STR(X_DSSPEAKER_STEREO) + FLAG2STR(X_DSSPEAKER_MONO) + FLAG2STR(X_DSSPEAKER_SURROUND) + FLAG2STR(X_DSSPEAKER_ENABLE_AC3) + FLAG2STR(X_DSSPEAKER_ENABLE_DTS) +FLAGS2STR_END_and_LOGRENDER(DSSPEAKER_FLAG) + +// DSound generic flag/enum +ENUM2STR_START(DSFILTER_MODE) + ENUM2STR_CASE(DSFILTER_MODE_BYPASS) + ENUM2STR_CASE(DSFILTER_MODE_DLS2) + ENUM2STR_CASE(DSFILTER_MODE_PARAMEQ) + ENUM2STR_CASE(DSFILTER_MODE_MULTI) +ENUM2STR_END_and_LOGRENDER(DSFILTER_MODE) + +// DSound Buffer flag/enum +FLAGS2STR_START(DSBCAPS_FLAG) + FLAG2STR(XTL_DSBCAPS_CTRL3D) + FLAG2STR(XTL_DSBCAPS_CTRLFREQUENCY) + FLAG2STR(XTL_DSBCAPS_CTRLVOLUME) + FLAG2STR(XTL_DSBCAPS_CTRLPOSITIONNOTIFY) + FLAG2STR(XTL_DSBCAPS_MIXIN) + FLAG2STR(XTL_DSBCAPS_MUTE3DATMAXDISTANCE) + FLAG2STR(XTL_DSBCAPS_LOCDEFER) + FLAG2STR(XTL_DSBCAPS_FXIN) + FLAG2STR(XTL_DSBCAPS_FXIN2) +FLAGS2STR_END_and_LOGRENDER(DSBCAPS_FLAG) + +FLAGS2STR_START(DSBPAUSE_FLAG) + FLAG2STR(X_DSBPAUSE_RESUME) + FLAG2STR(X_DSBPAUSE_PAUSE) + FLAG2STR(X_DSBPAUSE_SYNCHPLAYBACK) +FLAGS2STR_END_and_LOGRENDER(DSBPAUSE_FLAG) + +FLAGS2STR_START(DSBPLAY_FLAG) + FLAG2STR(X_DSBPLAY_LOOPING) + FLAG2STR(X_DSBPLAY_FROMSTART) + FLAG2STR(X_DSBPLAY_SYNCHPLAYBACK) +FLAGS2STR_END_and_LOGRENDER(DSBPLAY_FLAG) + +FLAGS2STR_START(DSBSTATUS_FLAG) + FLAG2STR(X_DSBSTATUS_PLAYING) + FLAG2STR(X_DSBSTATUS_PAUSED) + FLAG2STR(X_DSBSTATUS_LOOPING) +FLAGS2STR_END_and_LOGRENDER(DSBSTATUS_FLAG) + +FLAGS2STR_START(DSBSTOPEX_FLAG) + FLAG2STR(X_DSBSTOPEX_IMMEDIATE) + FLAG2STR(X_DSBSTOPEX_ENVELOPE) + FLAG2STR(X_DSBSTOPEX_RELEASEWAVEFORM) + //FLAG2STR(X_DSBSTOPEX_ALL) // NOTE: Only a combine of envelope and releasewaveform flags together. +FLAGS2STR_END_and_LOGRENDER(DSBSTOPEX_FLAG) + +// DSound Stream flag/enum +FLAGS2STR_START(DSSCAPS_FLAG) + FLAG2STR(XTL_DSSCAPS_CTRL3D) + FLAG2STR(XTL_DSSCAPS_CTRLFREQUENCY) + FLAG2STR(XTL_DSSCAPS_CTRLVOLUME) + FLAG2STR(XTL_DSSCAPS_MUTE3DATMAXDISTANCE) + FLAG2STR(XTL_DSSCAPS_LOCDEFER) + FLAG2STR(XTL_DSSCAPS_NOMERGE) + FLAG2STR(XTL_DSSCAPS_ACCURATENOTIFY) +FLAGS2STR_END_and_LOGRENDER(DSSCAPS_FLAG) + +FLAGS2STR_START(DSSFLUSHEX_FLAG) + FLAG2STR(X_DSSFLUSHEX_IMMEDIATE) + FLAG2STR(X_DSSFLUSHEX_ASYNC) + FLAG2STR(X_DSSFLUSHEX_ENVELOPE) + FLAG2STR(X_DSSFLUSHEX_ENVELOPE2) +FLAGS2STR_END_and_LOGRENDER(DSSFLUSHEX_FLAG) + +FLAGS2STR_START(DSSPAUSE_FLAG) + FLAG2STR(X_DSSPAUSE_RESUME) + FLAG2STR(X_DSSPAUSE_PAUSE) + FLAG2STR(X_DSSPAUSE_SYNCHPLAYBACK) + FLAG2STR(X_DSSPAUSE_PAUSENOACTIVATE) +FLAGS2STR_END_and_LOGRENDER(DSSPAUSE_FLAG) + +FLAGS2STR_START(DSSSTATUS_FLAG) + FLAG2STR(X_DSSSTATUS_READY) + FLAG2STR(X_DSSSTATUS_PLAYING) + FLAG2STR(X_DSSSTATUS_PAUSED) + FLAG2STR(X_DSSSTATUS_STARVED) + FLAG2STR(X_DSSSTATUS_ENVELOPECOMPLETE) +FLAGS2STR_END_and_LOGRENDER(DSSSTATUS_FLAG) + +// DSound XMedia flag/enum +ENUM2STR_START(XMP_STATUS) + ENUM2STR_CASE(XMP_STATUS_SUCCESS) + ENUM2STR_CASE(XMP_STATUS_PENDING) + ENUM2STR_CASE(XMP_STATUS_FLUSHED) + ENUM2STR_CASE(XMP_STATUS_FAILURE) + ENUM2STR_CASE((int)XMP_STATUS_RELEASE_CXBXR) // NOTE: Custom status for Cxbx-Reloaded. +ENUM2STR_END_and_LOGRENDER(XMP_STATUS) + +FLAGS2STR_START(XMO_STREAMF) + FLAG2STR(XMO_STREAMF_FIXED_SAMPLE_SIZE) + FLAG2STR(XMO_STREAMF_FIXED_PACKET_ALIGNMENT) + FLAG2STR(XMO_STREAMF_INPUT_ASYNC) + FLAG2STR(XMO_STREAMF_OUTPUT_ASYNC) + FLAG2STR(XMO_STREAMF_IN_PLACE) + FLAG2STR(XMO_STREAMF_MASK) +FLAGS2STR_END_and_LOGRENDER(XMO_STREAMF) + +// DSound class usage +LOGRENDER(X_DSCAPS) +{ + return os + LOGRENDER_MEMBER(dwFree2DBuffers) + LOGRENDER_MEMBER(dwFree3DBuffers) + LOGRENDER_MEMBER(dwFreeBufferSGEs) + LOGRENDER_MEMBER(dwMemoryAllocated) + ; +} + +LOGRENDER(X_DSI3DL2LISTENER) +{ + return os + LOGRENDER_MEMBER(lRoom) + LOGRENDER_MEMBER(lRoomHF) + LOGRENDER_MEMBER(flRoomRolloffFactor) + LOGRENDER_MEMBER(flDecayTime) + LOGRENDER_MEMBER(flDecayHFRatio) + LOGRENDER_MEMBER(lReflections) + LOGRENDER_MEMBER(flReflectionsDelay) + LOGRENDER_MEMBER(lReverb) + LOGRENDER_MEMBER(flReverbDelay) + LOGRENDER_MEMBER(flDiffusion) + LOGRENDER_MEMBER(flDensity) + LOGRENDER_MEMBER(flHFReference) + ; +} + +LOGRENDER(X_DSMIXBINS) +{ + return os + LOGRENDER_MEMBER(dwCount) + LOGRENDER_MEMBER_ARRAY_TYPE(X_DSMIXBINVOLUMEPAIR, lpMixBinVolumePairs, dwCount) + ; +} + +LOGRENDER(X_DSMIXBINVOLUMEPAIR) +{ + return os + LOGRENDER_MEMBER(dwMixBin) + LOGRENDER_MEMBER(lVolume) + ; +} + +LOGRENDER(X_DSOUTPUTLEVELS) +{ + return os + LOGRENDER_MEMBER(dwAnalogLeftTotalPeak) + LOGRENDER_MEMBER(dwAnalogRightTotalPeak) + LOGRENDER_MEMBER(dwAnalogLeftTotalRMS) + LOGRENDER_MEMBER(dwAnalogRightTotalRMS) + LOGRENDER_MEMBER(dwDigitalFrontLeftPeak) + LOGRENDER_MEMBER(dwDigitalFrontCenterPeak) + LOGRENDER_MEMBER(dwDigitalFrontRightPeak) + LOGRENDER_MEMBER(dwDigitalBackLeftPeak) + LOGRENDER_MEMBER(dwDigitalBackRightPeak) + LOGRENDER_MEMBER(dwDigitalLowFrequencyPeak) + LOGRENDER_MEMBER(dwDigitalFrontLeftRMS) + LOGRENDER_MEMBER(dwDigitalFrontCenterRMS) + LOGRENDER_MEMBER(dwDigitalFrontRightRMS) + LOGRENDER_MEMBER(dwDigitalBackLeftRMS) + LOGRENDER_MEMBER(dwDigitalBackRightRMS) + LOGRENDER_MEMBER(dwDigitalLowFrequencyRMS) + ; +} + +// DSound generic class usage +LOGRENDER(X_DS3DBUFFER) +{ + return os + LOGRENDER_MEMBER(dwSize) + LOGRENDER_MEMBER_TYPE(D3DVECTOR, vPosition) + LOGRENDER_MEMBER_TYPE(D3DVECTOR, vVelocity) + LOGRENDER_MEMBER(dwInsideConeAngle) + LOGRENDER_MEMBER(dwOutsideConeAngle) + LOGRENDER_MEMBER_TYPE(D3DVECTOR, vConeOrientation) + LOGRENDER_MEMBER(lConeOutsideVolume) + LOGRENDER_MEMBER(flMinDistance) + LOGRENDER_MEMBER(flMaxDistance) + LOGRENDER_MEMBER(dwMode) + LOGRENDER_MEMBER(flDistanceFactor) + LOGRENDER_MEMBER(flRolloffFactor) + LOGRENDER_MEMBER(flDopplerFactor) + ; +} + +LOGRENDER(X_DSENVOLOPEDESC) +{ + return os + LOGRENDER_MEMBER(dwEnvelopGenerator) + LOGRENDER_MEMBER(dwMode) + LOGRENDER_MEMBER(dwDelay) + LOGRENDER_MEMBER(dwAttack) + LOGRENDER_MEMBER(dwHold) + LOGRENDER_MEMBER(dwDecay) + LOGRENDER_MEMBER(dwRelease) + LOGRENDER_MEMBER(dwSustain) + LOGRENDER_MEMBER(lPitchScale) + LOGRENDER_MEMBER(lFilterCutOff) + ; +} + +LOGRENDER(X_DSFILTERDESC) +{ + return os + LOGRENDER_MEMBER_TYPE(DSFILTER_MODE, dwMode) + LOGRENDER_MEMBER(dwQCoefficient) + LOGRENDER_MEMBER(adwCoefficients) + ; +} + +LOGRENDER(X_DSI3DL2BUFFER) +{ + return os + LOGRENDER_MEMBER(lDirect) + LOGRENDER_MEMBER(lDirectHF) + LOGRENDER_MEMBER(lRoom) + LOGRENDER_MEMBER(lRoomHF) + LOGRENDER_MEMBER(flRoomRolloffFactor) + LOGRENDER_MEMBER_TYPE(X_DSI3DL2OBSTRUCTION, Obstruction) + LOGRENDER_MEMBER_TYPE(X_DSI3DL2OCCLUSION, Occlusion) + ; +} + +LOGRENDER(X_DSI3DL2OBSTRUCTION) +{ + return os + LOGRENDER_MEMBER(lHFLevel) + LOGRENDER_MEMBER(flLFRatio) + ; +} + +LOGRENDER(X_DSI3DL2OCCLUSION) +{ + return os + LOGRENDER_MEMBER(lHFLevel) + LOGRENDER_MEMBER(flLFRatio) + ; +} + +LOGRENDER(DSLFODESC) +{ + return os + LOGRENDER_MEMBER(dwLFO) + LOGRENDER_MEMBER(dwDelay) + LOGRENDER_MEMBER(dwDelta) + LOGRENDER_MEMBER(lPitchModulation) + LOGRENDER_MEMBER(lFilterCutOffRange) + LOGRENDER_MEMBER(lAmplitudeModulation) + ; +} + +LOGRENDER(XBOXADPCMWAVEFORMAT) +{ + return os + LOGRENDER_MEMBER_TYPE(WAVEFORMATEX, wfx) + LOGRENDER_MEMBER(wSamplesPerBlock) + ; +} + +// DSound Buffer class usage +LOGRENDER(X_DSBUFFERDESC) +{ + if (g_LibVersion_DSOUND < 4039) { + return os + LOGRENDER_MEMBER(dwSize) + LOGRENDER_MEMBER_TYPE(DSBCAPS_FLAG, dwFlags) + LOGRENDER_MEMBER(dwBufferBytes) + LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) + LOGRENDER_MEMBER_TYPE(DWORD, lpMixBinsOutput) + LOGRENDER_MEMBER(dwInputMixBin) + ; + } + else { + return os + LOGRENDER_MEMBER(dwSize) + LOGRENDER_MEMBER_TYPE(DSBCAPS_FLAG, dwFlags) + LOGRENDER_MEMBER(dwBufferBytes) + LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) + LOGRENDER_MEMBER(lpMixBinsOutput) + LOGRENDER_MEMBER(dwInputMixBin) + ; + } +} + +// DSound Stream class usage +LOGRENDER(X_DSSTREAMDESC) +{ + if (g_LibVersion_DSOUND < 4039) { + return os + LOGRENDER_MEMBER_TYPE(DSSCAPS_FLAG, dwFlags) + LOGRENDER_MEMBER(dwMaxAttachedPackets) + LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) + LOGRENDER_MEMBER_TYPE(void*, lpfnCallback) + LOGRENDER_MEMBER(lpvContext) + LOGRENDER_MEMBER_TYPE(DWORD, lpMixBinsOutput) + ; + } + else { + return os + LOGRENDER_MEMBER_TYPE(DSSCAPS_FLAG, dwFlags) + LOGRENDER_MEMBER(dwMaxAttachedPackets) + LOGRENDER_MEMBER_TYPE(LPWAVEFORMATEX, lpwfxFormat) + LOGRENDER_MEMBER_TYPE(void*, lpfnCallback) + LOGRENDER_MEMBER(lpvContext) + LOGRENDER_MEMBER(lpMixBinsOutput) + ; + } +} + +// DSound XMedia class usage +LOGRENDER(XMEDIAINFO) +{ + return os + LOGRENDER_MEMBER(dwFlags) + LOGRENDER_MEMBER(dwInputSize) + LOGRENDER_MEMBER(dwOutputSize) + LOGRENDER_MEMBER(dwMaxLookahead) + ; +} + +LOGRENDER(XMEDIAPACKET) +{ + return os + LOGRENDER_MEMBER(pvBuffer) + LOGRENDER_MEMBER(dwMaxSize) + LOGRENDER_MEMBER(pdwCompletedSize) + LOGRENDER_MEMBER_TYPE(XMO_STREAMF*, pdwStatus) + // NOTE: hCompletionEvent and pContext are a union. + //LOGRENDER_MEMBER(hCompletionEvent) + //LOGRENDER_MEMBER(pContext) + LOGRENDER_MEMBER(prtTimestamp) + ; +} + +} diff --git a/src/core/hle/DSOUND/XbDSoundLogging.hpp b/src/core/hle/DSOUND/XbDSoundLogging.hpp index 2c771d77d..89e1d790b 100644 --- a/src/core/hle/DSOUND/XbDSoundLogging.hpp +++ b/src/core/hle/DSOUND/XbDSoundLogging.hpp @@ -1,94 +1,94 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// * -// * (c) 2019 RadWolfie -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef XBDSOUNDLOGGING_H -#define XBDSOUNDLOGGING_H - -#include "Logging.h" -#include "XbDSoundTypes.h" - -namespace XTL { - -// TODO: Everything, only small portions had been implemented. - -// DSound flag/enum -enum DSMIXBIN_SPEAKER : int; -enum DSSPEAKER_FLAG : int; -// DSound generic flag/enum -enum DSFILTER_MODE : int; -// DSound Buffer flag/enum -enum DSBCAPS_FLAG : int; -enum DSBPAUSE_FLAG : int; -enum DSBPLAY_FLAG : int; -enum DSBSTATUS_FLAG : int; -enum DSBSTOPEX_FLAG : int; -// DSound Stream flag/enum -enum DSSCAPS_FLAG : int; -enum DSSFLUSHEX_FLAG : int; -enum DSSPAUSE_FLAG : int; -enum DSSSTATUS_FLAG : int; -// DSound XMedia flag/enum -enum XMP_STATUS : int; -enum XMO_STREAMF : int; - -// DSound flag/enum -FLAGS2STR_HEADER(DSSPEAKER_FLAG) -// DSound generic flag/enum -ENUM2STR_HEADER(DSFILTER_MODE) -// DSound Buffer flag/enum -FLAGS2STR_HEADER(DSBCAPS_FLAG) -FLAGS2STR_HEADER(DSBPAUSE_FLAG) -FLAGS2STR_HEADER(DSBPLAY_FLAG) -FLAGS2STR_HEADER(DSBSTATUS_FLAG) -FLAGS2STR_HEADER(DSBSTOPEX_FLAG) -// DSound Stream flag/enum -FLAGS2STR_HEADER(DSSCAPS_FLAG) -FLAGS2STR_HEADER(DSSFLUSHEX_FLAG) -FLAGS2STR_HEADER(DSSPAUSE_FLAG) -FLAGS2STR_HEADER(DSSSTATUS_FLAG) - -// DSound class usage -LOGRENDER_HEADER(X_DSCAPS) -LOGRENDER_HEADER(X_DSI3DL2LISTENER) -LOGRENDER_HEADER(X_DSMIXBINS) -LOGRENDER_HEADER(X_DSMIXBINVOLUMEPAIR) -LOGRENDER_HEADER(X_DSOUTPUTLEVELS) -// DSound generic class usage -LOGRENDER_HEADER(X_DS3DBUFFER) -LOGRENDER_HEADER(X_DSENVOLOPEDESC) -LOGRENDER_HEADER(X_DSFILTERDESC) -LOGRENDER_HEADER(X_DSI3DL2BUFFER) -LOGRENDER_HEADER(X_DSI3DL2OBSTRUCTION) -LOGRENDER_HEADER(X_DSI3DL2OCCLUSION) -LOGRENDER_HEADER(DSLFODESC) -LOGRENDER_HEADER(XBOXADPCMWAVEFORMAT) -// DSound Buffer class usage -LOGRENDER_HEADER(X_DSBUFFERDESC) -// DSound Stream class usage -LOGRENDER_HEADER(X_DSSTREAMDESC) -// DSound XMedia class usage -LOGRENDER_HEADER(XMEDIAINFO) -LOGRENDER_HEADER(XMEDIAPACKET) -} -#endif +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// * +// * (c) 2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef XBDSOUNDLOGGING_H +#define XBDSOUNDLOGGING_H + +#include "Logging.h" +#include "XbDSoundTypes.h" + +namespace XTL { + +// TODO: Everything, only small portions had been implemented. + +// DSound flag/enum +enum DSMIXBIN_SPEAKER : int; +enum DSSPEAKER_FLAG : int; +// DSound generic flag/enum +enum DSFILTER_MODE : int; +// DSound Buffer flag/enum +enum DSBCAPS_FLAG : int; +enum DSBPAUSE_FLAG : int; +enum DSBPLAY_FLAG : int; +enum DSBSTATUS_FLAG : int; +enum DSBSTOPEX_FLAG : int; +// DSound Stream flag/enum +enum DSSCAPS_FLAG : int; +enum DSSFLUSHEX_FLAG : int; +enum DSSPAUSE_FLAG : int; +enum DSSSTATUS_FLAG : int; +// DSound XMedia flag/enum +enum XMP_STATUS : int; +enum XMO_STREAMF : int; + +// DSound flag/enum +FLAGS2STR_HEADER(DSSPEAKER_FLAG) +// DSound generic flag/enum +ENUM2STR_HEADER(DSFILTER_MODE) +// DSound Buffer flag/enum +FLAGS2STR_HEADER(DSBCAPS_FLAG) +FLAGS2STR_HEADER(DSBPAUSE_FLAG) +FLAGS2STR_HEADER(DSBPLAY_FLAG) +FLAGS2STR_HEADER(DSBSTATUS_FLAG) +FLAGS2STR_HEADER(DSBSTOPEX_FLAG) +// DSound Stream flag/enum +FLAGS2STR_HEADER(DSSCAPS_FLAG) +FLAGS2STR_HEADER(DSSFLUSHEX_FLAG) +FLAGS2STR_HEADER(DSSPAUSE_FLAG) +FLAGS2STR_HEADER(DSSSTATUS_FLAG) + +// DSound class usage +LOGRENDER_HEADER(X_DSCAPS) +LOGRENDER_HEADER(X_DSI3DL2LISTENER) +LOGRENDER_HEADER(X_DSMIXBINS) +LOGRENDER_HEADER(X_DSMIXBINVOLUMEPAIR) +LOGRENDER_HEADER(X_DSOUTPUTLEVELS) +// DSound generic class usage +LOGRENDER_HEADER(X_DS3DBUFFER) +LOGRENDER_HEADER(X_DSENVOLOPEDESC) +LOGRENDER_HEADER(X_DSFILTERDESC) +LOGRENDER_HEADER(X_DSI3DL2BUFFER) +LOGRENDER_HEADER(X_DSI3DL2OBSTRUCTION) +LOGRENDER_HEADER(X_DSI3DL2OCCLUSION) +LOGRENDER_HEADER(DSLFODESC) +LOGRENDER_HEADER(XBOXADPCMWAVEFORMAT) +// DSound Buffer class usage +LOGRENDER_HEADER(X_DSBUFFERDESC) +// DSound Stream class usage +LOGRENDER_HEADER(X_DSSTREAMDESC) +// DSound XMedia class usage +LOGRENDER_HEADER(XMEDIAINFO) +LOGRENDER_HEADER(XMEDIAPACKET) +} +#endif diff --git a/src/core/hle/DSOUND/XbDSoundTypes.h b/src/core/hle/DSOUND/XbDSoundTypes.h index 62ea3a782..1947d5d68 100644 --- a/src/core/hle/DSOUND/XbDSoundTypes.h +++ b/src/core/hle/DSOUND/XbDSoundTypes.h @@ -1,375 +1,375 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2017-2019 RadWolfie -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef XBDSOUNDTYPES_H -#define XBDSOUNDTYPES_H - -namespace XTL { - -#include // TODO: FIXME after global namespace XTL issue is resolved. -// TODO: Port PC dsound.h to xbox edition here base on previous research. -// TODO: Also need to use fixed size to able support cross-platform without extra work. -// Such as uint32_t, uint16_t, etc. - -#define WAVE_FORMAT_XBOX_ADPCM 0x0069 +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2017-2019 RadWolfie +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef XBDSOUNDTYPES_H +#define XBDSOUNDTYPES_H + +namespace XTL { + +#include // TODO: FIXME after global namespace XTL issue is resolved. +// TODO: Port PC dsound.h to xbox edition here base on previous research. +// TODO: Also need to use fixed size to able support cross-platform without extra work. +// Such as uint32_t, uint16_t, etc. + +#define WAVE_FORMAT_XBOX_ADPCM 0x0069 // Xbox SGE Memory layout #define XTL_DS_SGE_COUNT_MAX 2047 #define XTL_DS_SGE_PAGE_MAX (4 * ONE_KB) -#define XTL_DS_SGE_SIZE_MAX (XTL_DS_SGE_COUNT_MAX * XTL_DS_SGE_PAGE_MAX) - -// XDSMIXBIN Flags -#define XDSMIXBIN_FRONT_LEFT 0 -#define XDSMIXBIN_FRONT_RIGHT 1 -#define XDSMIXBIN_FRONT_CENTER 2 -#define XDSMIXBIN_LOW_FREQUENCY 3 -#define XDSMIXBIN_BACK_LEFT 4 -#define XDSMIXBIN_BACK_RIGHT 5 -#define XDSMIXBIN_SPEAKERS_MAX 6 // Max count for speakers -// Other flags are used - -// ****************************************************************** -// * X_DSMIXBINVOLUMEPAIR -// ****************************************************************** -typedef struct _XDSMIXBINVOLUMEPAIR { - DWORD dwMixBin; - LONG lVolume; -} X_DSMIXBINVOLUMEPAIR, *X_LPDSMIXBINVOLUMEPAIR; - -// ****************************************************************** -// * X_DSMIXBINS -// ****************************************************************** -// Revision 2 (4039+) -typedef struct _XDSMIXBINS { - DWORD dwCount; - X_LPDSMIXBINVOLUMEPAIR lpMixBinVolumePairs; -} X_DSMIXBINS, *X_LPDSMIXBINS; - -// EmuIDirectSoundBuffer_Play flags -#define X_DSBPLAY_LOOPING 0x00000001 -#define X_DSBPLAY_FROMSTART 0x00000002 -#define X_DSBPLAY_SYNCHPLAYBACK 0x00000004 - -// EmuIDirectSoundBuffer_Pause flags -#define X_DSBPAUSE_RESUME 0x00000000 -#define X_DSBPAUSE_PAUSE 0x00000001 -#define X_DSBPAUSE_SYNCHPLAYBACK 0x00000002 - -// EmuIDirectSoundStream_Pause flags -#define X_DSSPAUSE_RESUME 0x00000000 -#define X_DSSPAUSE_PAUSE 0x00000001 -#define X_DSSPAUSE_SYNCHPLAYBACK 0x00000002 -#define X_DSSPAUSE_PAUSENOACTIVATE 0x00000003 - -// EmuIDirectSoundStream_FlushEx flags -#define X_DSSFLUSHEX_IMMEDIATE 0x00000000 -#define X_DSSFLUSHEX_ASYNC 0x00000001 -#define X_DSSFLUSHEX_ENVELOPE 0x00000002 -#define X_DSSFLUSHEX_ENVELOPE2 0x00000004 - -// EmuIDirectSoundStream_GetStatus flags -#define X_DSSSTATUS_READY 0x00000001 -#define X_DSSSTATUS_PLAYING 0x00010000 -#define X_DSSSTATUS_PAUSED 0x00020000 -#define X_DSSSTATUS_STARVED 0x00040000 -#define X_DSSSTATUS_ENVELOPECOMPLETE 0x00080000 - -// EmuIDirectSoundBuffer_GetStatus flags -#define X_DSBSTATUS_PLAYING 0x00000001 -#define X_DSBSTATUS_PAUSED 0x00000002 -#define X_DSBSTATUS_LOOPING 0x00000004 - -// EmuIDirectSoundBuffer_StopEx flags -#define X_DSBSTOPEX_IMMEDIATE 0x00000000 -#define X_DSBSTOPEX_ENVELOPE 0x00000001 -#define X_DSBSTOPEX_RELEASEWAVEFORM 0x00000002 -#define X_DSBSTOPEX_ALL (X_DSBSTOPEX_ENVELOPE | X_DSBSTOPEX_RELEASEWAVEFORM) - -// Generic frequency range -#define XTL_DSXFREQUENCY_ORIGINAL 0x00000000 -//#define XTL_DSGFREQUENCY_MIN 0x00000??? -//#define XTL_DSGFREQUENCY_MAX 0x000????? - -#define XTL_DSBCAPS_CTRL3D 0x00000010 -#define XTL_DSBCAPS_CTRLFREQUENCY 0x00000020 -#define XTL_DSBCAPS_CTRLVOLUME 0x00000080 -#define XTL_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 -#define XTL_DSBCAPS_MIXIN 0x00002000 -#define XTL_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define XTL_DSBCAPS_LOCDEFER 0x00040000 -#define XTL_DSBCAPS_FXIN 0x00080000 -#define XTL_DSBCAPS_FXIN2 0x00100000 - -// ****************************************************************** -// * X_DSBUFFERDESC -// ****************************************************************** -struct X_DSBUFFERDESC -{ - DWORD dwSize; - DWORD dwFlags; - DWORD dwBufferBytes; - LPWAVEFORMATEX lpwfxFormat; - X_LPDSMIXBINS lpMixBinsOutput; - DWORD dwInputMixBin; -}; - -// ****************************************************************** -// * X_DSENVELOPEDESC -// ****************************************************************** -struct X_DSENVOLOPEDESC -{ - DWORD dwEnvelopGenerator; - DWORD dwMode; - DWORD dwDelay; - DWORD dwAttack; - DWORD dwHold; - DWORD dwDecay; - DWORD dwRelease; - DWORD dwSustain; - LONG lPitchScale; - LONG lFilterCutOff; -}; - -#define XTL_DSSCAPS_CTRL3D 0x00000010 -#define XTL_DSSCAPS_CTRLFREQUENCY 0x00000020 -#define XTL_DSSCAPS_CTRLVOLUME 0x00000080 -#define XTL_DSSCAPS_MUTE3DATMAXDISTANCE 0x00020000 -#define XTL_DSSCAPS_LOCDEFER 0x00040000 -#define XTL_DSSCAPS_NOMERGE 0x20000000 -#define XTL_DSSCAPS_ACCURATENOTIFY 0x40000000 - -typedef VOID(CALLBACK *LPFNXMOCALLBACK)(LPVOID pStreamContext, LPVOID pPacketContext, DWORD dwStatus); - -// ****************************************************************** -// * X_DSSTREAMDESC -// ****************************************************************** -struct X_DSSTREAMDESC -{ - DWORD dwFlags; - DWORD dwMaxAttachedPackets; - LPWAVEFORMATEX lpwfxFormat; - LPFNXMOCALLBACK lpfnCallback; - LPVOID lpvContext; - X_LPDSMIXBINS lpMixBinsOutput; -}; - -// ****************************************************************** -// * REFERENCE_TIME -// ****************************************************************** -typedef LONGLONG REFERENCE_TIME, *PREFERENCE_TIME, *LPREFERENCE_TIME; - -// ****************************************************************** -// * XMEDIAPACKET -// ****************************************************************** -typedef struct _XMEDIAPACKET -{ - LPVOID pvBuffer; - DWORD dwMaxSize; - PDWORD pdwCompletedSize; - PDWORD pdwStatus; - union { - HANDLE hCompletionEvent; - PVOID pContext; - }; - PREFERENCE_TIME prtTimestamp; // Not supported in xbox -} -XMEDIAPACKET, *PXMEDIAPACKET, *LPXMEDIAPACKET; - -#define XMP_STATUS_SUCCESS S_OK -#define XMP_STATUS_PENDING E_PENDING -#define XMP_STATUS_FLUSHED E_ABORT -#define XMP_STATUS_FAILURE E_FAIL -#define XMP_STATUS_RELEASE_CXBXR 0xFFFFFFFF - -// ****************************************************************** -// * XMEDIAINFO -// ****************************************************************** -typedef struct _XMEDIAINFO -{ - DWORD dwFlags; - DWORD dwInputSize; - DWORD dwOutputSize; - DWORD dwMaxLookahead; -} -XMEDIAINFO, *PXEIDIAINFO, *LPXMEDIAINFO; - -// XMEDIAINFO Flags -#define XMO_STREAMF_FIXED_SAMPLE_SIZE 0x00000001 // The object supports only a fixed sample size -#define XMO_STREAMF_FIXED_PACKET_ALIGNMENT 0x00000002 // The object supports only a fixed packet alignment -#define XMO_STREAMF_INPUT_ASYNC 0x00000004 // The object supports receiving input data asynchronously -#define XMO_STREAMF_OUTPUT_ASYNC 0x00000008 // The object supports providing output data asynchronously -#define XMO_STREAMF_IN_PLACE 0x00000010 // The object supports in-place modification of data -#define XMO_STREAMF_MASK 0x0000001F - -// ****************************************************************** -// * X_DSFILTERDESC -// ****************************************************************** -struct X_DSFILTERDESC -{ - DWORD dwMode; - DWORD dwQCoefficient; - DWORD adwCoefficients[4]; -}; - -// X_DSFILTERDESC modes -#define DSFILTER_MODE_BYPASS 0x00000000 // The filter is bypassed -#define DSFILTER_MODE_DLS2 0x00000001 // DLS2 mode -#define DSFILTER_MODE_PARAMEQ 0x00000002 // Parametric equalizer mode -#define DSFILTER_MODE_MULTI 0x00000003 // Multifunction mode - -// ****************************************************************** -// * DSLFODESC -// ****************************************************************** -typedef struct _DSLFODESC -{ - DWORD dwLFO; - DWORD dwDelay; - DWORD dwDelta; - LONG lPitchModulation; - LONG lFilterCutOffRange; - LONG lAmplitudeModulation; -} -DSLFODESC, *LPCDSLFODESC; - -// ****************************************************************** -// * XBOXADPCMWAVEFORMAT -// ****************************************************************** -typedef struct xbox_adpcmwaveformat_tag -{ - WAVEFORMATEX wfx; // WAVEFORMATEX data - WORD wSamplesPerBlock; // Number of samples per encoded block. It must be 64. -} -XBOXADPCMWAVEFORMAT, *PXBOXADPCMWAVEFORMAT, *LPXBOXADPCMWAVEFORMAT; - -typedef const XBOXADPCMWAVEFORMAT *LPCXBOXADPCMWAVEFORMAT; - -// ****************************************************************** -// * X_DSOUTPUTLEVELS -// ****************************************************************** -struct X_DSOUTPUTLEVELS -{ - DWORD dwAnalogLeftTotalPeak; // analog peak - DWORD dwAnalogRightTotalPeak; - DWORD dwAnalogLeftTotalRMS; // analog RMS - DWORD dwAnalogRightTotalRMS; - DWORD dwDigitalFrontLeftPeak; // digital peak levels - DWORD dwDigitalFrontCenterPeak; - DWORD dwDigitalFrontRightPeak; - DWORD dwDigitalBackLeftPeak; - DWORD dwDigitalBackRightPeak; - DWORD dwDigitalLowFrequencyPeak; - DWORD dwDigitalFrontLeftRMS; // digital RMS levels - DWORD dwDigitalFrontCenterRMS; - DWORD dwDigitalFrontRightRMS; - DWORD dwDigitalBackLeftRMS; - DWORD dwDigitalBackRightRMS; - DWORD dwDigitalLowFrequencyRMS; -}; - -// ****************************************************************** -// * X_DSCAPS -// ****************************************************************** -struct X_DSCAPS -{ - DWORD dwFree2DBuffers; - DWORD dwFree3DBuffers; - DWORD dwFreeBufferSGEs; - DWORD dwMemoryAllocated; -}; - -#define X_DSSPEAKER_STEREO 0x00000000 -#define X_DSSPEAKER_MONO 0x00000001 -#define X_DSSPEAKER_SURROUND 0x00000002 -#define X_DSSPEAKER_ENABLE_AC3 0x00010000 -#define X_DSSPEAKER_ENABLE_DTS 0x00020000 - -struct X_DS3DBUFFER { - DWORD dwSize; - D3DVECTOR vPosition; - D3DVECTOR vVelocity; - DWORD dwInsideConeAngle; - DWORD dwOutsideConeAngle; - D3DVECTOR vConeOrientation; - LONG lConeOutsideVolume; - FLOAT flMinDistance; - FLOAT flMaxDistance; - DWORD dwMode; - FLOAT flDistanceFactor; - FLOAT flRolloffFactor; - FLOAT flDopplerFactor; -}; - -struct X_DSI3DL2LISTENER { - LONG lRoom; - LONG lRoomHF; - FLOAT flRoomRolloffFactor; - FLOAT flDecayTime; - FLOAT flDecayHFRatio; - LONG lReflections; - FLOAT flReflectionsDelay; - LONG lReverb; - FLOAT flReverbDelay; - FLOAT flDiffusion; - FLOAT flDensity; - FLOAT flHFReference; -}; - -struct X_DSI3DL2OBSTRUCTION { - LONG lHFLevel; - FLOAT flLFRatio; -}; - -struct X_DSI3DL2OCCLUSION { - LONG lHFLevel; - FLOAT flLFRatio; -}; - -struct X_DSI3DL2BUFFER { - LONG lDirect; - LONG lDirectHF; - LONG lRoom; - LONG lRoomHF; - FLOAT flRoomRolloffFactor; - X_DSI3DL2OBSTRUCTION Obstruction; - X_DSI3DL2OCCLUSION Occlusion; -}; - -typedef struct IDirectSoundStream IDirectSoundStream; -typedef IDirectSoundStream *LPDIRECTSOUNDSTREAM; - -struct X_DSVOICEPROPS { - DWORD dwMixBinCount; - X_DSMIXBINVOLUMEPAIR MixBinVolumePairs[8]; - LONG lPitch; - LONG l3DDistanceVolume; - LONG l3DConeVolume; - LONG l3DDopplerPitch; - LONG lI3DL2DirectVolume; - LONG lI3DL2RoomVolume; -}; - -} // end of namespace XTL - -#endif +#define XTL_DS_SGE_SIZE_MAX (XTL_DS_SGE_COUNT_MAX * XTL_DS_SGE_PAGE_MAX) + +// XDSMIXBIN Flags +#define XDSMIXBIN_FRONT_LEFT 0 +#define XDSMIXBIN_FRONT_RIGHT 1 +#define XDSMIXBIN_FRONT_CENTER 2 +#define XDSMIXBIN_LOW_FREQUENCY 3 +#define XDSMIXBIN_BACK_LEFT 4 +#define XDSMIXBIN_BACK_RIGHT 5 +#define XDSMIXBIN_SPEAKERS_MAX 6 // Max count for speakers +// Other flags are used + +// ****************************************************************** +// * X_DSMIXBINVOLUMEPAIR +// ****************************************************************** +typedef struct _XDSMIXBINVOLUMEPAIR { + DWORD dwMixBin; + LONG lVolume; +} X_DSMIXBINVOLUMEPAIR, *X_LPDSMIXBINVOLUMEPAIR; + +// ****************************************************************** +// * X_DSMIXBINS +// ****************************************************************** +// Revision 2 (4039+) +typedef struct _XDSMIXBINS { + DWORD dwCount; + X_LPDSMIXBINVOLUMEPAIR lpMixBinVolumePairs; +} X_DSMIXBINS, *X_LPDSMIXBINS; + +// EmuIDirectSoundBuffer_Play flags +#define X_DSBPLAY_LOOPING 0x00000001 +#define X_DSBPLAY_FROMSTART 0x00000002 +#define X_DSBPLAY_SYNCHPLAYBACK 0x00000004 + +// EmuIDirectSoundBuffer_Pause flags +#define X_DSBPAUSE_RESUME 0x00000000 +#define X_DSBPAUSE_PAUSE 0x00000001 +#define X_DSBPAUSE_SYNCHPLAYBACK 0x00000002 + +// EmuIDirectSoundStream_Pause flags +#define X_DSSPAUSE_RESUME 0x00000000 +#define X_DSSPAUSE_PAUSE 0x00000001 +#define X_DSSPAUSE_SYNCHPLAYBACK 0x00000002 +#define X_DSSPAUSE_PAUSENOACTIVATE 0x00000003 + +// EmuIDirectSoundStream_FlushEx flags +#define X_DSSFLUSHEX_IMMEDIATE 0x00000000 +#define X_DSSFLUSHEX_ASYNC 0x00000001 +#define X_DSSFLUSHEX_ENVELOPE 0x00000002 +#define X_DSSFLUSHEX_ENVELOPE2 0x00000004 + +// EmuIDirectSoundStream_GetStatus flags +#define X_DSSSTATUS_READY 0x00000001 +#define X_DSSSTATUS_PLAYING 0x00010000 +#define X_DSSSTATUS_PAUSED 0x00020000 +#define X_DSSSTATUS_STARVED 0x00040000 +#define X_DSSSTATUS_ENVELOPECOMPLETE 0x00080000 + +// EmuIDirectSoundBuffer_GetStatus flags +#define X_DSBSTATUS_PLAYING 0x00000001 +#define X_DSBSTATUS_PAUSED 0x00000002 +#define X_DSBSTATUS_LOOPING 0x00000004 + +// EmuIDirectSoundBuffer_StopEx flags +#define X_DSBSTOPEX_IMMEDIATE 0x00000000 +#define X_DSBSTOPEX_ENVELOPE 0x00000001 +#define X_DSBSTOPEX_RELEASEWAVEFORM 0x00000002 +#define X_DSBSTOPEX_ALL (X_DSBSTOPEX_ENVELOPE | X_DSBSTOPEX_RELEASEWAVEFORM) + +// Generic frequency range +#define XTL_DSXFREQUENCY_ORIGINAL 0x00000000 +//#define XTL_DSGFREQUENCY_MIN 0x00000??? +//#define XTL_DSGFREQUENCY_MAX 0x000????? + +#define XTL_DSBCAPS_CTRL3D 0x00000010 +#define XTL_DSBCAPS_CTRLFREQUENCY 0x00000020 +#define XTL_DSBCAPS_CTRLVOLUME 0x00000080 +#define XTL_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define XTL_DSBCAPS_MIXIN 0x00002000 +#define XTL_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 +#define XTL_DSBCAPS_LOCDEFER 0x00040000 +#define XTL_DSBCAPS_FXIN 0x00080000 +#define XTL_DSBCAPS_FXIN2 0x00100000 + +// ****************************************************************** +// * X_DSBUFFERDESC +// ****************************************************************** +struct X_DSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + LPWAVEFORMATEX lpwfxFormat; + X_LPDSMIXBINS lpMixBinsOutput; + DWORD dwInputMixBin; +}; + +// ****************************************************************** +// * X_DSENVELOPEDESC +// ****************************************************************** +struct X_DSENVOLOPEDESC +{ + DWORD dwEnvelopGenerator; + DWORD dwMode; + DWORD dwDelay; + DWORD dwAttack; + DWORD dwHold; + DWORD dwDecay; + DWORD dwRelease; + DWORD dwSustain; + LONG lPitchScale; + LONG lFilterCutOff; +}; + +#define XTL_DSSCAPS_CTRL3D 0x00000010 +#define XTL_DSSCAPS_CTRLFREQUENCY 0x00000020 +#define XTL_DSSCAPS_CTRLVOLUME 0x00000080 +#define XTL_DSSCAPS_MUTE3DATMAXDISTANCE 0x00020000 +#define XTL_DSSCAPS_LOCDEFER 0x00040000 +#define XTL_DSSCAPS_NOMERGE 0x20000000 +#define XTL_DSSCAPS_ACCURATENOTIFY 0x40000000 + +typedef VOID(CALLBACK *LPFNXMOCALLBACK)(LPVOID pStreamContext, LPVOID pPacketContext, DWORD dwStatus); + +// ****************************************************************** +// * X_DSSTREAMDESC +// ****************************************************************** +struct X_DSSTREAMDESC +{ + DWORD dwFlags; + DWORD dwMaxAttachedPackets; + LPWAVEFORMATEX lpwfxFormat; + LPFNXMOCALLBACK lpfnCallback; + LPVOID lpvContext; + X_LPDSMIXBINS lpMixBinsOutput; +}; + +// ****************************************************************** +// * REFERENCE_TIME +// ****************************************************************** +typedef LONGLONG REFERENCE_TIME, *PREFERENCE_TIME, *LPREFERENCE_TIME; + +// ****************************************************************** +// * XMEDIAPACKET +// ****************************************************************** +typedef struct _XMEDIAPACKET +{ + LPVOID pvBuffer; + DWORD dwMaxSize; + PDWORD pdwCompletedSize; + PDWORD pdwStatus; + union { + HANDLE hCompletionEvent; + PVOID pContext; + }; + PREFERENCE_TIME prtTimestamp; // Not supported in xbox +} +XMEDIAPACKET, *PXMEDIAPACKET, *LPXMEDIAPACKET; + +#define XMP_STATUS_SUCCESS S_OK +#define XMP_STATUS_PENDING E_PENDING +#define XMP_STATUS_FLUSHED E_ABORT +#define XMP_STATUS_FAILURE E_FAIL +#define XMP_STATUS_RELEASE_CXBXR 0xFFFFFFFF + +// ****************************************************************** +// * XMEDIAINFO +// ****************************************************************** +typedef struct _XMEDIAINFO +{ + DWORD dwFlags; + DWORD dwInputSize; + DWORD dwOutputSize; + DWORD dwMaxLookahead; +} +XMEDIAINFO, *PXEIDIAINFO, *LPXMEDIAINFO; + +// XMEDIAINFO Flags +#define XMO_STREAMF_FIXED_SAMPLE_SIZE 0x00000001 // The object supports only a fixed sample size +#define XMO_STREAMF_FIXED_PACKET_ALIGNMENT 0x00000002 // The object supports only a fixed packet alignment +#define XMO_STREAMF_INPUT_ASYNC 0x00000004 // The object supports receiving input data asynchronously +#define XMO_STREAMF_OUTPUT_ASYNC 0x00000008 // The object supports providing output data asynchronously +#define XMO_STREAMF_IN_PLACE 0x00000010 // The object supports in-place modification of data +#define XMO_STREAMF_MASK 0x0000001F + +// ****************************************************************** +// * X_DSFILTERDESC +// ****************************************************************** +struct X_DSFILTERDESC +{ + DWORD dwMode; + DWORD dwQCoefficient; + DWORD adwCoefficients[4]; +}; + +// X_DSFILTERDESC modes +#define DSFILTER_MODE_BYPASS 0x00000000 // The filter is bypassed +#define DSFILTER_MODE_DLS2 0x00000001 // DLS2 mode +#define DSFILTER_MODE_PARAMEQ 0x00000002 // Parametric equalizer mode +#define DSFILTER_MODE_MULTI 0x00000003 // Multifunction mode + +// ****************************************************************** +// * DSLFODESC +// ****************************************************************** +typedef struct _DSLFODESC +{ + DWORD dwLFO; + DWORD dwDelay; + DWORD dwDelta; + LONG lPitchModulation; + LONG lFilterCutOffRange; + LONG lAmplitudeModulation; +} +DSLFODESC, *LPCDSLFODESC; + +// ****************************************************************** +// * XBOXADPCMWAVEFORMAT +// ****************************************************************** +typedef struct xbox_adpcmwaveformat_tag +{ + WAVEFORMATEX wfx; // WAVEFORMATEX data + WORD wSamplesPerBlock; // Number of samples per encoded block. It must be 64. +} +XBOXADPCMWAVEFORMAT, *PXBOXADPCMWAVEFORMAT, *LPXBOXADPCMWAVEFORMAT; + +typedef const XBOXADPCMWAVEFORMAT *LPCXBOXADPCMWAVEFORMAT; + +// ****************************************************************** +// * X_DSOUTPUTLEVELS +// ****************************************************************** +struct X_DSOUTPUTLEVELS +{ + DWORD dwAnalogLeftTotalPeak; // analog peak + DWORD dwAnalogRightTotalPeak; + DWORD dwAnalogLeftTotalRMS; // analog RMS + DWORD dwAnalogRightTotalRMS; + DWORD dwDigitalFrontLeftPeak; // digital peak levels + DWORD dwDigitalFrontCenterPeak; + DWORD dwDigitalFrontRightPeak; + DWORD dwDigitalBackLeftPeak; + DWORD dwDigitalBackRightPeak; + DWORD dwDigitalLowFrequencyPeak; + DWORD dwDigitalFrontLeftRMS; // digital RMS levels + DWORD dwDigitalFrontCenterRMS; + DWORD dwDigitalFrontRightRMS; + DWORD dwDigitalBackLeftRMS; + DWORD dwDigitalBackRightRMS; + DWORD dwDigitalLowFrequencyRMS; +}; + +// ****************************************************************** +// * X_DSCAPS +// ****************************************************************** +struct X_DSCAPS +{ + DWORD dwFree2DBuffers; + DWORD dwFree3DBuffers; + DWORD dwFreeBufferSGEs; + DWORD dwMemoryAllocated; +}; + +#define X_DSSPEAKER_STEREO 0x00000000 +#define X_DSSPEAKER_MONO 0x00000001 +#define X_DSSPEAKER_SURROUND 0x00000002 +#define X_DSSPEAKER_ENABLE_AC3 0x00010000 +#define X_DSSPEAKER_ENABLE_DTS 0x00020000 + +struct X_DS3DBUFFER { + DWORD dwSize; + D3DVECTOR vPosition; + D3DVECTOR vVelocity; + DWORD dwInsideConeAngle; + DWORD dwOutsideConeAngle; + D3DVECTOR vConeOrientation; + LONG lConeOutsideVolume; + FLOAT flMinDistance; + FLOAT flMaxDistance; + DWORD dwMode; + FLOAT flDistanceFactor; + FLOAT flRolloffFactor; + FLOAT flDopplerFactor; +}; + +struct X_DSI3DL2LISTENER { + LONG lRoom; + LONG lRoomHF; + FLOAT flRoomRolloffFactor; + FLOAT flDecayTime; + FLOAT flDecayHFRatio; + LONG lReflections; + FLOAT flReflectionsDelay; + LONG lReverb; + FLOAT flReverbDelay; + FLOAT flDiffusion; + FLOAT flDensity; + FLOAT flHFReference; +}; + +struct X_DSI3DL2OBSTRUCTION { + LONG lHFLevel; + FLOAT flLFRatio; +}; + +struct X_DSI3DL2OCCLUSION { + LONG lHFLevel; + FLOAT flLFRatio; +}; + +struct X_DSI3DL2BUFFER { + LONG lDirect; + LONG lDirectHF; + LONG lRoom; + LONG lRoomHF; + FLOAT flRoomRolloffFactor; + X_DSI3DL2OBSTRUCTION Obstruction; + X_DSI3DL2OCCLUSION Occlusion; +}; + +typedef struct IDirectSoundStream IDirectSoundStream; +typedef IDirectSoundStream *LPDIRECTSOUNDSTREAM; + +struct X_DSVOICEPROPS { + DWORD dwMixBinCount; + X_DSMIXBINVOLUMEPAIR MixBinVolumePairs[8]; + LONG lPitch; + LONG l3DDistanceVolume; + LONG l3DConeVolume; + LONG l3DDopplerPitch; + LONG lI3DL2DirectVolume; + LONG lI3DL2RoomVolume; +}; + +} // end of namespace XTL + +#endif diff --git a/src/core/hle/Intercept.cpp b/src/core/hle/Intercept.cpp index 05386e524..0889c6cee 100644 --- a/src/core/hle/Intercept.cpp +++ b/src/core/hle/Intercept.cpp @@ -1,758 +1,758 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2016-2018 Luke Usher -// * (c) 2016-2018 Patrick van Logchem -// * (c) 2017-2018 RadWolfie -// * (c) 2017-2018 jarupxx -// * (c) 2018 x1nixmzeng -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::HLE - -#include -#include // For std::setfill and std::setw - -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\support\Emu.h" -#include "EmuShared.h" -#include "common\CxbxDebugger.h" -#include "Logging.h" -#pragma comment(lib, "XbSymbolDatabase.lib") -#include "..\..\import\XbSymbolDatabase\XbSymbolDatabase.h" -#include "Intercept.hpp" -#include "Patches.hpp" -#include "common\util\hasher.h" - -#include -#include -#include -#include -#include -#include - -static const char* section_info = "Info"; -static struct { - const char* SymbolDatabaseVersionHash = "SymbolDatabaseVersionHash"; -} sect_info_keys; - -static const char* section_certificate = "Certificate"; -static struct { - const char* Name = "Name"; - const char* TitleID = "TitleID"; - const char* TitleIDHex = "TitleIDHex"; - const char* Region = "Region"; -} sect_certificate_keys; - -static const char* section_libs = "Libs"; -static struct { - const char* BuildVersion = "BuildVersion"; -} sect_libs_keys; - -static const char* section_symbols = "Symbols"; - -std::map g_SymbolAddresses; -bool g_SymbolCacheUsed = false; - -bool bLLE_APU = false; // Set this to true for experimental APU (sound) LLE -bool bLLE_GPU = false; // Set this to true for experimental GPU (graphics) LLE -bool bLLE_USB = false; // Set this to true for experimental USB (input) LLE -bool bLLE_JIT = false; // Set this to true for experimental JIT - -void* GetXboxFunctionPointer(std::string functionName) -{ - void* ptr = GetPatchedFunctionTrampoline(functionName); - if (ptr != nullptr) { - return ptr; - } - - // If we got here, the function wasn't patched, so we can just look it up the symbol cache - // and return the correct offset - auto symbol = g_SymbolAddresses.find(functionName); - if (symbol != g_SymbolAddresses.end()) { - return (void*)symbol->second; - } - - // Finally, if none of the above were matched, return nullptr - return nullptr; -} - -// NOTE: GetDetectedSymbolName do not get to be in XbSymbolDatabase, get symbol string in Cxbx project only. -std::string GetDetectedSymbolName(const xbaddr address, int * const symbolOffset) -{ - std::string result = ""; - int closestMatch = MAXINT; - - for (auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) { - xbaddr symbolAddr = it->second; - if (symbolAddr == xbnull) - continue; - - if (symbolAddr <= address) - { - int distance = address - symbolAddr; - if (closestMatch > distance) - { - closestMatch = distance; - result = it->first; - } - } - } - - if (closestMatch < MAXINT) - { - *symbolOffset = closestMatch; - return result; - } - - *symbolOffset = 0; - return "unknown"; -} - -// NOTE: VerifySymbolAddressAgainstXRef do not get to be in XbSymbolDatabase, perform verification in Cxbx project only. -/* -bool VerifySymbolAddressAgainstXRef(char *SymbolName, xbaddr Address, int XRef) -{ - // Temporary verification - is XREF_D3DTSS_TEXCOORDINDEX derived correctly? - // TODO : Remove this when XREF_D3DTSS_TEXCOORDINDEX derivation is deemed stable - xbaddr XRefAddr = XRefDataBase[XRef]; - if (XRefAddr == Address) - return true; - - if (XRefAddr == XREF_ADDR_DERIVE) { - printf("HLE: XRef #%d derived 0x%.08X -> %s\n", XRef, Address, SymbolName); - XRefDataBase[XRef] = Address; - return true; - } - - PopupCustom(LOG_LEVEL::WARNING, CxbxMsgDlgIcon_Warn, - "Verification of %s failed : XREF was 0x%.8X while lookup gave 0x%.8X", SymbolName, XRefAddr, Address); - // test case : Kabuki Warriors (for XREF_D3DTSS_TEXCOORDINDEX) - return false; -}*/ - -// x1nixmzeng: Hack to notify CxbxDebugger of the SymbolCache file, which is currently a hashed XBE header AND stripped title (see EmuHLEIntercept) -class CxbxDebuggerScopedMessage -{ - std::string& message; - - CxbxDebuggerScopedMessage() = delete; - CxbxDebuggerScopedMessage(const CxbxDebuggerScopedMessage&) = delete; -public: - - CxbxDebuggerScopedMessage(std::string& message_string) - : message(message_string) - { } - - ~CxbxDebuggerScopedMessage() - { - if (CxbxDebugger::CanReport()) - { - CxbxDebugger::ReportHLECacheFile(message.c_str()); - } - } -}; - -void CDECL EmuOutputMessage(xb_output_message mFlag, - const char* message) -{ - switch (mFlag) { - case XB_OUTPUT_MESSAGE_INFO: { - printf("%s\n", message); - break; - } - case XB_OUTPUT_MESSAGE_WARN: { - EmuLog(LOG_LEVEL::WARNING, "%s", message); - break; - } - case XB_OUTPUT_MESSAGE_ERROR: { - CxbxKrnlCleanup("%s", message); - break; - } - case XB_OUTPUT_MESSAGE_DEBUG: - default: { -#ifdef _DEBUG_TRACE - printf("%s\n", message); -#endif - - break; - } - } -} - -void CDECL EmuRegisterSymbol(const char* library_str, - uint32_t library_flag, - const char* symbol_str, - uint32_t func_addr, - uint32_t revision) -{ - // Ignore registered symbol in current database. - uint32_t hasSymbol = g_SymbolAddresses[symbol_str]; - if (hasSymbol != 0) - return; - - // Output some details - std::stringstream output; - output << "Symbol: 0x" << std::setfill('0') << std::setw(8) << std::hex << func_addr - << " -> " << symbol_str << " " << std::dec << revision; - -#if 0 // TODO: XbSymbolDatabase - Need to create a structure for patch and stuff. - bool IsXRef = OovpaTable->Oovpa->XRefSaveIndex != XRefNoSaveIndex; - if (IsXRef) { - output << "\t(XREF)"; - - // do we need to save the found address? - OOVPA* Oovpa = OovpaTable->Oovpa; - if (Oovpa->XRefSaveIndex != XRefNoSaveIndex) { - // is the XRef not saved yet? - switch (XRefDataBase[Oovpa->XRefSaveIndex]) { - case XREF_ADDR_NOT_FOUND: - { - EmuLog(LOG_LEVEL::WARNING, "Found OOVPA after first finding nothing?"); - // fallthrough to XREF_ADDR_UNDETERMINED - } - case XREF_ADDR_UNDETERMINED: - { - // save and count the found address - UnResolvedXRefs--; - XRefDataBase[Oovpa->XRefSaveIndex] = pFunc; - break; - } - case XREF_ADDR_DERIVE: - { - EmuLog(LOG_LEVEL::WARNING, "Cannot derive a save index!"); - break; - } - default: - { - if (XRefDataBase[OovpaTable->Oovpa->XRefSaveIndex] != pFunc) { - EmuLog(LOG_LEVEL::WARNING, "Found OOVPA on other address than in XRefDataBase!"); - EmuLog(LOG_LEVEL::WARNING, "%s: %4d - pFunc: %08X, stored: %08X", OovpaTable->szFuncName, Oovpa->XRefSaveIndex, pFunc, XRefDataBase[Oovpa->XRefSaveIndex]); - } - break; - } - } - } - } - - // Retrieve the associated patch, if any is available - void* addr = GetEmuPatchAddr(std::string(OovpaTable->szFuncName)); - - if (addr != nullptr) { - EmuInstallPatch(OovpaTable->szFuncName, pFunc, addr); - output << "\t*PATCHED*"; - } else { - const char* checkDisableStr = nullptr; - size_t getFuncStrLength = strlen(OovpaTable->szFuncName); - - if (getFuncStrLength > 10) { - checkDisableStr = &OovpaTable->szFuncName[getFuncStrLength - 10]; - } - - if (checkDisableStr != nullptr && strcmp(checkDisableStr, "_UNPATCHED") == 0) { - output << "\t*UNPATCHED*"; - - // Mention there's no patch available, if it was to be applied - } else if (!IsXRef) { - output << "\t*NO PATCH AVAILABLE!*"; - } - } -#endif - - output << "\n"; - - g_SymbolAddresses[symbol_str] = func_addr; - printf(output.str().c_str()); -} - -// Update shared structure with GUI process -void EmuUpdateLLEStatus(uint32_t XbLibScan) -{ - unsigned int FlagsLLE; - g_EmuShared->GetFlagsLLE(&FlagsLLE); - - if ((FlagsLLE & LLE_GPU) == false - && !((XbLibScan & XbSymbolLib_D3D8) > 0 - || (XbLibScan & XbSymbolLib_D3D8LTCG) > 0)) { - bLLE_GPU = true; - FlagsLLE ^= LLE_GPU; - EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE GPU."); - } - - if ((FlagsLLE & LLE_APU) == false - && (XbLibScan & XbSymbolLib_DSOUND) == 0) { - bLLE_APU = true; - FlagsLLE ^= LLE_APU; - EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE APU."); - } -#if 0 // Reenable this when LLE USB actually works - if ((FlagsLLE & LLE_USB) == false - && (XbLibScan & XbSymbolLib_XAPILIB) == 0) { - bLLE_USB = true; - FlagsLLE ^= LLE_USB; - EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE USB."); - } -#endif - ipc_send_gui_update(IPC_UPDATE_GUI::LLE_FLAGS, FlagsLLE); - //return FlagsLLE; -} - -// NOTE: EmuHLEIntercept do not get to be in XbSymbolDatabase, do the intecept in Cxbx project only. -void EmuHLEIntercept(Xbe::Header *pXbeHeader) -{ - Xbe::LibraryVersion *pLibraryVersion = (Xbe::LibraryVersion*)pXbeHeader->dwLibraryVersionsAddr; - - uint16_t xdkVersion = 0; - uint32_t XbLibScan = 0; - - // NOTE: We need to check if title has library header to optimize verification process. - if (pLibraryVersion != nullptr) { - uint32_t dwLibraryVersions = pXbeHeader->dwLibraryVersions; - const char* SectionName = nullptr; - Xbe::SectionHeader* pSectionHeaders = (Xbe::SectionHeader*)pXbeHeader->dwSectionHeadersAddr; - uint32_t XbLibFlag; - - // Get the highest revision build and prefix library to scan. - for (uint32_t v = 0; v < dwLibraryVersions; v++) { - uint16_t BuildVersion, QFEVersion; - BuildVersion = pLibraryVersion[v].wBuildVersion; - QFEVersion = pLibraryVersion[v].wFlags.QFEVersion; - - if (xdkVersion < BuildVersion) { - xdkVersion = BuildVersion; - } - XbLibFlag = XbSymbolDatabase_LibraryToFlag(std::string(pLibraryVersion[v].szName, pLibraryVersion[v].szName + 8).c_str()); - XbLibScan |= XbLibFlag; - - // Keep certain library versions for plugin usage. - if ((XbLibFlag & (XbSymbolLib_D3D8 | XbSymbolLib_D3D8LTCG)) > 0) { - if (g_LibVersion_D3D8 < BuildVersion) { - g_LibVersion_D3D8 = BuildVersion; - } - } - else if ((XbLibFlag & XbSymbolLib_DSOUND) > 0) { - g_LibVersion_DSOUND = BuildVersion; - } - } - - // Since XDK 4039 title does not have library version for DSOUND, let's check section header if it exists or not. - for (unsigned int v = 0; v < pXbeHeader->dwSections; v++) { - SectionName = (const char*)pSectionHeaders[v].dwSectionNameAddr; - if (strncmp(SectionName, Lib_DSOUND, 8) == 0) { - XbLibScan |= XbSymbolLib_DSOUND; - - // If DSOUND version is not set, we need to force set it. - if (g_LibVersion_DSOUND == 0) { - g_LibVersion_DSOUND = xdkVersion; - } - break; - } - } - } - - EmuUpdateLLEStatus(XbLibScan); - - std::cout << "\n" - "*******************************************************************************\n" - "* Cxbx-Reloaded High Level Emulation database\n" - "*******************************************************************************\n" - << std::endl; - - // Make sure the Symbol Cache directory exists - std::string cachePath = std::string(szFolder_CxbxReloadedData) + "\\SymbolCache\\"; - if (!std::filesystem::exists(cachePath) && !std::filesystem::create_directory(cachePath)) { - CxbxKrnlCleanup("Couldn't create Cxbx-Reloaded SymbolCache folder!"); - } - - // Hash the loaded XBE's header, use it as a filename - uint64_t uiHash = ComputeHash((void*)&CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header)); - std::stringstream sstream; - char tAsciiTitle[40] = "Unknown"; - std::setlocale(LC_ALL, "English"); - std::wcstombs(tAsciiTitle, g_pCertificate->wszTitleName, sizeof(tAsciiTitle)); - std::string szTitleName(tAsciiTitle); - CxbxKrnl_Xbe->PurgeBadChar(szTitleName); - sstream << cachePath << szTitleName << "-" << std::hex << uiHash << ".ini"; - std::string filename = sstream.str(); - - // This will fire when we exit this function scope; either after detecting a previous cache file, or when one is created - CxbxDebuggerScopedMessage symbolCacheFilename(filename); - - CSimpleIniA symbolCacheData; - - if (std::filesystem::exists(filename.c_str())) { - std::printf("Found Symbol Cache File: %08llX.ini\n", uiHash); - - symbolCacheData.LoadFile(filename.c_str()); - - xdkVersion = (uint16_t)symbolCacheData.GetLongValue(section_libs, sect_libs_keys.BuildVersion, /*Default=*/0); - - // Verify the version of the cache file against the Symbol Database version hash - const uint32_t SymbolDatabaseVersionHash = symbolCacheData.GetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, /*Default=*/0); - - if (SymbolDatabaseVersionHash == XbSymbolDatabase_LibraryVersion()) { - g_SymbolCacheUsed = true; - CSimpleIniA::TNamesDepend symbol_names; - - if (g_SymbolCacheUsed) { - std::printf("Using Symbol Cache\n"); - - // Parse the .INI file into the map of symbol addresses - symbolCacheData.GetAllKeys(section_symbols, symbol_names); - for (auto it = symbol_names.begin(); it != symbol_names.end(); ++it) { - g_SymbolAddresses[it->pItem] = symbolCacheData.GetLongValue(section_symbols, it->pItem, /*Default=*/0); - } - - // Iterate through the map of symbol addresses, calling GetEmuPatchAddr on all functions. - for (auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) { - std::string functionName = it->first; - xbaddr location = it->second; - - std::stringstream output; - output << "SymbolCache: 0x" << std::setfill('0') << std::setw(8) << std::hex << location - << " -> " << functionName << "\n"; - std::printf(output.str().c_str()); - } - - // Fix up Render state and Texture States - if (g_SymbolAddresses.find("D3DDeferredRenderState") == g_SymbolAddresses.end() - || g_SymbolAddresses["D3DDeferredRenderState"] == 0) { - EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState was not found!"); - } - - if (g_SymbolAddresses.find("D3DDeferredTextureState") == g_SymbolAddresses.end() - || g_SymbolAddresses["D3DDeferredTextureState"] == 0) { - EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredTextureState was not found!"); - } - - if (g_SymbolAddresses.find("D3DDEVICE") == g_SymbolAddresses.end() - || g_SymbolAddresses["D3DDEVICE"] == 0) { - EmuLog(LOG_LEVEL::WARNING, "D3DDEVICE was not found!"); - } - } - } - - // If g_SymbolAddresses didn't get filled, then symbol cache is invalid - if (g_SymbolAddresses.empty()) { - std::printf("Symbol Cache file is outdated and will be regenerated\n"); - g_SymbolCacheUsed = false; - } - } - - // If the Symbol Cache was used, go straight to patching, no need to re-scan - if (g_SymbolCacheUsed) { - EmuInstallPatches(); - return; - } - - // - // initialize Microsoft XDK emulation - // - if(pLibraryVersion != nullptr) { - - std::printf("Symbol: Detected Microsoft XDK application...\n"); - -#if 0 // NOTE: This code is currently disabled due to not optimized and require more work to do. - - XbSymbolRegisterLibrary(XbLibScan); - - while (true) { - - size_t SymbolSize = g_SymbolAddresses.size(); - - Xbe::SectionHeader* pSectionHeaders = reinterpret_cast(pXbeHeader->dwSectionHeadersAddr); - Xbe::SectionHeader* pSectionScan = nullptr; - - for (uint32_t v = 0; v < pXbeHeader->dwSections; v++) { - - pSectionScan = pSectionHeaders + v; - - XbSymbolScanSection((uint32_t)pXbeHeader, 64 * ONE_MB, (const char*)pSectionScan->dwSectionNameAddr, pSectionScan->dwVirtualAddr, pSectionScan->dwSizeofRaw, xdkVersion, EmuRegisterSymbol); - } - - // If symbols are not adding to array, break the loop. - if (SymbolSize == g_SymbolAddresses.size()) { - break; - } - } -#endif - - XbSymbolDatabase_SetOutputMessage(EmuOutputMessage); - - XbSymbolScan(pXbeHeader, EmuRegisterSymbol, false); - } - - std::printf("\n"); - - // Perform a reset just in case a cached file data still exist. - symbolCacheData.Reset(); - - // Store Symbol Database version - symbolCacheData.SetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, XbSymbolDatabase_LibraryVersion(), nullptr, /*UseHex =*/false); - - // Store Certificate Details - symbolCacheData.SetValue(section_certificate, sect_certificate_keys.Name, tAsciiTitle); - symbolCacheData.SetValue(section_certificate, sect_certificate_keys.TitleID, FormatTitleId(g_pCertificate->dwTitleId).c_str()); - symbolCacheData.SetLongValue(section_certificate, sect_certificate_keys.TitleIDHex, g_pCertificate->dwTitleId, nullptr, /*UseHex =*/true); - symbolCacheData.SetLongValue(section_certificate, sect_certificate_keys.Region, g_pCertificate->dwGameRegion, nullptr, /*UseHex =*/true); - - // Store Library Details - for (unsigned int i = 0; i < pXbeHeader->dwLibraryVersions; i++) { - std::string LibraryName(pLibraryVersion[i].szName, pLibraryVersion[i].szName + 8); - symbolCacheData.SetLongValue(section_libs, LibraryName.c_str(), pLibraryVersion[i].wBuildVersion, nullptr, /*UseHex =*/false); - } - - symbolCacheData.SetLongValue(section_libs, sect_libs_keys.BuildVersion, xdkVersion, nullptr, /*UseHex =*/false); - - // Store detected symbol addresses - for(auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) { - symbolCacheData.SetLongValue(section_symbols, it->first.c_str(), it->second, nullptr, /*UseHex =*/true); - } - - // Save data to unique symbol cache file - symbolCacheData.SaveFile(filename.c_str()); - - EmuInstallPatches(); -} - - -#if 0 // TODO: Need to move this into XbSymbolDatabase for depth verification usage. -#ifdef _DEBUG_TRACE - -struct HLEVerifyContext { - const HLEData *main_data; - OOVPA *oovpa, *against; - const HLEData *against_data; - uint32_t main_index, against_index; -}; - -std::string HLEErrorString(const HLEData *data, uint16_t buildVersion, uint32_t index) -{ - std::string result = - "OOVPATable " + (std::string)(data->LibSec.library) + "_" + std::to_string(buildVersion) - + "[" + std::to_string(index) + "] " - + (std::string)(data->OovpaTable[index].szFuncName); - - return result; -} - -void HLEError(HLEVerifyContext *context, uint16_t buildVersion, char *format, ...) -{ - std::string output = "HLE Error "; - if (context->main_data != nullptr) { - output += "in " + HLEErrorString(context->main_data, buildVersion, context->main_index); - } - - if (context->against != nullptr && context->against_data != nullptr) { - output += ", comparing against " + HLEErrorString(context->against_data, buildVersion, context->against_index); - } - - // format specific error message - char buffer[200]; - va_list args; - va_start(args, format); - vsprintf(buffer, format, args); - va_end(args); - - output += " : " + (std::string)buffer + (std::string)"\n"; - printf(output.c_str()); -} - -void VerifyHLEDataBaseAgainst(HLEVerifyContext *context); // forward - -void VerifyHLEOOVPA(HLEVerifyContext *context, uint16_t buildVersion, OOVPA *oovpa) -{ - if (context->against == nullptr) { - // TODO : verify XRefSaveIndex and XRef's (how?) - - // verify offsets are in increasing order - uint32_t prev_offset; - uint8_t dummy_value; - GetOovpaEntry(oovpa, oovpa->XRefCount, prev_offset, dummy_value); - for (int p = oovpa->XRefCount + 1; p < oovpa->Count; p++) { - uint32_t curr_offset; - GetOovpaEntry(oovpa, p, curr_offset, dummy_value); - if (!(curr_offset > prev_offset)) { - HLEError(context, buildVersion, "Lovp[%d] : Offset (0x%x) must be larger then previous offset (0x%x)", - p, curr_offset, prev_offset); - } - } - - // find duplicate OOVPA's across all other data-table-oovpa's - context->oovpa = oovpa; - context->against = oovpa; - VerifyHLEDataBaseAgainst(context); - context->against = nullptr; // reset scanning state - return; - } - - // prevent checking an oovpa against itself - if (context->against == oovpa) { - return; - } - - // compare {Offset, Value}-pairs between two OOVPA's - OOVPA *left = context->against, *right = oovpa; - int l = 0, r = 0; - uint32_t left_offset, right_offset; - uint8_t left_value, right_value; - GetOovpaEntry(left, l, left_offset, left_value); - GetOovpaEntry(right, r, right_offset, right_value); - int unique_offset_left = 0; - int unique_offset_right = 0; - int equal_offset_value = 0; - int equal_offset_different_value = 0; - while (true) { - bool left_next = true; - bool right_next = true; - - if (left_offset < right_offset) { - unique_offset_left++; - right_next = false; - } else if (left_offset > right_offset) { - unique_offset_right++; - left_next = false; - } else if (left_value == right_value) { - equal_offset_value++; - } else { - equal_offset_different_value++; - } - - // increment r before use (in left_next) - if (right_next) { - r++; - } - - if (left_next) { - l++; - if (l >= left->Count) { - unique_offset_right += right->Count - r; - break; - } - - GetOovpaEntry(left, l, left_offset, left_value); - } - - if (right_next) { - if (r >= right->Count) { - unique_offset_left += left->Count - l; - break; - } - - GetOovpaEntry(right, r, right_offset, right_value); - } - } - - // no mismatching values on identical offsets? - if (equal_offset_different_value == 0) { - // enough matching OV-pairs? - if (equal_offset_value > 4) { - // no unique OV-pairs on either side? - if (unique_offset_left + unique_offset_right == 0) { - HLEError(context, buildVersion, "OOVPA's are identical", - unique_offset_left, - unique_offset_right); - } else { - // not too many new OV-pairs on the left side? - if (unique_offset_left < 6) { - // not too many new OV-parirs on the right side? - if (unique_offset_right < 6) { - HLEError(context, buildVersion, "OOVPA's are expanded (left +%d, right +%d)", - unique_offset_left, - unique_offset_right); - } - } - } - } - } -} - -void VerifyHLEDataEntry(HLEVerifyContext *context, const OOVPATable *table, uint32_t index) -{ - if (context->against == nullptr) { - context->main_index = index; - } else { - context->against_index = index; - } - - if (context->against == nullptr) { - const char* checkDisableStr = nullptr; - size_t getFuncStrLength = strlen(table[index].szFuncName); - - if (getFuncStrLength > 10) { - checkDisableStr = &table[index].szFuncName[getFuncStrLength - 10]; - } - - if (checkDisableStr != nullptr && strcmp(checkDisableStr, "_UNPATCHED") == 0) { - if (GetEmuPatchAddr((std::string)table[index].szFuncName)) { - HLEError(context, table[index].Version, "OOVPA registration UNPATCHED while a patch exists!"); - } - } else if (table[index].Oovpa->XRefSaveIndex != XRefNoSaveIndex) { - if (GetEmuPatchAddr((std::string)table[index].szFuncName)) { - HLEError(context, table[index].Version, "OOVPA registration XREF while a patch exists!"); - } - } - } - - // verify the OOVPA of this entry - if (table[index].Oovpa != nullptr) { - VerifyHLEOOVPA(context, table[index].Version, table[index].Oovpa); - } -} - -void VerifyHLEData(HLEVerifyContext *context, const HLEData *data) -{ - if (context->against == nullptr) { - context->main_data = data; - } else { - context->against_data = data; - } - - // Don't check a database against itself : - if (context->main_data == context->against_data) { - return; - } - - // verify each entry in this HLEData - for (uint32_t e = 0; e < data->OovpaTableCount; e++) { - VerifyHLEDataEntry(context, data->OovpaTable, e); - } -} - -void VerifyHLEDataBaseAgainst(HLEVerifyContext *context) -{ - // verify all HLEData's - for (uint32_t d = 0; d < HLEDataBaseCount; d++) { - VerifyHLEData(context, &HLEDataBase[d]); - } -} - -void VerifyHLEDataBase() -{ - HLEVerifyContext context = { 0 }; - VerifyHLEDataBaseAgainst(&context); -} -#endif // _DEBUG_TRACE -#endif +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2016-2018 Luke Usher +// * (c) 2016-2018 Patrick van Logchem +// * (c) 2017-2018 RadWolfie +// * (c) 2017-2018 jarupxx +// * (c) 2018 x1nixmzeng +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::HLE + +#include +#include // For std::setfill and std::setw + +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\Emu.h" +#include "EmuShared.h" +#include "common\CxbxDebugger.h" +#include "Logging.h" +#pragma comment(lib, "XbSymbolDatabase.lib") +#include "..\..\import\XbSymbolDatabase\XbSymbolDatabase.h" +#include "Intercept.hpp" +#include "Patches.hpp" +#include "common\util\hasher.h" + +#include +#include +#include +#include +#include +#include + +static const char* section_info = "Info"; +static struct { + const char* SymbolDatabaseVersionHash = "SymbolDatabaseVersionHash"; +} sect_info_keys; + +static const char* section_certificate = "Certificate"; +static struct { + const char* Name = "Name"; + const char* TitleID = "TitleID"; + const char* TitleIDHex = "TitleIDHex"; + const char* Region = "Region"; +} sect_certificate_keys; + +static const char* section_libs = "Libs"; +static struct { + const char* BuildVersion = "BuildVersion"; +} sect_libs_keys; + +static const char* section_symbols = "Symbols"; + +std::map g_SymbolAddresses; +bool g_SymbolCacheUsed = false; + +bool bLLE_APU = false; // Set this to true for experimental APU (sound) LLE +bool bLLE_GPU = false; // Set this to true for experimental GPU (graphics) LLE +bool bLLE_USB = false; // Set this to true for experimental USB (input) LLE +bool bLLE_JIT = false; // Set this to true for experimental JIT + +void* GetXboxFunctionPointer(std::string functionName) +{ + void* ptr = GetPatchedFunctionTrampoline(functionName); + if (ptr != nullptr) { + return ptr; + } + + // If we got here, the function wasn't patched, so we can just look it up the symbol cache + // and return the correct offset + auto symbol = g_SymbolAddresses.find(functionName); + if (symbol != g_SymbolAddresses.end()) { + return (void*)symbol->second; + } + + // Finally, if none of the above were matched, return nullptr + return nullptr; +} + +// NOTE: GetDetectedSymbolName do not get to be in XbSymbolDatabase, get symbol string in Cxbx project only. +std::string GetDetectedSymbolName(const xbaddr address, int * const symbolOffset) +{ + std::string result = ""; + int closestMatch = MAXINT; + + for (auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) { + xbaddr symbolAddr = it->second; + if (symbolAddr == xbnull) + continue; + + if (symbolAddr <= address) + { + int distance = address - symbolAddr; + if (closestMatch > distance) + { + closestMatch = distance; + result = it->first; + } + } + } + + if (closestMatch < MAXINT) + { + *symbolOffset = closestMatch; + return result; + } + + *symbolOffset = 0; + return "unknown"; +} + +// NOTE: VerifySymbolAddressAgainstXRef do not get to be in XbSymbolDatabase, perform verification in Cxbx project only. +/* +bool VerifySymbolAddressAgainstXRef(char *SymbolName, xbaddr Address, int XRef) +{ + // Temporary verification - is XREF_D3DTSS_TEXCOORDINDEX derived correctly? + // TODO : Remove this when XREF_D3DTSS_TEXCOORDINDEX derivation is deemed stable + xbaddr XRefAddr = XRefDataBase[XRef]; + if (XRefAddr == Address) + return true; + + if (XRefAddr == XREF_ADDR_DERIVE) { + printf("HLE: XRef #%d derived 0x%.08X -> %s\n", XRef, Address, SymbolName); + XRefDataBase[XRef] = Address; + return true; + } + + PopupCustom(LOG_LEVEL::WARNING, CxbxMsgDlgIcon_Warn, + "Verification of %s failed : XREF was 0x%.8X while lookup gave 0x%.8X", SymbolName, XRefAddr, Address); + // test case : Kabuki Warriors (for XREF_D3DTSS_TEXCOORDINDEX) + return false; +}*/ + +// x1nixmzeng: Hack to notify CxbxDebugger of the SymbolCache file, which is currently a hashed XBE header AND stripped title (see EmuHLEIntercept) +class CxbxDebuggerScopedMessage +{ + std::string& message; + + CxbxDebuggerScopedMessage() = delete; + CxbxDebuggerScopedMessage(const CxbxDebuggerScopedMessage&) = delete; +public: + + CxbxDebuggerScopedMessage(std::string& message_string) + : message(message_string) + { } + + ~CxbxDebuggerScopedMessage() + { + if (CxbxDebugger::CanReport()) + { + CxbxDebugger::ReportHLECacheFile(message.c_str()); + } + } +}; + +void CDECL EmuOutputMessage(xb_output_message mFlag, + const char* message) +{ + switch (mFlag) { + case XB_OUTPUT_MESSAGE_INFO: { + printf("%s\n", message); + break; + } + case XB_OUTPUT_MESSAGE_WARN: { + EmuLog(LOG_LEVEL::WARNING, "%s", message); + break; + } + case XB_OUTPUT_MESSAGE_ERROR: { + CxbxKrnlCleanup("%s", message); + break; + } + case XB_OUTPUT_MESSAGE_DEBUG: + default: { +#ifdef _DEBUG_TRACE + printf("%s\n", message); +#endif + + break; + } + } +} + +void CDECL EmuRegisterSymbol(const char* library_str, + uint32_t library_flag, + const char* symbol_str, + uint32_t func_addr, + uint32_t revision) +{ + // Ignore registered symbol in current database. + uint32_t hasSymbol = g_SymbolAddresses[symbol_str]; + if (hasSymbol != 0) + return; + + // Output some details + std::stringstream output; + output << "Symbol: 0x" << std::setfill('0') << std::setw(8) << std::hex << func_addr + << " -> " << symbol_str << " " << std::dec << revision; + +#if 0 // TODO: XbSymbolDatabase - Need to create a structure for patch and stuff. + bool IsXRef = OovpaTable->Oovpa->XRefSaveIndex != XRefNoSaveIndex; + if (IsXRef) { + output << "\t(XREF)"; + + // do we need to save the found address? + OOVPA* Oovpa = OovpaTable->Oovpa; + if (Oovpa->XRefSaveIndex != XRefNoSaveIndex) { + // is the XRef not saved yet? + switch (XRefDataBase[Oovpa->XRefSaveIndex]) { + case XREF_ADDR_NOT_FOUND: + { + EmuLog(LOG_LEVEL::WARNING, "Found OOVPA after first finding nothing?"); + // fallthrough to XREF_ADDR_UNDETERMINED + } + case XREF_ADDR_UNDETERMINED: + { + // save and count the found address + UnResolvedXRefs--; + XRefDataBase[Oovpa->XRefSaveIndex] = pFunc; + break; + } + case XREF_ADDR_DERIVE: + { + EmuLog(LOG_LEVEL::WARNING, "Cannot derive a save index!"); + break; + } + default: + { + if (XRefDataBase[OovpaTable->Oovpa->XRefSaveIndex] != pFunc) { + EmuLog(LOG_LEVEL::WARNING, "Found OOVPA on other address than in XRefDataBase!"); + EmuLog(LOG_LEVEL::WARNING, "%s: %4d - pFunc: %08X, stored: %08X", OovpaTable->szFuncName, Oovpa->XRefSaveIndex, pFunc, XRefDataBase[Oovpa->XRefSaveIndex]); + } + break; + } + } + } + } + + // Retrieve the associated patch, if any is available + void* addr = GetEmuPatchAddr(std::string(OovpaTable->szFuncName)); + + if (addr != nullptr) { + EmuInstallPatch(OovpaTable->szFuncName, pFunc, addr); + output << "\t*PATCHED*"; + } else { + const char* checkDisableStr = nullptr; + size_t getFuncStrLength = strlen(OovpaTable->szFuncName); + + if (getFuncStrLength > 10) { + checkDisableStr = &OovpaTable->szFuncName[getFuncStrLength - 10]; + } + + if (checkDisableStr != nullptr && strcmp(checkDisableStr, "_UNPATCHED") == 0) { + output << "\t*UNPATCHED*"; + + // Mention there's no patch available, if it was to be applied + } else if (!IsXRef) { + output << "\t*NO PATCH AVAILABLE!*"; + } + } +#endif + + output << "\n"; + + g_SymbolAddresses[symbol_str] = func_addr; + printf(output.str().c_str()); +} + +// Update shared structure with GUI process +void EmuUpdateLLEStatus(uint32_t XbLibScan) +{ + unsigned int FlagsLLE; + g_EmuShared->GetFlagsLLE(&FlagsLLE); + + if ((FlagsLLE & LLE_GPU) == false + && !((XbLibScan & XbSymbolLib_D3D8) > 0 + || (XbLibScan & XbSymbolLib_D3D8LTCG) > 0)) { + bLLE_GPU = true; + FlagsLLE ^= LLE_GPU; + EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE GPU."); + } + + if ((FlagsLLE & LLE_APU) == false + && (XbLibScan & XbSymbolLib_DSOUND) == 0) { + bLLE_APU = true; + FlagsLLE ^= LLE_APU; + EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE APU."); + } +#if 0 // Reenable this when LLE USB actually works + if ((FlagsLLE & LLE_USB) == false + && (XbLibScan & XbSymbolLib_XAPILIB) == 0) { + bLLE_USB = true; + FlagsLLE ^= LLE_USB; + EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE USB."); + } +#endif + ipc_send_gui_update(IPC_UPDATE_GUI::LLE_FLAGS, FlagsLLE); + //return FlagsLLE; +} + +// NOTE: EmuHLEIntercept do not get to be in XbSymbolDatabase, do the intecept in Cxbx project only. +void EmuHLEIntercept(Xbe::Header *pXbeHeader) +{ + Xbe::LibraryVersion *pLibraryVersion = (Xbe::LibraryVersion*)pXbeHeader->dwLibraryVersionsAddr; + + uint16_t xdkVersion = 0; + uint32_t XbLibScan = 0; + + // NOTE: We need to check if title has library header to optimize verification process. + if (pLibraryVersion != nullptr) { + uint32_t dwLibraryVersions = pXbeHeader->dwLibraryVersions; + const char* SectionName = nullptr; + Xbe::SectionHeader* pSectionHeaders = (Xbe::SectionHeader*)pXbeHeader->dwSectionHeadersAddr; + uint32_t XbLibFlag; + + // Get the highest revision build and prefix library to scan. + for (uint32_t v = 0; v < dwLibraryVersions; v++) { + uint16_t BuildVersion, QFEVersion; + BuildVersion = pLibraryVersion[v].wBuildVersion; + QFEVersion = pLibraryVersion[v].wFlags.QFEVersion; + + if (xdkVersion < BuildVersion) { + xdkVersion = BuildVersion; + } + XbLibFlag = XbSymbolDatabase_LibraryToFlag(std::string(pLibraryVersion[v].szName, pLibraryVersion[v].szName + 8).c_str()); + XbLibScan |= XbLibFlag; + + // Keep certain library versions for plugin usage. + if ((XbLibFlag & (XbSymbolLib_D3D8 | XbSymbolLib_D3D8LTCG)) > 0) { + if (g_LibVersion_D3D8 < BuildVersion) { + g_LibVersion_D3D8 = BuildVersion; + } + } + else if ((XbLibFlag & XbSymbolLib_DSOUND) > 0) { + g_LibVersion_DSOUND = BuildVersion; + } + } + + // Since XDK 4039 title does not have library version for DSOUND, let's check section header if it exists or not. + for (unsigned int v = 0; v < pXbeHeader->dwSections; v++) { + SectionName = (const char*)pSectionHeaders[v].dwSectionNameAddr; + if (strncmp(SectionName, Lib_DSOUND, 8) == 0) { + XbLibScan |= XbSymbolLib_DSOUND; + + // If DSOUND version is not set, we need to force set it. + if (g_LibVersion_DSOUND == 0) { + g_LibVersion_DSOUND = xdkVersion; + } + break; + } + } + } + + EmuUpdateLLEStatus(XbLibScan); + + std::cout << "\n" + "*******************************************************************************\n" + "* Cxbx-Reloaded High Level Emulation database\n" + "*******************************************************************************\n" + << std::endl; + + // Make sure the Symbol Cache directory exists + std::string cachePath = std::string(szFolder_CxbxReloadedData) + "\\SymbolCache\\"; + if (!std::filesystem::exists(cachePath) && !std::filesystem::create_directory(cachePath)) { + CxbxKrnlCleanup("Couldn't create Cxbx-Reloaded SymbolCache folder!"); + } + + // Hash the loaded XBE's header, use it as a filename + uint64_t uiHash = ComputeHash((void*)&CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header)); + std::stringstream sstream; + char tAsciiTitle[40] = "Unknown"; + std::setlocale(LC_ALL, "English"); + std::wcstombs(tAsciiTitle, g_pCertificate->wszTitleName, sizeof(tAsciiTitle)); + std::string szTitleName(tAsciiTitle); + CxbxKrnl_Xbe->PurgeBadChar(szTitleName); + sstream << cachePath << szTitleName << "-" << std::hex << uiHash << ".ini"; + std::string filename = sstream.str(); + + // This will fire when we exit this function scope; either after detecting a previous cache file, or when one is created + CxbxDebuggerScopedMessage symbolCacheFilename(filename); + + CSimpleIniA symbolCacheData; + + if (std::filesystem::exists(filename.c_str())) { + std::printf("Found Symbol Cache File: %08llX.ini\n", uiHash); + + symbolCacheData.LoadFile(filename.c_str()); + + xdkVersion = (uint16_t)symbolCacheData.GetLongValue(section_libs, sect_libs_keys.BuildVersion, /*Default=*/0); + + // Verify the version of the cache file against the Symbol Database version hash + const uint32_t SymbolDatabaseVersionHash = symbolCacheData.GetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, /*Default=*/0); + + if (SymbolDatabaseVersionHash == XbSymbolDatabase_LibraryVersion()) { + g_SymbolCacheUsed = true; + CSimpleIniA::TNamesDepend symbol_names; + + if (g_SymbolCacheUsed) { + std::printf("Using Symbol Cache\n"); + + // Parse the .INI file into the map of symbol addresses + symbolCacheData.GetAllKeys(section_symbols, symbol_names); + for (auto it = symbol_names.begin(); it != symbol_names.end(); ++it) { + g_SymbolAddresses[it->pItem] = symbolCacheData.GetLongValue(section_symbols, it->pItem, /*Default=*/0); + } + + // Iterate through the map of symbol addresses, calling GetEmuPatchAddr on all functions. + for (auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) { + std::string functionName = it->first; + xbaddr location = it->second; + + std::stringstream output; + output << "SymbolCache: 0x" << std::setfill('0') << std::setw(8) << std::hex << location + << " -> " << functionName << "\n"; + std::printf(output.str().c_str()); + } + + // Fix up Render state and Texture States + if (g_SymbolAddresses.find("D3DDeferredRenderState") == g_SymbolAddresses.end() + || g_SymbolAddresses["D3DDeferredRenderState"] == 0) { + EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState was not found!"); + } + + if (g_SymbolAddresses.find("D3DDeferredTextureState") == g_SymbolAddresses.end() + || g_SymbolAddresses["D3DDeferredTextureState"] == 0) { + EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredTextureState was not found!"); + } + + if (g_SymbolAddresses.find("D3DDEVICE") == g_SymbolAddresses.end() + || g_SymbolAddresses["D3DDEVICE"] == 0) { + EmuLog(LOG_LEVEL::WARNING, "D3DDEVICE was not found!"); + } + } + } + + // If g_SymbolAddresses didn't get filled, then symbol cache is invalid + if (g_SymbolAddresses.empty()) { + std::printf("Symbol Cache file is outdated and will be regenerated\n"); + g_SymbolCacheUsed = false; + } + } + + // If the Symbol Cache was used, go straight to patching, no need to re-scan + if (g_SymbolCacheUsed) { + EmuInstallPatches(); + return; + } + + // + // initialize Microsoft XDK emulation + // + if(pLibraryVersion != nullptr) { + + std::printf("Symbol: Detected Microsoft XDK application...\n"); + +#if 0 // NOTE: This code is currently disabled due to not optimized and require more work to do. + + XbSymbolRegisterLibrary(XbLibScan); + + while (true) { + + size_t SymbolSize = g_SymbolAddresses.size(); + + Xbe::SectionHeader* pSectionHeaders = reinterpret_cast(pXbeHeader->dwSectionHeadersAddr); + Xbe::SectionHeader* pSectionScan = nullptr; + + for (uint32_t v = 0; v < pXbeHeader->dwSections; v++) { + + pSectionScan = pSectionHeaders + v; + + XbSymbolScanSection((uint32_t)pXbeHeader, 64 * ONE_MB, (const char*)pSectionScan->dwSectionNameAddr, pSectionScan->dwVirtualAddr, pSectionScan->dwSizeofRaw, xdkVersion, EmuRegisterSymbol); + } + + // If symbols are not adding to array, break the loop. + if (SymbolSize == g_SymbolAddresses.size()) { + break; + } + } +#endif + + XbSymbolDatabase_SetOutputMessage(EmuOutputMessage); + + XbSymbolScan(pXbeHeader, EmuRegisterSymbol, false); + } + + std::printf("\n"); + + // Perform a reset just in case a cached file data still exist. + symbolCacheData.Reset(); + + // Store Symbol Database version + symbolCacheData.SetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, XbSymbolDatabase_LibraryVersion(), nullptr, /*UseHex =*/false); + + // Store Certificate Details + symbolCacheData.SetValue(section_certificate, sect_certificate_keys.Name, tAsciiTitle); + symbolCacheData.SetValue(section_certificate, sect_certificate_keys.TitleID, FormatTitleId(g_pCertificate->dwTitleId).c_str()); + symbolCacheData.SetLongValue(section_certificate, sect_certificate_keys.TitleIDHex, g_pCertificate->dwTitleId, nullptr, /*UseHex =*/true); + symbolCacheData.SetLongValue(section_certificate, sect_certificate_keys.Region, g_pCertificate->dwGameRegion, nullptr, /*UseHex =*/true); + + // Store Library Details + for (unsigned int i = 0; i < pXbeHeader->dwLibraryVersions; i++) { + std::string LibraryName(pLibraryVersion[i].szName, pLibraryVersion[i].szName + 8); + symbolCacheData.SetLongValue(section_libs, LibraryName.c_str(), pLibraryVersion[i].wBuildVersion, nullptr, /*UseHex =*/false); + } + + symbolCacheData.SetLongValue(section_libs, sect_libs_keys.BuildVersion, xdkVersion, nullptr, /*UseHex =*/false); + + // Store detected symbol addresses + for(auto it = g_SymbolAddresses.begin(); it != g_SymbolAddresses.end(); ++it) { + symbolCacheData.SetLongValue(section_symbols, it->first.c_str(), it->second, nullptr, /*UseHex =*/true); + } + + // Save data to unique symbol cache file + symbolCacheData.SaveFile(filename.c_str()); + + EmuInstallPatches(); +} + + +#if 0 // TODO: Need to move this into XbSymbolDatabase for depth verification usage. +#ifdef _DEBUG_TRACE + +struct HLEVerifyContext { + const HLEData *main_data; + OOVPA *oovpa, *against; + const HLEData *against_data; + uint32_t main_index, against_index; +}; + +std::string HLEErrorString(const HLEData *data, uint16_t buildVersion, uint32_t index) +{ + std::string result = + "OOVPATable " + (std::string)(data->LibSec.library) + "_" + std::to_string(buildVersion) + + "[" + std::to_string(index) + "] " + + (std::string)(data->OovpaTable[index].szFuncName); + + return result; +} + +void HLEError(HLEVerifyContext *context, uint16_t buildVersion, char *format, ...) +{ + std::string output = "HLE Error "; + if (context->main_data != nullptr) { + output += "in " + HLEErrorString(context->main_data, buildVersion, context->main_index); + } + + if (context->against != nullptr && context->against_data != nullptr) { + output += ", comparing against " + HLEErrorString(context->against_data, buildVersion, context->against_index); + } + + // format specific error message + char buffer[200]; + va_list args; + va_start(args, format); + vsprintf(buffer, format, args); + va_end(args); + + output += " : " + (std::string)buffer + (std::string)"\n"; + printf(output.c_str()); +} + +void VerifyHLEDataBaseAgainst(HLEVerifyContext *context); // forward + +void VerifyHLEOOVPA(HLEVerifyContext *context, uint16_t buildVersion, OOVPA *oovpa) +{ + if (context->against == nullptr) { + // TODO : verify XRefSaveIndex and XRef's (how?) + + // verify offsets are in increasing order + uint32_t prev_offset; + uint8_t dummy_value; + GetOovpaEntry(oovpa, oovpa->XRefCount, prev_offset, dummy_value); + for (int p = oovpa->XRefCount + 1; p < oovpa->Count; p++) { + uint32_t curr_offset; + GetOovpaEntry(oovpa, p, curr_offset, dummy_value); + if (!(curr_offset > prev_offset)) { + HLEError(context, buildVersion, "Lovp[%d] : Offset (0x%x) must be larger then previous offset (0x%x)", + p, curr_offset, prev_offset); + } + } + + // find duplicate OOVPA's across all other data-table-oovpa's + context->oovpa = oovpa; + context->against = oovpa; + VerifyHLEDataBaseAgainst(context); + context->against = nullptr; // reset scanning state + return; + } + + // prevent checking an oovpa against itself + if (context->against == oovpa) { + return; + } + + // compare {Offset, Value}-pairs between two OOVPA's + OOVPA *left = context->against, *right = oovpa; + int l = 0, r = 0; + uint32_t left_offset, right_offset; + uint8_t left_value, right_value; + GetOovpaEntry(left, l, left_offset, left_value); + GetOovpaEntry(right, r, right_offset, right_value); + int unique_offset_left = 0; + int unique_offset_right = 0; + int equal_offset_value = 0; + int equal_offset_different_value = 0; + while (true) { + bool left_next = true; + bool right_next = true; + + if (left_offset < right_offset) { + unique_offset_left++; + right_next = false; + } else if (left_offset > right_offset) { + unique_offset_right++; + left_next = false; + } else if (left_value == right_value) { + equal_offset_value++; + } else { + equal_offset_different_value++; + } + + // increment r before use (in left_next) + if (right_next) { + r++; + } + + if (left_next) { + l++; + if (l >= left->Count) { + unique_offset_right += right->Count - r; + break; + } + + GetOovpaEntry(left, l, left_offset, left_value); + } + + if (right_next) { + if (r >= right->Count) { + unique_offset_left += left->Count - l; + break; + } + + GetOovpaEntry(right, r, right_offset, right_value); + } + } + + // no mismatching values on identical offsets? + if (equal_offset_different_value == 0) { + // enough matching OV-pairs? + if (equal_offset_value > 4) { + // no unique OV-pairs on either side? + if (unique_offset_left + unique_offset_right == 0) { + HLEError(context, buildVersion, "OOVPA's are identical", + unique_offset_left, + unique_offset_right); + } else { + // not too many new OV-pairs on the left side? + if (unique_offset_left < 6) { + // not too many new OV-parirs on the right side? + if (unique_offset_right < 6) { + HLEError(context, buildVersion, "OOVPA's are expanded (left +%d, right +%d)", + unique_offset_left, + unique_offset_right); + } + } + } + } + } +} + +void VerifyHLEDataEntry(HLEVerifyContext *context, const OOVPATable *table, uint32_t index) +{ + if (context->against == nullptr) { + context->main_index = index; + } else { + context->against_index = index; + } + + if (context->against == nullptr) { + const char* checkDisableStr = nullptr; + size_t getFuncStrLength = strlen(table[index].szFuncName); + + if (getFuncStrLength > 10) { + checkDisableStr = &table[index].szFuncName[getFuncStrLength - 10]; + } + + if (checkDisableStr != nullptr && strcmp(checkDisableStr, "_UNPATCHED") == 0) { + if (GetEmuPatchAddr((std::string)table[index].szFuncName)) { + HLEError(context, table[index].Version, "OOVPA registration UNPATCHED while a patch exists!"); + } + } else if (table[index].Oovpa->XRefSaveIndex != XRefNoSaveIndex) { + if (GetEmuPatchAddr((std::string)table[index].szFuncName)) { + HLEError(context, table[index].Version, "OOVPA registration XREF while a patch exists!"); + } + } + } + + // verify the OOVPA of this entry + if (table[index].Oovpa != nullptr) { + VerifyHLEOOVPA(context, table[index].Version, table[index].Oovpa); + } +} + +void VerifyHLEData(HLEVerifyContext *context, const HLEData *data) +{ + if (context->against == nullptr) { + context->main_data = data; + } else { + context->against_data = data; + } + + // Don't check a database against itself : + if (context->main_data == context->against_data) { + return; + } + + // verify each entry in this HLEData + for (uint32_t e = 0; e < data->OovpaTableCount; e++) { + VerifyHLEDataEntry(context, data->OovpaTable, e); + } +} + +void VerifyHLEDataBaseAgainst(HLEVerifyContext *context) +{ + // verify all HLEData's + for (uint32_t d = 0; d < HLEDataBaseCount; d++) { + VerifyHLEData(context, &HLEDataBase[d]); + } +} + +void VerifyHLEDataBase() +{ + HLEVerifyContext context = { 0 }; + VerifyHLEDataBaseAgainst(&context); +} +#endif // _DEBUG_TRACE +#endif diff --git a/src/core/hle/XACTENG/XactEng.h b/src/core/hle/XACTENG/XactEng.h index 81a4e74c2..bda6c0f7c 100644 --- a/src/core/hle/XACTENG/XactEng.h +++ b/src/core/hle/XACTENG/XactEng.h @@ -26,7 +26,7 @@ #define EMUXACTENG_H #include "common\xbe\Xbe.h" -#include "core\hle\XAPI\Xapi.h" // For EMUPATCH +#include "core\hle\XAPI\Xapi.h" // For EMUPATCH #undef FIELD_OFFSET // prevent macro redefinition warnings #include diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 24ad1e347..292a5ee9c 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -27,11 +27,11 @@ #define LOG_PREFIX CXBXR_MODULE::XAPI -#undef FIELD_OFFSET // prevent macro redefinition warnings - +#undef FIELD_OFFSET // prevent macro redefinition warnings -#include -#include "common\input\SdlJoystick.h" + +#include +#include "common\input\SdlJoystick.h" #include "common\input\InputManager.h" #include #include "core\kernel\init\CxbxKrnl.h" @@ -43,7 +43,7 @@ #include "core\hle\Intercept.hpp" #include "vsbc\CxbxVSBC.h" #include "Windef.h" -#include +#include #include "core\hle\XAPI\Xapi.h" #include "core\hle\XAPI\XapiCxbxr.h" @@ -61,128 +61,128 @@ PFARPROC1 fnCxbxVSBCOpen; //typedef DWORD(*fnCxbxVSBCOpen)(HWND); //typedef DWORD(*fnCxbxVSBCSetState)(UCHAR *); //typedef DWORD(*fnCxbxVSBCGetState)(UCHAR *); -XTL::PXPP_DEVICE_TYPE g_DeviceType_Gamepad = nullptr; - -// Flag is unset after initialize devices is done by simulate LLE USB thread. +XTL::PXPP_DEVICE_TYPE g_DeviceType_Gamepad = nullptr; + +// Flag is unset after initialize devices is done by simulate LLE USB thread. std::atomic g_bIsDevicesInitializing = true; std::atomic g_bIsDevicesEmulating = false; //global bridge for xbox controller to host, 4 elements for 4 ports. -CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4] = { - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, - { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, +CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4] = { + { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, + { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, + { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, + { NULL, PORT_INVALID, XBOX_INPUT_DEVICE::DEVICE_INVALID, nullptr, false, false, false, { 0, 0, 0, 0, 0 } }, }; - -bool operator==(XTL::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) -{ - switch (XidType) - { - case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: - case XBOX_INPUT_DEVICE::MS_CONTROLLER_S: { - if (XppType == g_DeviceType_Gamepad) { - return true; - } - } - break; - - case XBOX_INPUT_DEVICE::LIGHT_GUN: - case XBOX_INPUT_DEVICE::STEERING_WHEEL: - case XBOX_INPUT_DEVICE::MEMORY_UNIT: - case XBOX_INPUT_DEVICE::IR_DONGLE: - case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER: - default: - break; - } - - return false; -} - -bool operator!=(XTL::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) -{ - return !(XppType == XidType); -} - -bool ConstructHleInputDevice(int Type, int Port) -{ - g_bIsDevicesEmulating = true; - bool ret = true; - - switch (Type) - { - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): { - g_XboxControllerHostBridge[Port].XboxPort = Port; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE; - g_XboxControllerHostBridge[Port].InState = new XpadInput(); - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - } - break; - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): { - g_XboxControllerHostBridge[Port].XboxPort = Port; - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_S; - g_XboxControllerHostBridge[Port].InState = new XpadInput(); - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - } - break; - case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): { - // TODO - } - break; - - case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN): - case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL): - case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): - case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): { - EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(Type).c_str()); - ret = false; - } - break; - - default: - EmuLog(LOG_LEVEL::WARNING, "Attempted to attach an unknown device type (type was %d)", Type); - ret = false; - break; - } - - g_bIsDevicesEmulating = false; - return ret; -} - -void DestructHleInputDevice(int Port) -{ - g_bIsDevicesEmulating = true; - - g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::DEVICE_INVALID; - g_XboxControllerHostBridge[Port].XboxPort = PORT_INVALID; - while (g_XboxControllerHostBridge[Port].bIoInProgress) {} - delete g_XboxControllerHostBridge[Port].InState; - g_XboxControllerHostBridge[Port].bPendingRemoval = false; - g_XboxControllerHostBridge[Port].bSignaled = false; - g_XboxControllerHostBridge[Port].bIoInProgress = false; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = 0; - g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; - - g_bIsDevicesEmulating = false; -} + +bool operator==(XTL::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) +{ + switch (XidType) + { + case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE: + case XBOX_INPUT_DEVICE::MS_CONTROLLER_S: { + if (XppType == g_DeviceType_Gamepad) { + return true; + } + } + break; + + case XBOX_INPUT_DEVICE::LIGHT_GUN: + case XBOX_INPUT_DEVICE::STEERING_WHEEL: + case XBOX_INPUT_DEVICE::MEMORY_UNIT: + case XBOX_INPUT_DEVICE::IR_DONGLE: + case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER: + default: + break; + } + + return false; +} + +bool operator!=(XTL::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType) +{ + return !(XppType == XidType); +} + +bool ConstructHleInputDevice(int Type, int Port) +{ + g_bIsDevicesEmulating = true; + bool ret = true; + + switch (Type) + { + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): { + g_XboxControllerHostBridge[Port].XboxPort = Port; + g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE; + g_XboxControllerHostBridge[Port].InState = new XpadInput(); + g_XboxControllerHostBridge[Port].bPendingRemoval = false; + g_XboxControllerHostBridge[Port].bSignaled = false; + g_XboxControllerHostBridge[Port].bIoInProgress = false; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); + g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; + } + break; + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): { + g_XboxControllerHostBridge[Port].XboxPort = Port; + g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::MS_CONTROLLER_S; + g_XboxControllerHostBridge[Port].InState = new XpadInput(); + g_XboxControllerHostBridge[Port].bPendingRemoval = false; + g_XboxControllerHostBridge[Port].bSignaled = false; + g_XboxControllerHostBridge[Port].bIoInProgress = false; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = XINPUT_DEVTYPE_GAMEPAD; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = sizeof(XpadInput); + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = sizeof(XpadOutput); + g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; + } + break; + case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): { + // TODO + } + break; + + case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN): + case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL): + case to_underlying(XBOX_INPUT_DEVICE::MEMORY_UNIT): + case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE): { + EmuLog(LOG_LEVEL::INFO, "%s: device %s is not yet supported", __func__, GetInputDeviceName(Type).c_str()); + ret = false; + } + break; + + default: + EmuLog(LOG_LEVEL::WARNING, "Attempted to attach an unknown device type (type was %d)", Type); + ret = false; + break; + } + + g_bIsDevicesEmulating = false; + return ret; +} + +void DestructHleInputDevice(int Port) +{ + g_bIsDevicesEmulating = true; + + g_XboxControllerHostBridge[Port].XboxType = XBOX_INPUT_DEVICE::DEVICE_INVALID; + g_XboxControllerHostBridge[Port].XboxPort = PORT_INVALID; + while (g_XboxControllerHostBridge[Port].bIoInProgress) {} + delete g_XboxControllerHostBridge[Port].InState; + g_XboxControllerHostBridge[Port].bPendingRemoval = false; + g_XboxControllerHostBridge[Port].bSignaled = false; + g_XboxControllerHostBridge[Port].bIoInProgress = false; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucType = 0; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType = 0; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucInputStateSize = 0; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucFeedbackSize = 0; + g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber = 0; + + g_bIsDevicesEmulating = false; +} void SetupXboxDeviceTypes() { @@ -281,36 +281,36 @@ VOID WINAPI XTL::EMUPATCH(XInitDevices) }).detach(); } -// This is called to emulate async for both XGetDevices and XGetDeviceChanges -void UpdateConnectedDeviceState(XTL::PXPP_DEVICE_TYPE DeviceType) { - - // Do not process the queries until initialize delay and device emulating are complete. - if (g_bIsDevicesInitializing || g_bIsDevicesEmulating){ - return; - } - +// This is called to emulate async for both XGetDevices and XGetDeviceChanges +void UpdateConnectedDeviceState(XTL::PXPP_DEVICE_TYPE DeviceType) { + + // Do not process the queries until initialize delay and device emulating are complete. + if (g_bIsDevicesInitializing || g_bIsDevicesEmulating){ + return; + } + int Port, PortMask; for (Port = PORT_1, PortMask = 1; Port <= PORT_4; Port++, PortMask <<= 1) { - if (DeviceType == g_XboxControllerHostBridge[Port].XboxType) { + if (DeviceType == g_XboxControllerHostBridge[Port].XboxType) { if (!g_XboxControllerHostBridge[Port].bPendingRemoval) { DeviceType->CurrentConnected |= PortMask; - } - else { - if (!g_XboxControllerHostBridge[Port].bSignaled) { - g_XboxControllerHostBridge[Port].bSignaled = true; - SDL_Event DeviceRemoveEvent; - SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); - DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; - DeviceRemoveEvent.user.data1 = new int(Port); - SDL_PushEvent(&DeviceRemoveEvent); - } - DeviceType->CurrentConnected &= ~PortMask; } - } + else { + if (!g_XboxControllerHostBridge[Port].bSignaled) { + g_XboxControllerHostBridge[Port].bSignaled = true; + SDL_Event DeviceRemoveEvent; + SDL_memset(&DeviceRemoveEvent, 0, sizeof(SDL_Event)); + DeviceRemoveEvent.type = Sdl::DeviceRemoveAck_t; + DeviceRemoveEvent.user.data1 = new int(Port); + SDL_PushEvent(&DeviceRemoveEvent); + } + DeviceType->CurrentConnected &= ~PortMask; + } + } else { DeviceType->CurrentConnected &= ~PortMask; } - } + } DeviceType->ChangeConnected = DeviceType->PreviousConnected ^ DeviceType->CurrentConnected; } @@ -326,14 +326,14 @@ DWORD WINAPI XTL::EMUPATCH(XGetDevices) PXPP_DEVICE_TYPE DeviceType ) { - LOG_FUNC_ONE_ARG(DeviceType); - + LOG_FUNC_ONE_ARG(DeviceType); + UpdateConnectedDeviceState(DeviceType); UCHAR oldIrql = xboxkrnl::KeRaiseIrqlToDpcLevel(); - - DWORD ret = DeviceType->CurrentConnected; - + + DWORD ret = DeviceType->CurrentConnected; + DeviceType->ChangeConnected = 0; DeviceType->PreviousConnected = DeviceType->CurrentConnected; @@ -362,8 +362,8 @@ BOOL WINAPI XTL::EMUPATCH(XGetDeviceChanges) LOG_FUNC_ARG(pdwRemovals) LOG_FUNC_END; - UpdateConnectedDeviceState(DeviceType); - + UpdateConnectedDeviceState(DeviceType); + BOOL ret = FALSE; @@ -375,17 +375,17 @@ BOOL WINAPI XTL::EMUPATCH(XGetDeviceChanges) else { UCHAR oldIrql = xboxkrnl::KeRaiseIrqlToDpcLevel(); - + // Insertions and removals *pdwInsertions = (DeviceType->CurrentConnected & ~DeviceType->PreviousConnected); - *pdwRemovals = (DeviceType->PreviousConnected & ~DeviceType->CurrentConnected); - + *pdwRemovals = (DeviceType->PreviousConnected & ~DeviceType->CurrentConnected); + // Detect devices that were removed and then immediately inserted again ULONG RemoveInsert = DeviceType->ChangeConnected & DeviceType->CurrentConnected & DeviceType->PreviousConnected; *pdwRemovals |= RemoveInsert; - *pdwInsertions |= RemoveInsert; + *pdwInsertions |= RemoveInsert; DeviceType->ChangeConnected = 0; DeviceType->PreviousConnected = DeviceType->CurrentConnected; @@ -416,7 +416,7 @@ HANDLE WINAPI XTL::EMUPATCH(XInputOpen) LOG_FUNC_END; if (dwPort >= PORT_1 && dwPort <= PORT_4) { - if (DeviceType == g_XboxControllerHostBridge[dwPort].XboxType) { + if (DeviceType == g_XboxControllerHostBridge[dwPort].XboxType) { #if 0 // TODO if(g_XboxControllerHostBridge[dwPort].dwHostType==X_XONTROLLER_HOST_BRIDGE_HOSTTYPE_VIRTUAL_SBC){ //if DLL not loaded yet, load it. @@ -441,8 +441,8 @@ HANDLE WINAPI XTL::EMUPATCH(XInputOpen) } //DWORD dwVXBCOpenResult = CxbxVSBC::MyCxbxVSBC::VSBCOpen(X_XONTROLLER_HOST_BRIDGE_HOSTTYPE_VIRTUAL_SBC); - } -#endif + } +#endif g_XboxControllerHostBridge[dwPort].hXboxDevice = &g_XboxControllerHostBridge[dwPort]; RETURN(g_XboxControllerHostBridge[dwPort].hXboxDevice); } @@ -462,10 +462,10 @@ VOID WINAPI XTL::EMUPATCH(XInputClose) { LOG_FUNC_ONE_ARG(hDevice); - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; - int Port = Device->XboxPort; + PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + int Port = Device->XboxPort; if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) { - Device->hXboxDevice = NULL; + Device->hXboxDevice = NULL; } } @@ -477,9 +477,9 @@ DWORD WINAPI XTL::EMUPATCH(XInputPoll) IN HANDLE hDevice ) { - LOG_FUNC_ONE_ARG(hDevice); - - // Nothing to do + LOG_FUNC_ONE_ARG(hDevice); + + // Nothing to do RETURN(ERROR_SUCCESS); } @@ -498,8 +498,8 @@ DWORD WINAPI XTL::EMUPATCH(XInputGetCapabilities) LOG_FUNC_ARG_OUT(pCapabilities) LOG_FUNC_END; - DWORD ret = ERROR_DEVICE_NOT_CONNECTED; - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + DWORD ret = ERROR_DEVICE_NOT_CONNECTED; + PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; int Port = Device->XboxPort; if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) { pCapabilities->SubType = g_XboxControllerHostBridge[Port].XboxDeviceInfo.ucSubType; @@ -752,18 +752,18 @@ DWORD WINAPI XTL::EMUPATCH(XInputGetState) LOG_FUNC_ARG_OUT(pState) LOG_FUNC_END; - DWORD ret = ERROR_DEVICE_NOT_CONNECTED; - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + DWORD ret = ERROR_DEVICE_NOT_CONNECTED; + PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; int Port = Device->XboxPort; - if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) { - if (!g_XboxControllerHostBridge[Port].bPendingRemoval) { - g_XboxControllerHostBridge[Port].bIoInProgress = true; - if (g_InputDeviceManager.UpdateXboxPortInput(Port, g_XboxControllerHostBridge[Port].InState, DIRECTION_IN, to_underlying(g_XboxControllerHostBridge[Port].XboxType))) { - pState->dwPacketNumber = g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber++; - } - memcpy((void*)&pState->Gamepad, g_XboxControllerHostBridge[Port].InState, sizeof(pState->Gamepad)); - g_XboxControllerHostBridge[Port].bIoInProgress = false; - ret = ERROR_SUCCESS; + if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice) { + if (!g_XboxControllerHostBridge[Port].bPendingRemoval) { + g_XboxControllerHostBridge[Port].bIoInProgress = true; + if (g_InputDeviceManager.UpdateXboxPortInput(Port, g_XboxControllerHostBridge[Port].InState, DIRECTION_IN, to_underlying(g_XboxControllerHostBridge[Port].XboxType))) { + pState->dwPacketNumber = g_XboxControllerHostBridge[Port].XboxDeviceInfo.dwPacketNumber++; + } + memcpy((void*)&pState->Gamepad, g_XboxControllerHostBridge[Port].InState, sizeof(pState->Gamepad)); + g_XboxControllerHostBridge[Port].bIoInProgress = false; + ret = ERROR_SUCCESS; } } @@ -785,18 +785,18 @@ DWORD WINAPI XTL::EMUPATCH(XInputSetState) LOG_FUNC_ARG(hDevice) LOG_FUNC_ARG(pFeedback) LOG_FUNC_END; - - PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; + + PCXBX_CONTROLLER_HOST_BRIDGE Device = (PCXBX_CONTROLLER_HOST_BRIDGE)hDevice; int Port = Device->XboxPort; if (g_XboxControllerHostBridge[Port].hXboxDevice == hDevice && !g_XboxControllerHostBridge[Port].bPendingRemoval) { pFeedback->Header.dwStatus = ERROR_IO_PENDING; - g_InputDeviceManager.UpdateXboxPortInput(Port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_XboxControllerHostBridge[Port].XboxType)); - pFeedback->Header.dwStatus = ERROR_SUCCESS; - if (pFeedback->Header.hEvent != NULL && - ObReferenceObjectByHandle(pFeedback->Header.hEvent, &xboxkrnl::ExEventObjectType, (PVOID*)&pFeedback->Header.IoCompletedEvent) == ERROR_SUCCESS) { - KeSetEvent((xboxkrnl::PKEVENT)pFeedback->Header.IoCompletedEvent, NULL, FALSE); + g_InputDeviceManager.UpdateXboxPortInput(Port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_XboxControllerHostBridge[Port].XboxType)); + pFeedback->Header.dwStatus = ERROR_SUCCESS; + if (pFeedback->Header.hEvent != NULL && + ObReferenceObjectByHandle(pFeedback->Header.hEvent, &xboxkrnl::ExEventObjectType, (PVOID*)&pFeedback->Header.IoCompletedEvent) == ERROR_SUCCESS) { + KeSetEvent((xboxkrnl::PKEVENT)pFeedback->Header.IoCompletedEvent, NULL, FALSE); } - } + } else { pFeedback->Header.dwStatus = ERROR_DEVICE_NOT_CONNECTED; } diff --git a/src/core/hle/XAPI/Xapi.h b/src/core/hle/XAPI/Xapi.h index 44abb2dd6..feb975763 100644 --- a/src/core/hle/XAPI/Xapi.h +++ b/src/core/hle/XAPI/Xapi.h @@ -24,8 +24,8 @@ // ****************************************************************** #ifndef XAPI_H #define XAPI_H - -namespace XTL { + +namespace XTL { // ****************************************************************** // * calling conventions @@ -842,7 +842,7 @@ DWORD WINAPI EMUPATCH(XCalculateSignatureEnd) ); //*/ // +s - -} // end of namespace XTL + +} // end of namespace XTL #endif diff --git a/src/core/hle/XGRAPHIC/XGraphic.cpp b/src/core/hle/XGRAPHIC/XGraphic.cpp index bc93a099a..21d5ef931 100644 --- a/src/core/hle/XGRAPHIC/XGraphic.cpp +++ b/src/core/hle/XGRAPHIC/XGraphic.cpp @@ -33,7 +33,7 @@ #include "core\kernel\support\Emu.h" #include "common\Logging.h" #include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup() -#include "core\hle\XAPI\Xapi.h" // For EMUPATCH +#include "core\hle\XAPI\Xapi.h" // For EMUPATCH #include "core\hle\D3D8\XbD3D8Logging.h" // for log rendering of X_D3DFORMAT, etc. #include "core\hle\XGRAPHIC\XGraphic.h" diff --git a/src/core/hle/XGRAPHIC/XGraphic.h b/src/core/hle/XGRAPHIC/XGraphic.h index 85fd01bc6..1f37fd2e3 100644 --- a/src/core/hle/XGRAPHIC/XGraphic.h +++ b/src/core/hle/XGRAPHIC/XGraphic.h @@ -24,9 +24,9 @@ // ****************************************************************** #ifndef XGRAPHIC_H #define XGRAPHIC_H - + #include "core\hle\D3D8\XbD3D8Types.h" - + namespace XTL { typedef struct _XGPOINT3D @@ -114,7 +114,7 @@ HRESULT WINAPI EMUPATCH(XFONT_OpenBitmapFontFromMemory) unsigned uFontDataSize, void **ppFont ); - -} // end of namespace XTL + +} // end of namespace XTL #endif diff --git a/src/core/hle/XONLINE/XOnline.h b/src/core/hle/XONLINE/XOnline.h index 632dade71..ef3ada30c 100644 --- a/src/core/hle/XONLINE/XOnline.h +++ b/src/core/hle/XONLINE/XOnline.h @@ -23,9 +23,9 @@ // * // ****************************************************************** #if 0 // XOnline.h isn't used, but below is still useful documentation. - -#include "core\kernel\support\Emu.h" -#include "core\hle\XAPI\Xapi.h" // For EMUPATCH + +#include "core\kernel\support\Emu.h" +#include "core\hle\XAPI\Xapi.h" // For EMUPATCH namespace XTL { diff --git a/src/core/kernel/exports/EmuKrnl.cpp b/src/core/kernel/exports/EmuKrnl.cpp index bd41bee0b..9a3b7a93b 100644 --- a/src/core/kernel/exports/EmuKrnl.cpp +++ b/src/core/kernel/exports/EmuKrnl.cpp @@ -26,7 +26,7 @@ // ****************************************************************** #define LOG_PREFIX CXBXR_MODULE::KRNL - + #include #include @@ -173,8 +173,8 @@ void CallSoftwareInterrupt(const xboxkrnl::KIRQL SoftwareIrql) HalSystemInterrupts[SoftwareIrql - 4].Trigger(EmuInterruptList[SoftwareIrql - 4]); } break; - } - + } + HalInterruptRequestRegister ^= (1 << SoftwareIrql); } diff --git a/src/core/kernel/exports/EmuKrnlAv.cpp b/src/core/kernel/exports/EmuKrnlAv.cpp index c363555e6..c14ef5814 100644 --- a/src/core/kernel/exports/EmuKrnlAv.cpp +++ b/src/core/kernel/exports/EmuKrnlAv.cpp @@ -1,416 +1,416 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2016 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::AV - - -#include // For AvGetSavedDataAddress, etc. -#include "Logging.h" // For LOG_FUNC() -#include "EmuKrnlLogging.h" - -// prevent name collisions -namespace NtDll -{ -#include "core\kernel\support\EmuNtDll.h" -}; - -#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" -#include "devices\x86\EmuX86.h" - -#include "EmuKrnlAvModes.h" -#include "devices\Xbox.h" // For g_NV2A -#include "devices\video\nv2a_int.h" -#include "devices\video\nv2a.h" // For NV2ABlockInfo, EmuNV2A_Block() - -#ifndef VOID -#define VOID void -#endif - -// HW Register helper functions -xboxkrnl::UCHAR REG_RD08(VOID* Ptr, xboxkrnl::ULONG Addr) -{ - return EmuX86_Read((xbaddr)Ptr + Addr, sizeof(uint8_t)); -} - -VOID REG_WR08(VOID* Ptr, xboxkrnl::ULONG Addr, xboxkrnl::UCHAR Val) -{ - EmuX86_Write((xbaddr)Ptr + Addr, Val, sizeof(uint8_t)); -} - -xboxkrnl::ULONG REG_RD32(VOID* Ptr, xboxkrnl::ULONG Addr) -{ - return EmuX86_Read((xbaddr)Ptr + Addr, sizeof(uint32_t)); -} - -VOID REG_WR32(VOID* Ptr, xboxkrnl::ULONG Addr, xboxkrnl::ULONG Val) -{ - EmuX86_Write((xbaddr)Ptr + Addr, Val, sizeof(uint32_t)); -} - -VOID CRTC_WR(VOID* Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) -{ - static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMCIO_CRX__COLOR); - - g_NV2A->BlockWrite(block, NV_PRMCIO_CRX__COLOR, i, sizeof(uint8_t)); - g_NV2A->BlockWrite(block, NV_PRMCIO_CR__COLOR, d, sizeof(uint8_t)); -} - -VOID SRX_WR(VOID *Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) -{ - static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMVIO_SRX); - - g_NV2A->BlockWrite(block, NV_PRMVIO_SRX, i, sizeof(uint8_t)); - g_NV2A->BlockWrite(block, NV_PRMVIO_SR, d, sizeof(uint8_t)); -} - -VOID GRX_WR(VOID *Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) -{ - static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMVIO_GRX); - - g_NV2A->BlockWrite(block, NV_PRMVIO_GRX, i, sizeof(uint8_t)); - g_NV2A->BlockWrite(block, NV_PRMVIO_GX, d, sizeof(uint8_t)); -} - -VOID ARX_WR(VOID *Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) -{ - static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMCIO_ARX); - - g_NV2A->BlockWrite(block, NV_PRMCIO_ARX, i, sizeof(uint8_t)); - g_NV2A->BlockWrite(block, NV_PRMCIO_ARX, d, sizeof(uint8_t)); -} - - -// Global Variable(s) -PVOID g_pPersistedData = NULL; -ULONG AvpCurrentMode = 0; - -ULONG AvSMCVideoModeToAVPack(ULONG VideoMode) -{ - switch (VideoMode) - { - case 0x0: return AV_PACK_SCART; - case 0x1: return AV_PACK_HDTV; - case 0x2: return AV_PACK_VGA; - case 0x3: return AV_PACK_RFU; - case 0x4: return AV_PACK_SVIDEO; - case 0x6: return AV_PACK_STANDARD; - } - - return AV_PACK_NONE; -} - - -ULONG AvQueryAvCapabilities() -{ - ULONG avpack = AvSMCVideoModeToAVPack(xboxkrnl::HalBootSMCVideoMode); - ULONG type; - ULONG resultSize; - - // First, read the factory AV settings - ULONG avRegion; - NTSTATUS result = xboxkrnl::ExQueryNonVolatileSetting( - xboxkrnl::XC_FACTORY_AV_REGION, - &type, - &avRegion, - sizeof(ULONG), - &resultSize); - - // If this failed, default to AV_STANDARD_NTSC_M | AV_FLAGS_60Hz - if (result != STATUS_SUCCESS || resultSize != sizeof(ULONG)) { - avRegion = AV_STANDARD_NTSC_M | AV_FLAGS_60Hz; - } - - // Read the user-configurable (via the dashboard) settings - ULONG userSettings; - result = xboxkrnl::ExQueryNonVolatileSetting( - xboxkrnl::XC_VIDEO, - &type, - &userSettings, - sizeof(ULONG), - &resultSize); - - // If this failed, default to no user-options set - if (result != STATUS_SUCCESS || resultSize != sizeof(ULONG)) { - userSettings = 0; - } - - return avpack | (avRegion & (AV_STANDARD_MASK | AV_REFRESH_MASK)) | (userSettings & ~(AV_STANDARD_MASK | AV_PACK_MASK)); -} - -xboxkrnl::PVOID xboxkrnl::AvSavedDataAddress = xbnullptr; - -// ****************************************************************** -// * 0x0001 - AvGetSavedDataAddress() -// ****************************************************************** -XBSYSAPI EXPORTNUM(1) xboxkrnl::PVOID NTAPI xboxkrnl::AvGetSavedDataAddress(void) -{ - LOG_FUNC(); - - RETURN(AvSavedDataAddress); -} - -// ****************************************************************** -// * 0x0002 - AvSendTVEncoderOption() -// ****************************************************************** -XBSYSAPI EXPORTNUM(2) VOID NTAPI xboxkrnl::AvSendTVEncoderOption -( - IN PVOID RegisterBase, - IN ULONG Option, - IN ULONG Param, - OUT ULONG *Result -) -{ - LOG_FUNC_BEGIN; - LOG_FUNC_ARG(RegisterBase) - LOG_FUNC_ARG(Option) - LOG_FUNC_ARG(Param) - LOG_FUNC_ARG_OUT(Result) - LOG_FUNC_END; - - //if (RegisterBase == NULL) - // RegisterBase = (void *)NV20_REG_BASE_KERNEL; - - switch (Option) { - case AV_OPTION_MACROVISION_MODE: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_ENABLE_CC: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_DISABLE_CC: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_SEND_CC_DATA: - LOG_UNIMPLEMENTED(); - break; - case AV_QUERY_CC_STATUS: - LOG_UNIMPLEMENTED(); - break; - case AV_QUERY_AV_CAPABILITIES: - *Result = AvQueryAvCapabilities(); - break; - case AV_OPTION_BLANK_SCREEN: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_MACROVISION_COMMIT: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_FLICKER_FILTER: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_ZERO_MODE: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_QUERY_MODE: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_ENABLE_LUMA_FILTER: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_GUESS_FIELD: - LOG_UNIMPLEMENTED(); - break; - case AV_QUERY_ENCODER_TYPE: - LOG_UNIMPLEMENTED(); - break; - case AV_QUERY_MODE_TABLE_VERSION: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_CGMS: - LOG_UNIMPLEMENTED(); - break; - case AV_OPTION_WIDESCREEN: - LOG_UNIMPLEMENTED(); - break; - default: - // do nothing - break; - } -} - -// ****************************************************************** -// * 0x0003 - AvSetDisplayMode() -// ****************************************************************** -XBSYSAPI EXPORTNUM(3) xboxkrnl::ULONG NTAPI xboxkrnl::AvSetDisplayMode -( - IN PVOID RegisterBase, - IN ULONG Step, - IN ULONG Mode, - IN ULONG Format, - IN ULONG Pitch, - IN ULONG FrameBuffer -) -{ - LOG_FUNC_BEGIN; - LOG_FUNC_ARG(RegisterBase) - LOG_FUNC_ARG(Step) - LOG_FUNC_ARG(Mode) - LOG_FUNC_ARG(Format) - LOG_FUNC_ARG(Pitch) - LOG_FUNC_ARG(FrameBuffer) - LOG_FUNC_END; - - if (Mode == AV_MODE_OFF) { - Mode = AV_MODE_640x480_TO_NTSC_M_YC | AV_MODE_FLAGS_DACA_DISABLE | AV_MODE_FLAGS_DACB_DISABLE - | AV_MODE_FLAGS_DACC_DISABLE | AV_MODE_FLAGS_DACD_DISABLE; - } - - ULONG OutputMode = Mode & AV_MODE_OUT_MASK; - ULONG iRegister = (Mode & 0x00FF0000) >> 16; - ULONG iCRTC = (Mode & 0x0000FF00) >> 8; - ULONG iTV = (Mode & 0x0000007F); - UCHAR DACs = (UCHAR)((Mode & 0x0F000000) >> 24); - - ULONG GeneralControl = 0; - UCHAR CR28Depth = 0; - - switch (Format) - { - case XTL::X_D3DFMT_LIN_A1R5G5B5: - case XTL::X_D3DFMT_LIN_X1R5G5B5: - GeneralControl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; /*=0x00100030*/ - CR28Depth = 2; - break; - case XTL::X_D3DFMT_LIN_R5G6B5: - GeneralControl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; /*=0x00101030*/ - CR28Depth = 2; - break; - case XTL::X_D3DFMT_LIN_A8R8G8B8: - case XTL::X_D3DFMT_LIN_X8R8G8B8: - GeneralControl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; /*=0x00100030*/ - CR28Depth = 3; - break; - } - - Pitch /= 8; - - if (AvpCurrentMode == Mode) { - REG_WR32(RegisterBase, NV_PRAMDAC_GENERAL_CONTROL, GeneralControl); - CRTC_WR(RegisterBase, NV_CIO_SR_LOCK_INDEX /*=0x1c*/, NV_CIO_SR_UNLOCK_RW_VALUE); /* crtc lock */ - CRTC_WR(RegisterBase, NV_CIO_CR_OFFSET_INDEX /*=0x13*/, (UCHAR)(Pitch & 0xFF)); /* sets screen pitch */ - CRTC_WR(RegisterBase, NV_CIO_CRE_RPC0_INDEX /*=0x19*/, (UCHAR)((Pitch & 0x700) >> 3)); /* repaint control 0 */ - CRTC_WR(RegisterBase, NV_CIO_CRE_PIXEL_INDEX /*=0x28*/, 0x80 | CR28Depth); - REG_WR32(RegisterBase, NV_PCRTC_START, FrameBuffer); - - AvSendTVEncoderOption(RegisterBase, AV_OPTION_FLICKER_FILTER, 5, NULL); - AvSendTVEncoderOption(RegisterBase, AV_OPTION_ENABLE_LUMA_FILTER, FALSE, NULL); - - RETURN(STATUS_SUCCESS); - } - - CRTC_WR(RegisterBase, NV_CIO_CRE_PIXEL_INDEX /*=0x28*/, 0x80 | CR28Depth); - - // TODO: Lots of setup/TV encoder configuration - // Ignored for now since we don't emulate that stuff yet... - - LOG_INCOMPLETE(); - - REG_WR32(RegisterBase, NV_PRAMDAC_GENERAL_CONTROL, GeneralControl); - - const ULONG* pLong = AvpRegisters[iRegister]; - const ULONG* pLongMax = pLong + sizeof(AvpRegisters[0]) / sizeof(ULONG); - - for (long i = 0; pLong < pLongMax; pLong++, i++) { - REG_WR32(RegisterBase, AvpRegisters[0][i], *pLong); - } - - if (Mode & AV_MODE_FLAGS_SCART) { - REG_WR32(RegisterBase, 0x00680630, 0); // NV_RAMDAC + 0x0630 - REG_WR32(RegisterBase, 0x006808C4, 0); // NV_RAMDAC + 0x08C4 - REG_WR32(RegisterBase, 0x0068084C, 0); // NV_RAMDAC + 0x084C - } - - const UCHAR* pByte = AvpSRXRegisters; - const UCHAR* pByteMax = pByte + sizeof(AvpSRXRegisters); - - for (long i = 0; pByte < pByteMax; pByte++, i++) { - SRX_WR(RegisterBase, (UCHAR)i, *pByte); - } - - pByte = AvpGRXRegisters; - pByteMax = pByte + sizeof(AvpGRXRegisters); - - for (long i = 0; pByte < pByteMax; pByte++, i++) { - GRX_WR(RegisterBase, (UCHAR)i, *pByte); - } - - REG_RD08(RegisterBase, NV_PRMCIO_INP0__COLOR); - - pByte = AvpARXRegisters; - pByteMax = pByte + sizeof(AvpARXRegisters); - - for (long i = 0; pByte < pByteMax; pByte++, i++) { - ARX_WR(RegisterBase, (UCHAR)i, *pByte); - } - - REG_WR08(RegisterBase, NV_PRMCIO_ARX, 0x20); - - CRTC_WR(RegisterBase, NV_CIO_CR_VRE_INDEX /*=0x11*/, 0x00); - pByte = AvpCRTCRegisters[iCRTC]; - pByteMax = pByte + sizeof(AvpCRTCRegisters[0]); - - for (long i = 0; pByte < pByteMax; pByte++, i++) { - UCHAR Register = AvpCRTCRegisters[0][i]; - UCHAR Data = *pByte; - - switch (Register) { - case NV_CIO_CR_OFFSET_INDEX /*=0x13*/: - Data = (UCHAR)(Pitch & 0xFF); - break; - case NV_CIO_CRE_RPC0_INDEX /*=0x19*/: - Data |= (UCHAR)((Pitch & 0x700) >> 3); - break; - case NV_CIO_CRE_LSR_INDEX /*=0x25*/: - Data |= (UCHAR)((Pitch & 0x800) >> 6); - break; - } - - CRTC_WR(RegisterBase, Register, Data); - } - - // TODO: More TV Encoder stuff... - - REG_WR32(RegisterBase, NV_PCRTC_START, FrameBuffer); - AvpCurrentMode = Mode; - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x0004 - AvSetSavedDataAddress() -// ****************************************************************** -XBSYSAPI EXPORTNUM(4) VOID NTAPI xboxkrnl::AvSetSavedDataAddress -( - IN PVOID Address -) -{ - LOG_FUNC_ONE_ARG(Address); - - AvSavedDataAddress = Address; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2016 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::AV + + +#include // For AvGetSavedDataAddress, etc. +#include "Logging.h" // For LOG_FUNC() +#include "EmuKrnlLogging.h" + +// prevent name collisions +namespace NtDll +{ +#include "core\kernel\support\EmuNtDll.h" +}; + +#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" +#include "devices\x86\EmuX86.h" + +#include "EmuKrnlAvModes.h" +#include "devices\Xbox.h" // For g_NV2A +#include "devices\video\nv2a_int.h" +#include "devices\video\nv2a.h" // For NV2ABlockInfo, EmuNV2A_Block() + +#ifndef VOID +#define VOID void +#endif + +// HW Register helper functions +xboxkrnl::UCHAR REG_RD08(VOID* Ptr, xboxkrnl::ULONG Addr) +{ + return EmuX86_Read((xbaddr)Ptr + Addr, sizeof(uint8_t)); +} + +VOID REG_WR08(VOID* Ptr, xboxkrnl::ULONG Addr, xboxkrnl::UCHAR Val) +{ + EmuX86_Write((xbaddr)Ptr + Addr, Val, sizeof(uint8_t)); +} + +xboxkrnl::ULONG REG_RD32(VOID* Ptr, xboxkrnl::ULONG Addr) +{ + return EmuX86_Read((xbaddr)Ptr + Addr, sizeof(uint32_t)); +} + +VOID REG_WR32(VOID* Ptr, xboxkrnl::ULONG Addr, xboxkrnl::ULONG Val) +{ + EmuX86_Write((xbaddr)Ptr + Addr, Val, sizeof(uint32_t)); +} + +VOID CRTC_WR(VOID* Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) +{ + static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMCIO_CRX__COLOR); + + g_NV2A->BlockWrite(block, NV_PRMCIO_CRX__COLOR, i, sizeof(uint8_t)); + g_NV2A->BlockWrite(block, NV_PRMCIO_CR__COLOR, d, sizeof(uint8_t)); +} + +VOID SRX_WR(VOID *Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) +{ + static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMVIO_SRX); + + g_NV2A->BlockWrite(block, NV_PRMVIO_SRX, i, sizeof(uint8_t)); + g_NV2A->BlockWrite(block, NV_PRMVIO_SR, d, sizeof(uint8_t)); +} + +VOID GRX_WR(VOID *Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) +{ + static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMVIO_GRX); + + g_NV2A->BlockWrite(block, NV_PRMVIO_GRX, i, sizeof(uint8_t)); + g_NV2A->BlockWrite(block, NV_PRMVIO_GX, d, sizeof(uint8_t)); +} + +VOID ARX_WR(VOID *Ptr, xboxkrnl::UCHAR i, xboxkrnl::UCHAR d) +{ + static const NV2ABlockInfo* block = EmuNV2A_Block(NV_PRMCIO_ARX); + + g_NV2A->BlockWrite(block, NV_PRMCIO_ARX, i, sizeof(uint8_t)); + g_NV2A->BlockWrite(block, NV_PRMCIO_ARX, d, sizeof(uint8_t)); +} + + +// Global Variable(s) +PVOID g_pPersistedData = NULL; +ULONG AvpCurrentMode = 0; + +ULONG AvSMCVideoModeToAVPack(ULONG VideoMode) +{ + switch (VideoMode) + { + case 0x0: return AV_PACK_SCART; + case 0x1: return AV_PACK_HDTV; + case 0x2: return AV_PACK_VGA; + case 0x3: return AV_PACK_RFU; + case 0x4: return AV_PACK_SVIDEO; + case 0x6: return AV_PACK_STANDARD; + } + + return AV_PACK_NONE; +} + + +ULONG AvQueryAvCapabilities() +{ + ULONG avpack = AvSMCVideoModeToAVPack(xboxkrnl::HalBootSMCVideoMode); + ULONG type; + ULONG resultSize; + + // First, read the factory AV settings + ULONG avRegion; + NTSTATUS result = xboxkrnl::ExQueryNonVolatileSetting( + xboxkrnl::XC_FACTORY_AV_REGION, + &type, + &avRegion, + sizeof(ULONG), + &resultSize); + + // If this failed, default to AV_STANDARD_NTSC_M | AV_FLAGS_60Hz + if (result != STATUS_SUCCESS || resultSize != sizeof(ULONG)) { + avRegion = AV_STANDARD_NTSC_M | AV_FLAGS_60Hz; + } + + // Read the user-configurable (via the dashboard) settings + ULONG userSettings; + result = xboxkrnl::ExQueryNonVolatileSetting( + xboxkrnl::XC_VIDEO, + &type, + &userSettings, + sizeof(ULONG), + &resultSize); + + // If this failed, default to no user-options set + if (result != STATUS_SUCCESS || resultSize != sizeof(ULONG)) { + userSettings = 0; + } + + return avpack | (avRegion & (AV_STANDARD_MASK | AV_REFRESH_MASK)) | (userSettings & ~(AV_STANDARD_MASK | AV_PACK_MASK)); +} + +xboxkrnl::PVOID xboxkrnl::AvSavedDataAddress = xbnullptr; + +// ****************************************************************** +// * 0x0001 - AvGetSavedDataAddress() +// ****************************************************************** +XBSYSAPI EXPORTNUM(1) xboxkrnl::PVOID NTAPI xboxkrnl::AvGetSavedDataAddress(void) +{ + LOG_FUNC(); + + RETURN(AvSavedDataAddress); +} + +// ****************************************************************** +// * 0x0002 - AvSendTVEncoderOption() +// ****************************************************************** +XBSYSAPI EXPORTNUM(2) VOID NTAPI xboxkrnl::AvSendTVEncoderOption +( + IN PVOID RegisterBase, + IN ULONG Option, + IN ULONG Param, + OUT ULONG *Result +) +{ + LOG_FUNC_BEGIN; + LOG_FUNC_ARG(RegisterBase) + LOG_FUNC_ARG(Option) + LOG_FUNC_ARG(Param) + LOG_FUNC_ARG_OUT(Result) + LOG_FUNC_END; + + //if (RegisterBase == NULL) + // RegisterBase = (void *)NV20_REG_BASE_KERNEL; + + switch (Option) { + case AV_OPTION_MACROVISION_MODE: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_ENABLE_CC: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_DISABLE_CC: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_SEND_CC_DATA: + LOG_UNIMPLEMENTED(); + break; + case AV_QUERY_CC_STATUS: + LOG_UNIMPLEMENTED(); + break; + case AV_QUERY_AV_CAPABILITIES: + *Result = AvQueryAvCapabilities(); + break; + case AV_OPTION_BLANK_SCREEN: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_MACROVISION_COMMIT: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_FLICKER_FILTER: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_ZERO_MODE: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_QUERY_MODE: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_ENABLE_LUMA_FILTER: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_GUESS_FIELD: + LOG_UNIMPLEMENTED(); + break; + case AV_QUERY_ENCODER_TYPE: + LOG_UNIMPLEMENTED(); + break; + case AV_QUERY_MODE_TABLE_VERSION: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_CGMS: + LOG_UNIMPLEMENTED(); + break; + case AV_OPTION_WIDESCREEN: + LOG_UNIMPLEMENTED(); + break; + default: + // do nothing + break; + } +} + +// ****************************************************************** +// * 0x0003 - AvSetDisplayMode() +// ****************************************************************** +XBSYSAPI EXPORTNUM(3) xboxkrnl::ULONG NTAPI xboxkrnl::AvSetDisplayMode +( + IN PVOID RegisterBase, + IN ULONG Step, + IN ULONG Mode, + IN ULONG Format, + IN ULONG Pitch, + IN ULONG FrameBuffer +) +{ + LOG_FUNC_BEGIN; + LOG_FUNC_ARG(RegisterBase) + LOG_FUNC_ARG(Step) + LOG_FUNC_ARG(Mode) + LOG_FUNC_ARG(Format) + LOG_FUNC_ARG(Pitch) + LOG_FUNC_ARG(FrameBuffer) + LOG_FUNC_END; + + if (Mode == AV_MODE_OFF) { + Mode = AV_MODE_640x480_TO_NTSC_M_YC | AV_MODE_FLAGS_DACA_DISABLE | AV_MODE_FLAGS_DACB_DISABLE + | AV_MODE_FLAGS_DACC_DISABLE | AV_MODE_FLAGS_DACD_DISABLE; + } + + ULONG OutputMode = Mode & AV_MODE_OUT_MASK; + ULONG iRegister = (Mode & 0x00FF0000) >> 16; + ULONG iCRTC = (Mode & 0x0000FF00) >> 8; + ULONG iTV = (Mode & 0x0000007F); + UCHAR DACs = (UCHAR)((Mode & 0x0F000000) >> 24); + + ULONG GeneralControl = 0; + UCHAR CR28Depth = 0; + + switch (Format) + { + case XTL::X_D3DFMT_LIN_A1R5G5B5: + case XTL::X_D3DFMT_LIN_X1R5G5B5: + GeneralControl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; /*=0x00100030*/ + CR28Depth = 2; + break; + case XTL::X_D3DFMT_LIN_R5G6B5: + GeneralControl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; /*=0x00101030*/ + CR28Depth = 2; + break; + case XTL::X_D3DFMT_LIN_A8R8G8B8: + case XTL::X_D3DFMT_LIN_X8R8G8B8: + GeneralControl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; /*=0x00100030*/ + CR28Depth = 3; + break; + } + + Pitch /= 8; + + if (AvpCurrentMode == Mode) { + REG_WR32(RegisterBase, NV_PRAMDAC_GENERAL_CONTROL, GeneralControl); + CRTC_WR(RegisterBase, NV_CIO_SR_LOCK_INDEX /*=0x1c*/, NV_CIO_SR_UNLOCK_RW_VALUE); /* crtc lock */ + CRTC_WR(RegisterBase, NV_CIO_CR_OFFSET_INDEX /*=0x13*/, (UCHAR)(Pitch & 0xFF)); /* sets screen pitch */ + CRTC_WR(RegisterBase, NV_CIO_CRE_RPC0_INDEX /*=0x19*/, (UCHAR)((Pitch & 0x700) >> 3)); /* repaint control 0 */ + CRTC_WR(RegisterBase, NV_CIO_CRE_PIXEL_INDEX /*=0x28*/, 0x80 | CR28Depth); + REG_WR32(RegisterBase, NV_PCRTC_START, FrameBuffer); + + AvSendTVEncoderOption(RegisterBase, AV_OPTION_FLICKER_FILTER, 5, NULL); + AvSendTVEncoderOption(RegisterBase, AV_OPTION_ENABLE_LUMA_FILTER, FALSE, NULL); + + RETURN(STATUS_SUCCESS); + } + + CRTC_WR(RegisterBase, NV_CIO_CRE_PIXEL_INDEX /*=0x28*/, 0x80 | CR28Depth); + + // TODO: Lots of setup/TV encoder configuration + // Ignored for now since we don't emulate that stuff yet... + + LOG_INCOMPLETE(); + + REG_WR32(RegisterBase, NV_PRAMDAC_GENERAL_CONTROL, GeneralControl); + + const ULONG* pLong = AvpRegisters[iRegister]; + const ULONG* pLongMax = pLong + sizeof(AvpRegisters[0]) / sizeof(ULONG); + + for (long i = 0; pLong < pLongMax; pLong++, i++) { + REG_WR32(RegisterBase, AvpRegisters[0][i], *pLong); + } + + if (Mode & AV_MODE_FLAGS_SCART) { + REG_WR32(RegisterBase, 0x00680630, 0); // NV_RAMDAC + 0x0630 + REG_WR32(RegisterBase, 0x006808C4, 0); // NV_RAMDAC + 0x08C4 + REG_WR32(RegisterBase, 0x0068084C, 0); // NV_RAMDAC + 0x084C + } + + const UCHAR* pByte = AvpSRXRegisters; + const UCHAR* pByteMax = pByte + sizeof(AvpSRXRegisters); + + for (long i = 0; pByte < pByteMax; pByte++, i++) { + SRX_WR(RegisterBase, (UCHAR)i, *pByte); + } + + pByte = AvpGRXRegisters; + pByteMax = pByte + sizeof(AvpGRXRegisters); + + for (long i = 0; pByte < pByteMax; pByte++, i++) { + GRX_WR(RegisterBase, (UCHAR)i, *pByte); + } + + REG_RD08(RegisterBase, NV_PRMCIO_INP0__COLOR); + + pByte = AvpARXRegisters; + pByteMax = pByte + sizeof(AvpARXRegisters); + + for (long i = 0; pByte < pByteMax; pByte++, i++) { + ARX_WR(RegisterBase, (UCHAR)i, *pByte); + } + + REG_WR08(RegisterBase, NV_PRMCIO_ARX, 0x20); + + CRTC_WR(RegisterBase, NV_CIO_CR_VRE_INDEX /*=0x11*/, 0x00); + pByte = AvpCRTCRegisters[iCRTC]; + pByteMax = pByte + sizeof(AvpCRTCRegisters[0]); + + for (long i = 0; pByte < pByteMax; pByte++, i++) { + UCHAR Register = AvpCRTCRegisters[0][i]; + UCHAR Data = *pByte; + + switch (Register) { + case NV_CIO_CR_OFFSET_INDEX /*=0x13*/: + Data = (UCHAR)(Pitch & 0xFF); + break; + case NV_CIO_CRE_RPC0_INDEX /*=0x19*/: + Data |= (UCHAR)((Pitch & 0x700) >> 3); + break; + case NV_CIO_CRE_LSR_INDEX /*=0x25*/: + Data |= (UCHAR)((Pitch & 0x800) >> 6); + break; + } + + CRTC_WR(RegisterBase, Register, Data); + } + + // TODO: More TV Encoder stuff... + + REG_WR32(RegisterBase, NV_PCRTC_START, FrameBuffer); + AvpCurrentMode = Mode; + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x0004 - AvSetSavedDataAddress() +// ****************************************************************** +XBSYSAPI EXPORTNUM(4) VOID NTAPI xboxkrnl::AvSetSavedDataAddress +( + IN PVOID Address +) +{ + LOG_FUNC_ONE_ARG(Address); + + AvSavedDataAddress = Address; +} diff --git a/src/core/kernel/exports/EmuKrnlAvModes.h b/src/core/kernel/exports/EmuKrnlAvModes.h index 8412fb3a0..ce4a87374 100644 --- a/src/core/kernel/exports/EmuKrnlAvModes.h +++ b/src/core/kernel/exports/EmuKrnlAvModes.h @@ -1,420 +1,420 @@ -#pragma once - -// Mode enum values where: -// -// 0xC0000000 - output mode mask: -// -// 0x00000000 - 480 line SDTV -// 0x40000000 - 525 line SDTV -// 0x80000000 - HDTV -// 0xC0000000 - VGA -// -// 0x10000000 - enable WSS (_16x9) -// 0x20000000 - use SCART output (_RGB) -// -// 0x01000000 - disable DAC A -// 0x02000000 - disable DAC B -// 0x04000000 - disable DAC C -// 0x08000000 - disable DAC D -// -// 0x00FF0000 - register table index -// 0x0000FF00 - CRTC table index -// 0x000000FF - TV table index, based on output mode mask above - -#define AV_MODE_TABLE_VERSION 0 - -#define AV_MODE_OFF 0x00000000 -#define AV_MODE_640x480_TO_NTSC_M_YC 0x04010101 -#define AV_MODE_640x480_TO_NTSC_M_YC_16x9 0x14010101 -#define AV_MODE_720x480_TO_NTSC_M_YC 0x04020202 -#define AV_MODE_720x480_TO_NTSC_M_YC_16x9 0x14020202 -#define AV_MODE_640x480_TO_NTSC_M_RGB 0x20010101 -#define AV_MODE_640x480_TO_NTSC_M_RGB_16x9 0x30010101 -#define AV_MODE_720x480_TO_NTSC_M_RGB 0x20020202 -#define AV_MODE_720x480_TO_NTSC_M_RGB_16x9 0x30020202 -#define AV_MODE_640x480_TO_NTSC_J_YC 0x04010103 -#define AV_MODE_640x480_TO_NTSC_J_YC_16x9 0x14010103 -#define AV_MODE_720x480_TO_NTSC_J_YC 0x04020204 -#define AV_MODE_720x480_TO_NTSC_J_YC_16x9 0x14020204 -#define AV_MODE_640x480_TO_NTSC_J_RGB 0x20010103 -#define AV_MODE_640x480_TO_NTSC_J_RGB_16x9 0x30010103 -#define AV_MODE_720x480_TO_NTSC_J_RGB 0x20020204 -#define AV_MODE_720x480_TO_NTSC_J_RGB_16x9 0x30020204 -#define AV_MODE_640x480_TO_PAL_M_YC 0x04010105 -#define AV_MODE_640x480_TO_PAL_M_YC_16x9 0x14010105 -#define AV_MODE_720x480_TO_PAL_M_YC 0x04020206 -#define AV_MODE_720x480_TO_PAL_M_YC_16x9 0x14020206 -#define AV_MODE_640x480_TO_PAL_M_RGB 0x20010105 -#define AV_MODE_640x480_TO_PAL_M_RGB_16x9 0x30010105 -#define AV_MODE_720x480_TO_PAL_M_RGB 0x20020206 -#define AV_MODE_720x480_TO_PAL_M_RGB_16x9 0x30020206 -#define AV_MODE_640x480_TO_PAL_I_YC 0x44030307 -#define AV_MODE_640x480_TO_PAL_I_YC_16x9 0x54030307 -#define AV_MODE_720x480_TO_PAL_I_YC 0x44040408 -#define AV_MODE_720x480_TO_PAL_I_YC_16x9 0x54040408 -#define AV_MODE_640x576_TO_PAL_I_YC 0x44050509 -#define AV_MODE_640x576_TO_PAL_I_YC_16x9 0x54050509 -#define AV_MODE_720x576_TO_PAL_I_YC 0x4406060A -#define AV_MODE_720x576_TO_PAL_I_YC_16x9 0x5406060A -#define AV_MODE_640x480_TO_PAL_I_RGB 0x60030307 -#define AV_MODE_640x480_TO_PAL_I_RGB_16x9 0x70030307 -#define AV_MODE_720x480_TO_PAL_I_RGB 0x60040408 -#define AV_MODE_720x480_TO_PAL_I_RGB_16x9 0x70040408 -#define AV_MODE_640x576_TO_PAL_I_RGB 0x60050509 -#define AV_MODE_640x576_TO_PAL_I_RGB_16x9 0x70050509 -#define AV_MODE_720x576_TO_PAL_I_RGB 0x6006060A -#define AV_MODE_720x576_TO_PAL_I_RGB_16x9 0x7006060A -#define AV_MODE_640x480_TO_PAL_60_YC 0x0401010B -#define AV_MODE_640x480_TO_PAL_60_YC_16x9 0x1401010B -#define AV_MODE_720x480_TO_PAL_60_YC 0x0402020C -#define AV_MODE_720x480_TO_PAL_60_YC_16x9 0x1402020C -#define AV_MODE_640x480_TO_PAL_60_RGB 0x2001010B -#define AV_MODE_640x480_TO_PAL_60_RGB_16x9 0x3001010B -#define AV_MODE_720x480_TO_PAL_60_RGB 0x2002020C -#define AV_MODE_720x480_TO_PAL_60_RGB_16x9 0x3002020C -#define AV_MODE_640x480_TO_NTSC_YPrPb 0x0801010D -#define AV_MODE_640x480_TO_NTSC_YPrPb_16x9 0x1801010D -#define AV_MODE_720x480_TO_NTSC_YPrPb 0x0802020E -#define AV_MODE_720x480_TO_NTSC_YPrPb_16x9 0x1802020E -#define AV_MODE_640x480_FPAR_TO_NTSC_M_YC 0x040F0D0F -#define AV_MODE_640x480_FPAR_TO_NTSC_M_YC_16x9 0x140F0D0F -#define AV_MODE_640x480_FPAR_TO_NTSC_M_RGB 0x200F0D0F -#define AV_MODE_640x480_FPAR_TO_NTSC_M_RGB_16x9 0x300F0D0F -#define AV_MODE_640x480_FPAR_TO_NTSC_J_YC 0x040F0D10 -#define AV_MODE_640x480_FPAR_TO_NTSC_J_YC_16x9 0x140F0D10 -#define AV_MODE_640x480_FPAR_TO_NTSC_J_RGB 0x200F0D10 -#define AV_MODE_640x480_FPAR_TO_NTSC_J_RGB_16x9 0x300F0D10 -#define AV_MODE_640x480_FPAR_TO_PAL_60_YC 0x040F0D11 -#define AV_MODE_640x480_FPAR_TO_PAL_60_YC_16x9 0x140F0D11 -#define AV_MODE_640x480_FPAR_TO_PAL_60_RGB 0x200F0D11 -#define AV_MODE_640x480_FPAR_TO_PAL_60_RGB_16x9 0x300F0D11 -#define AV_MODE_640x480_FPAR_TO_NTSC_YPrPb 0x080F0D12 -#define AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 0x180F0D12 -#define AV_MODE_640x480_FPAR_TO_PAL_I_YC 0x44100E13 -#define AV_MODE_640x480_FPAR_TO_PAL_I_YC_16x9 0x54100E13 -#define AV_MODE_640x480_FPAR_TO_PAL_I_RGB 0x60100E13 -#define AV_MODE_640x480_FPAR_TO_PAL_I_RGB_16x9 0x70100E13 -#define AV_MODE_640x480_TO_PAL_I_YPrPb 0x48030314 -#define AV_MODE_640x480_TO_PAL_I_YPrPb_16x9 0x58030314 -#define AV_MODE_720x480_TO_PAL_I_YPrPb 0x48040415 -#define AV_MODE_720x480_TO_PAL_I_YPrPb_16x9 0x58040415 -#define AV_MODE_640x576_TO_PAL_I_YPrPb 0x48050516 -#define AV_MODE_640x576_TO_PAL_I_YPrPb_16x9 0x58050516 -#define AV_MODE_720x576_TO_PAL_I_YPrPb 0x48060617 -#define AV_MODE_720x576_TO_PAL_I_YPrPb_16x9 0x58060617 -#define AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb 0x48100E18 -#define AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb_16x9 0x58100E18 -#define AV_MODE_640x480_TO_PAL_60_YPrPb 0x08010119 -#define AV_MODE_640x480_TO_PAL_60_YPrPb_16x9 0x18010119 -#define AV_MODE_720x480_TO_PAL_60_YPrPb 0x0802021A -#define AV_MODE_720x480_TO_PAL_60_YPrPb_16x9 0x1802021A -#define AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb 0x080F0D1B -#define AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb_16x9 0x180F0D1B -#define AV_MODE_640x576_FPAR_TO_PAL_I_YC 0x4412101C -#define AV_MODE_640x576_FPAR_TO_PAL_I_YC_16x9 0x5412101C -#define AV_MODE_640x576_FPAR_TO_PAL_I_RGB 0x6012101C -#define AV_MODE_640x576_FPAR_TO_PAL_I_RGB_16x9 0x7012101C -#define AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb 0x4812101D -#define AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb_16x9 0x5812101D -#define AV_MODE_640x480_TO_VGA 0x88070701 -#define AV_MODE_720x480_TO_VGA 0x88080801 -#define AV_MODE_1280x720_TO_VGA 0x880B0A02 -#define AV_MODE_1920x1080_TO_VGA 0x880E0C03 -#define AV_MODE_640x480_TO_480P 0x88070701 -#define AV_MODE_720x480_TO_480P 0x88080801 -#define AV_MODE_1280x720P_TO_720P 0x880B0A02 -#define AV_MODE_1920x1080I_TO_1080I 0x880E0C03 -#define AV_MODE_640x480_FPAR_TO_480P 0x88110F01 - -#define AV_MODE_FLAGS_DACA_DISABLE 0x01000000 -#define AV_MODE_FLAGS_DACB_DISABLE 0x02000000 -#define AV_MODE_FLAGS_DACC_DISABLE 0x04000000 -#define AV_MODE_FLAGS_DACD_DISABLE 0x08000000 - -#define AV_MODE_FLAGS_WSS 0x10000000 // _16x9 -#define AV_MODE_FLAGS_SCART 0x20000000 // _RGB -#define AV_MODE_FLAGS_NTSCJ 0x00000080 - -#define AV_MODE_OUT_MASK 0xC0000000 -#define AV_MODE_OUT_480SDTV 0x00000000 -#define AV_MODE_OUT_525SDTV 0x40000000 -#define AV_MODE_OUT_HDTV 0x80000000 -#define AV_MODE_OUT_VGA 0xC0000000 - -// Register Tables -const ULONG AvpRegisters[][26] = -{ - { /* offset */ 0x00680898, 0x0068089C, 0x006808C0, 0x006808C4, 0x0068084C, 0x00680630, 0x00680800, 0x00680804, 0x00680808, 0x0068080C, 0x00680810, 0x00680814, 0x00680818, 0x00680820, 0x00680824, 0x00680828, 0x0068082C, 0x00680830, 0x00680834, 0x00680838, 0x00680848, 0x00680680, 0x00680684, 0x00680688, 0x0068068C, 0x00680690 }, /* offset */ - { /* 1 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F2, 0x000001F4, 0x00000000, 0x000001DF, 0x0000027F, 0x000003A7, 0x00000257, 0x000002F3, 0x00000333, 0x00000000, 0x0000027F, 0x10100111, 0x000C6ED0, 0x0000020D, 0x0000009B, 0x0000026C, 0x00000000 }, /* 1 */ - { /* 2 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F2, 0x000001F4, 0x00000000, 0x000001DF, 0x000002CF, 0x000003A7, 0x000002A7, 0x0000031B, 0x0000035B, 0x00000000, 0x000002CF, 0x10100111, 0x000DF05C, 0x0000020D, 0x000000AE, 0x000002B8, 0x00000000 }, /* 2 */ - { /* 3 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F4, 0x000001F6, 0x00000000, 0x000001DF, 0x0000027F, 0x0000035F, 0x00000257, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000F387C, 0x00000271, 0x000000BE, 0x000002F8, 0x00000000 }, /* 3 */ - { /* 4 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F4, 0x000001F6, 0x00000000, 0x000001DF, 0x000002CF, 0x0000035F, 0x000002AF, 0x0000030B, 0x0000034B, 0x00000000, 0x000002CF, 0x10100111, 0x0010D2A4, 0x00000271, 0x000000D2, 0x00000348, 0x00000000 }, /* 4 */ - { /* 5 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x0000023F, 0x00000270, 0x0000023F, 0x00000256, 0x00000258, 0x00000000, 0x0000023F, 0x0000027F, 0x0000035F, 0x00000257, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000F07A8, 0x00000271, 0x0000009D, 0x00000276, 0x00000000 }, /* 5 */ - { /* 6 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x0000023F, 0x00000270, 0x0000023F, 0x00000256, 0x00000258, 0x00000000, 0x0000023F, 0x000002CF, 0x0000035F, 0x000002AF, 0x0000030B, 0x0000034B, 0x00000000, 0x000002CF, 0x10100111, 0x0010E62C, 0x00000271, 0x000000B1, 0x000002C4, 0x00000000 }, /* 6 */ - { /* 7 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001E8, 0x000001EE, 0x00000000, 0x000001DF, 0x000002CF, 0x00000359, 0x0000029F, 0x000002E1, 0x00000320, 0x00000000, 0x000002CF, 0x10100011, 0x0000035A, 0x00000001, 0x000000AB, 0x000002AE, 0x00000001 }, /* 7 */ - { /* 8 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001E8, 0x000001EE, 0x00000000, 0x000001DF, 0x000002CF, 0x00000359, 0x0000029F, 0x000002E1, 0x00000320, 0x00000000, 0x000002CF, 0x10100111, 0x0000035A, 0x00000001, 0x000000AB, 0x000002AE, 0x00000001 }, /* 8 */ - { /* 9 */ 0x0AA94000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x000002CF, 0x000002ED, 0x000002CF, 0x000002D4, 0x000002D9, 0x00000000, 0x000002CF, 0x000004FF, 0x00000671, 0x000004FF, 0x00000545, 0x00000595, 0x000000A0, 0x0000045F, 0x10100011, 0x00000672, 0x00000001, 0x0000014A, 0x00000528, 0x00000001 }, /* 9 */ - { /* A */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x000002CF, 0x000002ED, 0x000002CF, 0x000002D4, 0x000002D9, 0x00000000, 0x000002CF, 0x000004FF, 0x00000671, 0x000004CF, 0x00000545, 0x00000595, 0x00000000, 0x000004FF, 0x10100011, 0x00000672, 0x00000001, 0x0000014A, 0x00000528, 0x00000001 }, /* A */ - { /* B */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x000002CF, 0x000002ED, 0x000002CF, 0x000002D4, 0x000002D9, 0x00000000, 0x000002CF, 0x000004FF, 0x00000671, 0x000004FF, 0x00000545, 0x00000595, 0x00000000, 0x000004FF, 0x10100111, 0x00000672, 0x00000001, 0x0000014A, 0x00000528, 0x00000001 }, /* B */ - { /* C */ 0x071AE000, 0x07183800, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x00000437, 0x00000464, 0x0000043C, 0x0000043C, 0x00000446, 0x00000000, 0x00000437, 0x0000077F, 0x00000897, 0x000007AA, 0x000007AB, 0x00000803, 0x000000F0, 0x0000068F, 0x10133011, 0x00000898, 0x00000001, 0x000001B8, 0x000006E0, 0x00000001 }, /* C */ - { /* D */ 0x10000000, 0x07183800, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x00000437, 0x00000464, 0x0000043C, 0x0000043C, 0x00000446, 0x00000000, 0x00000437, 0x0000077F, 0x00000897, 0x00000759, 0x000007AB, 0x00000803, 0x00000000, 0x0000077F, 0x10133011, 0x00000898, 0x00000001, 0x000001B8, 0x000006E0, 0x00000001 }, /* D */ - { /* E */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x00000437, 0x00000464, 0x0000043B, 0x0000043B, 0x00000445, 0x00000000, 0x00000437, 0x0000077F, 0x00000897, 0x000007AB, 0x000007AC, 0x00000804, 0x00000000, 0x0000077F, 0x10133111, 0x00000898, 0x00000001, 0x000001B8, 0x000006E0, 0x00000001 }, /* E */ - { /* F */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F2, 0x000001F4, 0x00000000, 0x000001DF, 0x0000027F, 0x000003A7, 0x000002A7, 0x000002F3, 0x00000333, 0x00000000, 0x0000027F, 0x10100111, 0x000DF05C, 0x0000020D, 0x000000AE, 0x000002B8, 0x00000000 }, /* F */ - { /* 10 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F4, 0x000001F6, 0x00000000, 0x000001DF, 0x0000027F, 0x0000035F, 0x000002A7, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000DF05C, 0x0000020D, 0x000000AE, 0x000002B8, 0x00000000 }, /* 10 */ - { /* 11 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001E8, 0x000001EE, 0x00000000, 0x000001DF, 0x000002CF, 0x00000359, 0x000002CF, 0x000002DF, 0x0000031E, 0x00000020, 0x000002AD, 0x10100011, 0x0000035A, 0x00000001, 0x000000AB, 0x000002AE, 0x00000001 }, /* 11 */ - { /* 12 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x0000023F, 0x00000270, 0x0000023F, 0x00000256, 0x00000258, 0x00000000, 0x0000023F, 0x0000027F, 0x0000035F, 0x00000257, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000F07A8, 0x00000271, 0x0000009D, 0x00000276, 0x00000000 }, /* 12 */ -}; - -const UCHAR AvpSRXRegisters[] = -{ - 0x03, 0x21, 0x0F, 0x00, 0x06, -}; - -const UCHAR AvpGRXRegisters[] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF, -}; - -const UCHAR AvpARXRegisters[] = -{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x01, 0x4A, 0x0F, 0x00, 0x00, -}; - -const UCHAR AvpCRTCRegisters[][34] = -{ - { /* offset */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x20, 0x25, 0x2D, 0x33, 0x39, 0x41 }, /* offset */ - { /* 1 */ 0x70, 0x4F, 0x4F, 0x94, 0x5D, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x04, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 1 */ - { /* 2 */ 0x70, 0x59, 0x59, 0x94, 0x62, 0xA4, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x04, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 2 */ - { /* 3 */ 0x67, 0x4F, 0x4F, 0x8B, 0x59, 0xBB, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x06, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 3 */ - { /* 4 */ 0x67, 0x59, 0x59, 0x8B, 0x5E, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x06, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 4 */ - { /* 5 */ 0x67, 0x4F, 0x4F, 0x8B, 0x59, 0xBB, 0x6F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x70, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 5 */ - { /* 6 */ 0x67, 0x59, 0x59, 0x8B, 0x5E, 0xBF, 0x6F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x70, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 6 */ - { /* 7 */ 0x59, 0x4F, 0x4F, 0x9D, 0x51, 0x39, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0E, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 7 */ - { /* 8 */ 0x63, 0x59, 0x59, 0x87, 0x5B, 0xA3, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0E, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 8 */ - { /* 9 */ 0x78, 0x4F, 0x4F, 0x9C, 0x57, 0xA1, 0xFC, 0x1F, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x09, 0xDF, 0x00, 0x00, 0xDF, 0xFD, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 9 */ - { /* A */ 0xC8, 0x9F, 0x9F, 0x8C, 0xA7, 0x31, 0xEC, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x09, 0xCF, 0x00, 0x00, 0xCF, 0xED, 0xE3, 0xFF, 0x00, 0x38, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* A */ - { /* B */ 0x67, 0x4F, 0x4F, 0x8B, 0x54, 0xBF, 0x03, 0x11, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0x06, 0xEF, 0x00, 0x00, 0xEF, 0x04, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0x36, 0x00 }, /* B */ - { /* C */ 0x04, 0xEF, 0xEF, 0x88, 0xF4, 0x3F, 0x2F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x02, 0x1B, 0x00, 0x00, 0x1B, 0x30, 0xE3, 0xFF, 0x00, 0x38, 0x05, 0x80, 0x00, 0x01, 0x11, 0x10, 0x00 }, /* C */ - { /* D */ 0x70, 0x4F, 0x4F, 0x94, 0x5D, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x04, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* D */ - { /* E */ 0x67, 0x4F, 0x4F, 0x8B, 0x59, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x06, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x00, 0x00, 0x11, 0xFF, 0x00 }, /* E */ - { /* F */ 0x61, 0x57, 0x57, 0x85, 0x59, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0E, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* F */ - { /* 10 */ 0x67, 0x4F, 0x4F, 0x94, 0x59, 0xBF, 0x6F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x70, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 10 */ -}; - -typedef struct -{ - ULONG AvInfo; - USHORT Width; - USHORT Height; - ULONG DisplayMode; -} XB_DisplayMode; - -const XB_DisplayMode g_DisplayModes[] = -{ - { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1280, 720, AV_MODE_1280x720_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1920, 1080, AV_MODE_1920x1080_TO_VGA }, - - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_720p, 1280, 720, AV_MODE_1280x720P_TO_720P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 1080, AV_MODE_1920x1080I_TO_1080I }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 540, AV_MODE_1920x1080I_TO_1080I }, - - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB_16x9 }, - - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_YC }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_YC }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_YC }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_YC }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_YC }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_YC }, - { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_YC_16x9 }, - - { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1280, 720, AV_MODE_1280x720_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1920, 1080, AV_MODE_1920x1080_TO_VGA }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_720p, 1280, 720, AV_MODE_1280x720P_TO_720P }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 1080, AV_MODE_1920x1080I_TO_1080I }, - { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 540, AV_MODE_1920x1080I_TO_1080I }, - - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB }, - { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB_16x9 }, - - // All other AV packs are the same. - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_YC }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_YC }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_YC }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_YC }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_YC }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_YC_16x9 }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_YC }, - { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_YC_16x9 }, - - { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1280, 720, AV_MODE_1280x720_TO_VGA }, - { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1920, 1080, AV_MODE_1920x1080_TO_VGA }, - - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb_16x9 }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb }, - { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb_16x9 }, - - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_RGB_16x9 }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_RGB }, - { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_RGB_16x9 }, - - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YC_16x9 }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YC }, - { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YC_16x9 }, - { 0xFFFFFFFF, 0, 0, 0 }, -}; - +#pragma once + +// Mode enum values where: +// +// 0xC0000000 - output mode mask: +// +// 0x00000000 - 480 line SDTV +// 0x40000000 - 525 line SDTV +// 0x80000000 - HDTV +// 0xC0000000 - VGA +// +// 0x10000000 - enable WSS (_16x9) +// 0x20000000 - use SCART output (_RGB) +// +// 0x01000000 - disable DAC A +// 0x02000000 - disable DAC B +// 0x04000000 - disable DAC C +// 0x08000000 - disable DAC D +// +// 0x00FF0000 - register table index +// 0x0000FF00 - CRTC table index +// 0x000000FF - TV table index, based on output mode mask above + +#define AV_MODE_TABLE_VERSION 0 + +#define AV_MODE_OFF 0x00000000 +#define AV_MODE_640x480_TO_NTSC_M_YC 0x04010101 +#define AV_MODE_640x480_TO_NTSC_M_YC_16x9 0x14010101 +#define AV_MODE_720x480_TO_NTSC_M_YC 0x04020202 +#define AV_MODE_720x480_TO_NTSC_M_YC_16x9 0x14020202 +#define AV_MODE_640x480_TO_NTSC_M_RGB 0x20010101 +#define AV_MODE_640x480_TO_NTSC_M_RGB_16x9 0x30010101 +#define AV_MODE_720x480_TO_NTSC_M_RGB 0x20020202 +#define AV_MODE_720x480_TO_NTSC_M_RGB_16x9 0x30020202 +#define AV_MODE_640x480_TO_NTSC_J_YC 0x04010103 +#define AV_MODE_640x480_TO_NTSC_J_YC_16x9 0x14010103 +#define AV_MODE_720x480_TO_NTSC_J_YC 0x04020204 +#define AV_MODE_720x480_TO_NTSC_J_YC_16x9 0x14020204 +#define AV_MODE_640x480_TO_NTSC_J_RGB 0x20010103 +#define AV_MODE_640x480_TO_NTSC_J_RGB_16x9 0x30010103 +#define AV_MODE_720x480_TO_NTSC_J_RGB 0x20020204 +#define AV_MODE_720x480_TO_NTSC_J_RGB_16x9 0x30020204 +#define AV_MODE_640x480_TO_PAL_M_YC 0x04010105 +#define AV_MODE_640x480_TO_PAL_M_YC_16x9 0x14010105 +#define AV_MODE_720x480_TO_PAL_M_YC 0x04020206 +#define AV_MODE_720x480_TO_PAL_M_YC_16x9 0x14020206 +#define AV_MODE_640x480_TO_PAL_M_RGB 0x20010105 +#define AV_MODE_640x480_TO_PAL_M_RGB_16x9 0x30010105 +#define AV_MODE_720x480_TO_PAL_M_RGB 0x20020206 +#define AV_MODE_720x480_TO_PAL_M_RGB_16x9 0x30020206 +#define AV_MODE_640x480_TO_PAL_I_YC 0x44030307 +#define AV_MODE_640x480_TO_PAL_I_YC_16x9 0x54030307 +#define AV_MODE_720x480_TO_PAL_I_YC 0x44040408 +#define AV_MODE_720x480_TO_PAL_I_YC_16x9 0x54040408 +#define AV_MODE_640x576_TO_PAL_I_YC 0x44050509 +#define AV_MODE_640x576_TO_PAL_I_YC_16x9 0x54050509 +#define AV_MODE_720x576_TO_PAL_I_YC 0x4406060A +#define AV_MODE_720x576_TO_PAL_I_YC_16x9 0x5406060A +#define AV_MODE_640x480_TO_PAL_I_RGB 0x60030307 +#define AV_MODE_640x480_TO_PAL_I_RGB_16x9 0x70030307 +#define AV_MODE_720x480_TO_PAL_I_RGB 0x60040408 +#define AV_MODE_720x480_TO_PAL_I_RGB_16x9 0x70040408 +#define AV_MODE_640x576_TO_PAL_I_RGB 0x60050509 +#define AV_MODE_640x576_TO_PAL_I_RGB_16x9 0x70050509 +#define AV_MODE_720x576_TO_PAL_I_RGB 0x6006060A +#define AV_MODE_720x576_TO_PAL_I_RGB_16x9 0x7006060A +#define AV_MODE_640x480_TO_PAL_60_YC 0x0401010B +#define AV_MODE_640x480_TO_PAL_60_YC_16x9 0x1401010B +#define AV_MODE_720x480_TO_PAL_60_YC 0x0402020C +#define AV_MODE_720x480_TO_PAL_60_YC_16x9 0x1402020C +#define AV_MODE_640x480_TO_PAL_60_RGB 0x2001010B +#define AV_MODE_640x480_TO_PAL_60_RGB_16x9 0x3001010B +#define AV_MODE_720x480_TO_PAL_60_RGB 0x2002020C +#define AV_MODE_720x480_TO_PAL_60_RGB_16x9 0x3002020C +#define AV_MODE_640x480_TO_NTSC_YPrPb 0x0801010D +#define AV_MODE_640x480_TO_NTSC_YPrPb_16x9 0x1801010D +#define AV_MODE_720x480_TO_NTSC_YPrPb 0x0802020E +#define AV_MODE_720x480_TO_NTSC_YPrPb_16x9 0x1802020E +#define AV_MODE_640x480_FPAR_TO_NTSC_M_YC 0x040F0D0F +#define AV_MODE_640x480_FPAR_TO_NTSC_M_YC_16x9 0x140F0D0F +#define AV_MODE_640x480_FPAR_TO_NTSC_M_RGB 0x200F0D0F +#define AV_MODE_640x480_FPAR_TO_NTSC_M_RGB_16x9 0x300F0D0F +#define AV_MODE_640x480_FPAR_TO_NTSC_J_YC 0x040F0D10 +#define AV_MODE_640x480_FPAR_TO_NTSC_J_YC_16x9 0x140F0D10 +#define AV_MODE_640x480_FPAR_TO_NTSC_J_RGB 0x200F0D10 +#define AV_MODE_640x480_FPAR_TO_NTSC_J_RGB_16x9 0x300F0D10 +#define AV_MODE_640x480_FPAR_TO_PAL_60_YC 0x040F0D11 +#define AV_MODE_640x480_FPAR_TO_PAL_60_YC_16x9 0x140F0D11 +#define AV_MODE_640x480_FPAR_TO_PAL_60_RGB 0x200F0D11 +#define AV_MODE_640x480_FPAR_TO_PAL_60_RGB_16x9 0x300F0D11 +#define AV_MODE_640x480_FPAR_TO_NTSC_YPrPb 0x080F0D12 +#define AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 0x180F0D12 +#define AV_MODE_640x480_FPAR_TO_PAL_I_YC 0x44100E13 +#define AV_MODE_640x480_FPAR_TO_PAL_I_YC_16x9 0x54100E13 +#define AV_MODE_640x480_FPAR_TO_PAL_I_RGB 0x60100E13 +#define AV_MODE_640x480_FPAR_TO_PAL_I_RGB_16x9 0x70100E13 +#define AV_MODE_640x480_TO_PAL_I_YPrPb 0x48030314 +#define AV_MODE_640x480_TO_PAL_I_YPrPb_16x9 0x58030314 +#define AV_MODE_720x480_TO_PAL_I_YPrPb 0x48040415 +#define AV_MODE_720x480_TO_PAL_I_YPrPb_16x9 0x58040415 +#define AV_MODE_640x576_TO_PAL_I_YPrPb 0x48050516 +#define AV_MODE_640x576_TO_PAL_I_YPrPb_16x9 0x58050516 +#define AV_MODE_720x576_TO_PAL_I_YPrPb 0x48060617 +#define AV_MODE_720x576_TO_PAL_I_YPrPb_16x9 0x58060617 +#define AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb 0x48100E18 +#define AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb_16x9 0x58100E18 +#define AV_MODE_640x480_TO_PAL_60_YPrPb 0x08010119 +#define AV_MODE_640x480_TO_PAL_60_YPrPb_16x9 0x18010119 +#define AV_MODE_720x480_TO_PAL_60_YPrPb 0x0802021A +#define AV_MODE_720x480_TO_PAL_60_YPrPb_16x9 0x1802021A +#define AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb 0x080F0D1B +#define AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb_16x9 0x180F0D1B +#define AV_MODE_640x576_FPAR_TO_PAL_I_YC 0x4412101C +#define AV_MODE_640x576_FPAR_TO_PAL_I_YC_16x9 0x5412101C +#define AV_MODE_640x576_FPAR_TO_PAL_I_RGB 0x6012101C +#define AV_MODE_640x576_FPAR_TO_PAL_I_RGB_16x9 0x7012101C +#define AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb 0x4812101D +#define AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb_16x9 0x5812101D +#define AV_MODE_640x480_TO_VGA 0x88070701 +#define AV_MODE_720x480_TO_VGA 0x88080801 +#define AV_MODE_1280x720_TO_VGA 0x880B0A02 +#define AV_MODE_1920x1080_TO_VGA 0x880E0C03 +#define AV_MODE_640x480_TO_480P 0x88070701 +#define AV_MODE_720x480_TO_480P 0x88080801 +#define AV_MODE_1280x720P_TO_720P 0x880B0A02 +#define AV_MODE_1920x1080I_TO_1080I 0x880E0C03 +#define AV_MODE_640x480_FPAR_TO_480P 0x88110F01 + +#define AV_MODE_FLAGS_DACA_DISABLE 0x01000000 +#define AV_MODE_FLAGS_DACB_DISABLE 0x02000000 +#define AV_MODE_FLAGS_DACC_DISABLE 0x04000000 +#define AV_MODE_FLAGS_DACD_DISABLE 0x08000000 + +#define AV_MODE_FLAGS_WSS 0x10000000 // _16x9 +#define AV_MODE_FLAGS_SCART 0x20000000 // _RGB +#define AV_MODE_FLAGS_NTSCJ 0x00000080 + +#define AV_MODE_OUT_MASK 0xC0000000 +#define AV_MODE_OUT_480SDTV 0x00000000 +#define AV_MODE_OUT_525SDTV 0x40000000 +#define AV_MODE_OUT_HDTV 0x80000000 +#define AV_MODE_OUT_VGA 0xC0000000 + +// Register Tables +const ULONG AvpRegisters[][26] = +{ + { /* offset */ 0x00680898, 0x0068089C, 0x006808C0, 0x006808C4, 0x0068084C, 0x00680630, 0x00680800, 0x00680804, 0x00680808, 0x0068080C, 0x00680810, 0x00680814, 0x00680818, 0x00680820, 0x00680824, 0x00680828, 0x0068082C, 0x00680830, 0x00680834, 0x00680838, 0x00680848, 0x00680680, 0x00680684, 0x00680688, 0x0068068C, 0x00680690 }, /* offset */ + { /* 1 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F2, 0x000001F4, 0x00000000, 0x000001DF, 0x0000027F, 0x000003A7, 0x00000257, 0x000002F3, 0x00000333, 0x00000000, 0x0000027F, 0x10100111, 0x000C6ED0, 0x0000020D, 0x0000009B, 0x0000026C, 0x00000000 }, /* 1 */ + { /* 2 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F2, 0x000001F4, 0x00000000, 0x000001DF, 0x000002CF, 0x000003A7, 0x000002A7, 0x0000031B, 0x0000035B, 0x00000000, 0x000002CF, 0x10100111, 0x000DF05C, 0x0000020D, 0x000000AE, 0x000002B8, 0x00000000 }, /* 2 */ + { /* 3 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F4, 0x000001F6, 0x00000000, 0x000001DF, 0x0000027F, 0x0000035F, 0x00000257, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000F387C, 0x00000271, 0x000000BE, 0x000002F8, 0x00000000 }, /* 3 */ + { /* 4 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F4, 0x000001F6, 0x00000000, 0x000001DF, 0x000002CF, 0x0000035F, 0x000002AF, 0x0000030B, 0x0000034B, 0x00000000, 0x000002CF, 0x10100111, 0x0010D2A4, 0x00000271, 0x000000D2, 0x00000348, 0x00000000 }, /* 4 */ + { /* 5 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x0000023F, 0x00000270, 0x0000023F, 0x00000256, 0x00000258, 0x00000000, 0x0000023F, 0x0000027F, 0x0000035F, 0x00000257, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000F07A8, 0x00000271, 0x0000009D, 0x00000276, 0x00000000 }, /* 5 */ + { /* 6 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x0000023F, 0x00000270, 0x0000023F, 0x00000256, 0x00000258, 0x00000000, 0x0000023F, 0x000002CF, 0x0000035F, 0x000002AF, 0x0000030B, 0x0000034B, 0x00000000, 0x000002CF, 0x10100111, 0x0010E62C, 0x00000271, 0x000000B1, 0x000002C4, 0x00000000 }, /* 6 */ + { /* 7 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001E8, 0x000001EE, 0x00000000, 0x000001DF, 0x000002CF, 0x00000359, 0x0000029F, 0x000002E1, 0x00000320, 0x00000000, 0x000002CF, 0x10100011, 0x0000035A, 0x00000001, 0x000000AB, 0x000002AE, 0x00000001 }, /* 7 */ + { /* 8 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001E8, 0x000001EE, 0x00000000, 0x000001DF, 0x000002CF, 0x00000359, 0x0000029F, 0x000002E1, 0x00000320, 0x00000000, 0x000002CF, 0x10100111, 0x0000035A, 0x00000001, 0x000000AB, 0x000002AE, 0x00000001 }, /* 8 */ + { /* 9 */ 0x0AA94000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x000002CF, 0x000002ED, 0x000002CF, 0x000002D4, 0x000002D9, 0x00000000, 0x000002CF, 0x000004FF, 0x00000671, 0x000004FF, 0x00000545, 0x00000595, 0x000000A0, 0x0000045F, 0x10100011, 0x00000672, 0x00000001, 0x0000014A, 0x00000528, 0x00000001 }, /* 9 */ + { /* A */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x000002CF, 0x000002ED, 0x000002CF, 0x000002D4, 0x000002D9, 0x00000000, 0x000002CF, 0x000004FF, 0x00000671, 0x000004CF, 0x00000545, 0x00000595, 0x00000000, 0x000004FF, 0x10100011, 0x00000672, 0x00000001, 0x0000014A, 0x00000528, 0x00000001 }, /* A */ + { /* B */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x000002CF, 0x000002ED, 0x000002CF, 0x000002D4, 0x000002D9, 0x00000000, 0x000002CF, 0x000004FF, 0x00000671, 0x000004FF, 0x00000545, 0x00000595, 0x00000000, 0x000004FF, 0x10100111, 0x00000672, 0x00000001, 0x0000014A, 0x00000528, 0x00000001 }, /* B */ + { /* C */ 0x071AE000, 0x07183800, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x00000437, 0x00000464, 0x0000043C, 0x0000043C, 0x00000446, 0x00000000, 0x00000437, 0x0000077F, 0x00000897, 0x000007AA, 0x000007AB, 0x00000803, 0x000000F0, 0x0000068F, 0x10133011, 0x00000898, 0x00000001, 0x000001B8, 0x000006E0, 0x00000001 }, /* C */ + { /* D */ 0x10000000, 0x07183800, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x00000437, 0x00000464, 0x0000043C, 0x0000043C, 0x00000446, 0x00000000, 0x00000437, 0x0000077F, 0x00000897, 0x00000759, 0x000007AB, 0x00000803, 0x00000000, 0x0000077F, 0x10133011, 0x00000898, 0x00000001, 0x000001B8, 0x000006E0, 0x00000001 }, /* D */ + { /* E */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000003, 0x00000437, 0x00000464, 0x0000043B, 0x0000043B, 0x00000445, 0x00000000, 0x00000437, 0x0000077F, 0x00000897, 0x000007AB, 0x000007AC, 0x00000804, 0x00000000, 0x0000077F, 0x10133111, 0x00000898, 0x00000001, 0x000001B8, 0x000006E0, 0x00000001 }, /* E */ + { /* F */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F2, 0x000001F4, 0x00000000, 0x000001DF, 0x0000027F, 0x000003A7, 0x000002A7, 0x000002F3, 0x00000333, 0x00000000, 0x0000027F, 0x10100111, 0x000DF05C, 0x0000020D, 0x000000AE, 0x000002B8, 0x00000000 }, /* F */ + { /* 10 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001F4, 0x000001F6, 0x00000000, 0x000001DF, 0x0000027F, 0x0000035F, 0x000002A7, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000DF05C, 0x0000020D, 0x000000AE, 0x000002B8, 0x00000000 }, /* 10 */ + { /* 11 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x000001DF, 0x0000020C, 0x000001DF, 0x000001E8, 0x000001EE, 0x00000000, 0x000001DF, 0x000002CF, 0x00000359, 0x000002CF, 0x000002DF, 0x0000031E, 0x00000020, 0x000002AD, 0x10100011, 0x0000035A, 0x00000001, 0x000000AB, 0x000002AE, 0x00000001 }, /* 11 */ + { /* 12 */ 0x10000000, 0x10000000, 0x00000000, 0x40801080, 0x00801080, 0x00000002, 0x0000023F, 0x00000270, 0x0000023F, 0x00000256, 0x00000258, 0x00000000, 0x0000023F, 0x0000027F, 0x0000035F, 0x00000257, 0x000002CF, 0x0000030F, 0x00000000, 0x0000027F, 0x10100111, 0x000F07A8, 0x00000271, 0x0000009D, 0x00000276, 0x00000000 }, /* 12 */ +}; + +const UCHAR AvpSRXRegisters[] = +{ + 0x03, 0x21, 0x0F, 0x00, 0x06, +}; + +const UCHAR AvpGRXRegisters[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF, +}; + +const UCHAR AvpARXRegisters[] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x01, 0x4A, 0x0F, 0x00, 0x00, +}; + +const UCHAR AvpCRTCRegisters[][34] = +{ + { /* offset */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x20, 0x25, 0x2D, 0x33, 0x39, 0x41 }, /* offset */ + { /* 1 */ 0x70, 0x4F, 0x4F, 0x94, 0x5D, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x04, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 1 */ + { /* 2 */ 0x70, 0x59, 0x59, 0x94, 0x62, 0xA4, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x04, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 2 */ + { /* 3 */ 0x67, 0x4F, 0x4F, 0x8B, 0x59, 0xBB, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x06, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 3 */ + { /* 4 */ 0x67, 0x59, 0x59, 0x8B, 0x5E, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x06, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 4 */ + { /* 5 */ 0x67, 0x4F, 0x4F, 0x8B, 0x59, 0xBB, 0x6F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x70, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 5 */ + { /* 6 */ 0x67, 0x59, 0x59, 0x8B, 0x5E, 0xBF, 0x6F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x70, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 6 */ + { /* 7 */ 0x59, 0x4F, 0x4F, 0x9D, 0x51, 0x39, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0E, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 7 */ + { /* 8 */ 0x63, 0x59, 0x59, 0x87, 0x5B, 0xA3, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0E, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 8 */ + { /* 9 */ 0x78, 0x4F, 0x4F, 0x9C, 0x57, 0xA1, 0xFC, 0x1F, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x09, 0xDF, 0x00, 0x00, 0xDF, 0xFD, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 9 */ + { /* A */ 0xC8, 0x9F, 0x9F, 0x8C, 0xA7, 0x31, 0xEC, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x09, 0xCF, 0x00, 0x00, 0xCF, 0xED, 0xE3, 0xFF, 0x00, 0x38, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* A */ + { /* B */ 0x67, 0x4F, 0x4F, 0x8B, 0x54, 0xBF, 0x03, 0x11, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0x06, 0xEF, 0x00, 0x00, 0xEF, 0x04, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0x36, 0x00 }, /* B */ + { /* C */ 0x04, 0xEF, 0xEF, 0x88, 0xF4, 0x3F, 0x2F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x02, 0x1B, 0x00, 0x00, 0x1B, 0x30, 0xE3, 0xFF, 0x00, 0x38, 0x05, 0x80, 0x00, 0x01, 0x11, 0x10, 0x00 }, /* C */ + { /* D */ 0x70, 0x4F, 0x4F, 0x94, 0x5D, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x04, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* D */ + { /* E */ 0x67, 0x4F, 0x4F, 0x8B, 0x59, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x06, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x00, 0x00, 0x11, 0xFF, 0x00 }, /* E */ + { /* F */ 0x61, 0x57, 0x57, 0x85, 0x59, 0xBF, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x0E, 0xDF, 0x00, 0x00, 0xDF, 0x0C, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* F */ + { /* 10 */ 0x67, 0x4F, 0x4F, 0x94, 0x59, 0xBF, 0x6F, 0xF0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x08, 0x3F, 0x00, 0x00, 0x3F, 0x70, 0xE3, 0xFF, 0x00, 0x3A, 0x05, 0x80, 0x10, 0x00, 0x11, 0xFF, 0x00 }, /* 10 */ +}; + +typedef struct +{ + ULONG AvInfo; + USHORT Width; + USHORT Height; + ULONG DisplayMode; +} XB_DisplayMode; + +const XB_DisplayMode g_DisplayModes[] = +{ + { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1280, 720, AV_MODE_1280x720_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1920, 1080, AV_MODE_1920x1080_TO_VGA }, + + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_720p, 1280, 720, AV_MODE_1280x720P_TO_720P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 1080, AV_MODE_1920x1080I_TO_1080I }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 540, AV_MODE_1920x1080I_TO_1080I }, + + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_RGB_16x9 }, + + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_YC }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_M_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_YC }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_M_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_YC }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_M_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_YC }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_M_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_YC }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_M_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_YC }, + { 0 | AV_STANDARD_NTSC_M | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_M_YC_16x9 }, + + { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1280, 720, AV_MODE_1280x720_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1920, 1080, AV_MODE_1920x1080_TO_VGA }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480p, 720, 480, AV_MODE_720x480_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480p, 640, 480, AV_MODE_640x480_FPAR_TO_480P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | 0 | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_720p, 1280, 720, AV_MODE_1280x720P_TO_720P }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 480, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_480i, 720, 240, AV_MODE_720x480_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | AV_FLAGS_HDTV_480i, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 1080, AV_MODE_1920x1080I_TO_1080I }, + { AV_PACK_HDTV | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | AV_FLAGS_HDTV_1080i, 1920, 540, AV_MODE_1920x1080I_TO_1080I }, + + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB }, + { AV_PACK_SCART | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_RGB_16x9 }, + + // All other AV packs are the same. + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_YC }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_NTSC_J_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_YC }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_NTSC_J_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_YC }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_NTSC_J_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_YC }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_NTSC_J_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_YC }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_NTSC_J_YC_16x9 }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_YC }, + { 0 | AV_STANDARD_NTSC_J | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_NTSC_J_YC_16x9 }, + + { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1280, 720, AV_MODE_1280x720_TO_VGA }, + { AV_PACK_VGA | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | 0 | 0 | 0 | 0, 1920, 1080, AV_MODE_1920x1080_TO_VGA }, + + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb_16x9 }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb }, + { AV_PACK_HDTV | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YPrPb_16x9 }, + + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_RGB_16x9 }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_RGB }, + { AV_PACK_SCART | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_RGB_16x9 }, + + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 576, AV_MODE_640x576_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 576, AV_MODE_720x576_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 288, AV_MODE_640x576_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 288, AV_MODE_720x576_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 576, AV_MODE_640x576_FPAR_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_50Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 288, AV_MODE_640x576_FPAR_TO_PAL_I_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 480, AV_MODE_640x480_TO_PAL_60_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 480, AV_MODE_720x480_TO_PAL_60_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 640, 240, AV_MODE_640x480_TO_PAL_60_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | 0 | 0, 720, 240, AV_MODE_720x480_TO_PAL_60_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | 0 | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_INTERLACED | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 480, AV_MODE_640x480_FPAR_TO_PAL_60_YC_16x9 }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | 0 | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YC }, + { 0 | AV_STANDARD_PAL_I | AV_FLAGS_60Hz | AV_FLAGS_FIELD | AV_FLAGS_WIDESCREEN | AV_FLAGS_10x11PAR | 0, 640, 240, AV_MODE_640x480_FPAR_TO_PAL_60_YC_16x9 }, + { 0xFFFFFFFF, 0, 0, 0 }, +}; + const uint32_t g_DisplayModeCount = sizeof(g_DisplayModes) / sizeof(XB_DisplayMode); \ No newline at end of file diff --git a/src/core/kernel/exports/EmuKrnlKd.cpp b/src/core/kernel/exports/EmuKrnlKd.cpp index 7a07ae8da..4c9ce9be2 100644 --- a/src/core/kernel/exports/EmuKrnlKd.cpp +++ b/src/core/kernel/exports/EmuKrnlKd.cpp @@ -29,7 +29,7 @@ #define LOG_PREFIX CXBXR_MODULE::KD -#include // For KdDebuggerEnabled, etc. +#include // For KdDebuggerEnabled, etc. #include "Logging.h" // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 3bc8cdb28..feed32f33 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -24,42 +24,42 @@ // * // * All rights reserved // * -// ****************************************************************** - -// Acknowledgment (timer functions): ReactOS (GPLv2) -// https://github.com/reactos/reactos - -// KeSetSystemTime -/* -* PROJECT: ReactOS Kernel -* LICENSE: GPL - See COPYING in the top level directory -* FILE: ntoskrnl/ke/clock.c -* PURPOSE: System Clock Support -* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) -*/ - -// KeInitializeTimerEx, KeSetTimer, KeSetTimerEx, KeCancelTimer -/* -* PROJECT: ReactOS Kernel -* LICENSE: GPL - See COPYING in the top level directory -* FILE: ntoskrnl/ke/timerobj.c -* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers) -* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) -*/ - -// COPYING file: -/* -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 - -ReactOS may be used, runtime linked, and distributed with non-free software -(meaning that such software has no obligations to open-source, or render -free, their non-free code) such as commercial device drivers and commercial -applications. This exception does not alter any other responsibilities of -the licensee under the GPL (meaning that such software must still obey -the GPL for the free ("open-sourced") code that has been integrated into -the said software). -*/ +// ****************************************************************** + +// Acknowledgment (timer functions): ReactOS (GPLv2) +// https://github.com/reactos/reactos + +// KeSetSystemTime +/* +* PROJECT: ReactOS Kernel +* LICENSE: GPL - See COPYING in the top level directory +* FILE: ntoskrnl/ke/clock.c +* PURPOSE: System Clock Support +* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) +*/ + +// KeInitializeTimerEx, KeSetTimer, KeSetTimerEx, KeCancelTimer +/* +* PROJECT: ReactOS Kernel +* LICENSE: GPL - See COPYING in the top level directory +* FILE: ntoskrnl/ke/timerobj.c +* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers) +* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) +*/ + +// COPYING file: +/* +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +ReactOS may be used, runtime linked, and distributed with non-free software +(meaning that such software has no obligations to open-source, or render +free, their non-free code) such as commercial device drivers and commercial +applications. This exception does not alter any other responsibilities of +the licensee under the GPL (meaning that such software must still obey +the GPL for the free ("open-sourced") code that has been integrated into +the said software). +*/ #define LOG_PREFIX CXBXR_MODULE::KE @@ -78,9 +78,9 @@ namespace NtDll #include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup #include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) #include "EmuKrnl.h" // For InitializeListHead(), etc. -#include "EmuKrnlKi.h" // For KiRemoveTreeTimer(), KiInsertTreeTimer() +#include "EmuKrnlKi.h" // For KiRemoveTreeTimer(), KiInsertTreeTimer() #include "EmuKrnlKe.h" -#include "core\kernel\support\EmuFile.h" // For IsEmuHandle(), NtStatusToString() +#include "core\kernel\support\EmuFile.h" // For IsEmuHandle(), NtStatusToString() #include "Timer.h" #include @@ -155,100 +155,100 @@ xboxkrnl::KPCR* WINAPI KeGetPcr() xboxkrnl::KPRCB *KeGetCurrentPrcb() { return &(KeGetPcr()->PrcbData); -} - +} + // ****************************************************************** // * KeSetSystemTime() -// ****************************************************************** -xboxkrnl::VOID NTAPI xboxkrnl::KeSetSystemTime -( - IN xboxkrnl::PLARGE_INTEGER NewTime, - OUT xboxkrnl::PLARGE_INTEGER OldTime -) -{ - KIRQL OldIrql, OldIrql2; - LARGE_INTEGER DeltaTime, HostTime; - PLIST_ENTRY ListHead, NextEntry; - PKTIMER Timer; - LIST_ENTRY TempList, TempList2; - ULONG Hand, i; - - /* Sanity checks */ - assert((NewTime->u.HighPart & 0xF0000000) == 0); - assert(KeGetCurrentIrql() <= DISPATCH_LEVEL); - - /* Lock the dispatcher, and raise IRQL */ - KiTimerLock(); - KiLockDispatcherDatabase(&OldIrql); - OldIrql2 = KfRaiseIrql(HIGH_LEVEL); - - /* Query the system time now */ - KeQuerySystemTime(OldTime); - - /* Surely, we won't set the system time here, but we CAN remember a delta to the host system time */ - HostTime.QuadPart = OldTime->QuadPart - HostSystemTimeDelta.load(); - HostSystemTimeDelta = NewTime->QuadPart - HostTime.QuadPart; - - /* Calculate the difference between the new and the old time */ - DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart; - - /* Lower IRQL back */ - KfLowerIrql(OldIrql2); - - /* Setup a temporary list of absolute timers */ - InitializeListHead(&TempList); - - /* Loop current timers */ - for (i = 0; i < TIMER_TABLE_SIZE; i++) - { - /* Loop the entries in this table and lock the timers */ - ListHead = &KiTimerTableListHead[i].Entry; - NextEntry = ListHead->Flink; - while (NextEntry != ListHead) - { - /* Get the timer */ - Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); - NextEntry = NextEntry->Flink; - - /* Is it absolute? */ - if (Timer->Header.Absolute) - { - /* Remove it from the timer list */ - KiRemoveEntryTimer(Timer, i); - - /* Insert it into our temporary list */ - InsertTailList(&TempList, &Timer->TimerListEntry); - } - } - } - - /* Setup a temporary list of expired timers */ - InitializeListHead(&TempList2); - - /* Loop absolute timers */ - while (TempList.Flink != &TempList) - { - /* Get the timer */ - Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry); - RemoveEntryList(&Timer->TimerListEntry); - - /* Update the due time and handle */ - Timer->DueTime.QuadPart -= DeltaTime.QuadPart; - Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); - - /* Lock the timer and re-insert it */ - if (KiInsertTimerTable(Timer, Hand)) - { - /* Remove it from the timer list */ - KiRemoveEntryTimer(Timer, Hand); - - /* Insert it into our temporary list */ - InsertTailList(&TempList2, &Timer->TimerListEntry); - } - } - - /* Process expired timers. This releases the dispatcher and timer locks */ - KiTimerListExpire(&TempList2, OldIrql); +// ****************************************************************** +xboxkrnl::VOID NTAPI xboxkrnl::KeSetSystemTime +( + IN xboxkrnl::PLARGE_INTEGER NewTime, + OUT xboxkrnl::PLARGE_INTEGER OldTime +) +{ + KIRQL OldIrql, OldIrql2; + LARGE_INTEGER DeltaTime, HostTime; + PLIST_ENTRY ListHead, NextEntry; + PKTIMER Timer; + LIST_ENTRY TempList, TempList2; + ULONG Hand, i; + + /* Sanity checks */ + assert((NewTime->u.HighPart & 0xF0000000) == 0); + assert(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + /* Lock the dispatcher, and raise IRQL */ + KiTimerLock(); + KiLockDispatcherDatabase(&OldIrql); + OldIrql2 = KfRaiseIrql(HIGH_LEVEL); + + /* Query the system time now */ + KeQuerySystemTime(OldTime); + + /* Surely, we won't set the system time here, but we CAN remember a delta to the host system time */ + HostTime.QuadPart = OldTime->QuadPart - HostSystemTimeDelta.load(); + HostSystemTimeDelta = NewTime->QuadPart - HostTime.QuadPart; + + /* Calculate the difference between the new and the old time */ + DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart; + + /* Lower IRQL back */ + KfLowerIrql(OldIrql2); + + /* Setup a temporary list of absolute timers */ + InitializeListHead(&TempList); + + /* Loop current timers */ + for (i = 0; i < TIMER_TABLE_SIZE; i++) + { + /* Loop the entries in this table and lock the timers */ + ListHead = &KiTimerTableListHead[i].Entry; + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) + { + /* Get the timer */ + Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); + NextEntry = NextEntry->Flink; + + /* Is it absolute? */ + if (Timer->Header.Absolute) + { + /* Remove it from the timer list */ + KiRemoveEntryTimer(Timer, i); + + /* Insert it into our temporary list */ + InsertTailList(&TempList, &Timer->TimerListEntry); + } + } + } + + /* Setup a temporary list of expired timers */ + InitializeListHead(&TempList2); + + /* Loop absolute timers */ + while (TempList.Flink != &TempList) + { + /* Get the timer */ + Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry); + RemoveEntryList(&Timer->TimerListEntry); + + /* Update the due time and handle */ + Timer->DueTime.QuadPart -= DeltaTime.QuadPart; + Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); + + /* Lock the timer and re-insert it */ + if (KiInsertTimerTable(Timer, Hand)) + { + /* Remove it from the timer list */ + KiRemoveEntryTimer(Timer, Hand); + + /* Insert it into our temporary list */ + InsertTailList(&TempList2, &Timer->TimerListEntry); + } + } + + /* Process expired timers. This releases the dispatcher and timer locks */ + KiTimerListExpire(&TempList2, OldIrql); } // ****************************************************************** @@ -259,10 +259,10 @@ xboxkrnl::VOID NTAPI xboxkrnl::KeInitializeTimer IN PKTIMER Timer ) { - LOG_FORWARD("KeInitializeTimerEx"); - - KeInitializeTimerEx(Timer, NotificationTimer); -} + LOG_FORWARD("KeInitializeTimerEx"); + + KeInitializeTimerEx(Timer, NotificationTimer); +} // Forward KeLowerIrql() to KfLowerIrql() #define KeLowerIrql(NewIrql) \ @@ -324,17 +324,17 @@ void InitDpcThread() g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr); } -#define XBOX_TSC_FREQUENCY 733333333 // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock) +#define XBOX_TSC_FREQUENCY 733333333 // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock) #define XBOX_ACPI_FREQUENCY 3375000 // Xbox ACPI frequency (3.375 mhz) -ULONGLONG NativeToXbox_FactorForRdtsc; +ULONGLONG NativeToXbox_FactorForRdtsc; ULONGLONG NativeToXbox_FactorForAcpi; ULONGLONG CxbxGetPerformanceCounter(bool acpi) { LARGE_INTEGER tsc; - ULARGE_INTEGER scaledTsc; - - QueryPerformanceCounter(&tsc); - + ULARGE_INTEGER scaledTsc; + + QueryPerformanceCounter(&tsc); + scaledTsc.QuadPart = 1000000000; scaledTsc.QuadPart *= (ULONGLONG)tsc.QuadPart; @@ -355,11 +355,11 @@ void CxbxInitPerformanceCounters() t = 1000000000; t *= HostClockFrequency; t /= XBOX_TSC_FREQUENCY; - NativeToXbox_FactorForRdtsc = t; - + NativeToXbox_FactorForRdtsc = t; + t = 1000000000; t *= HostClockFrequency; - t /= XBOX_ACPI_FREQUENCY; + t /= XBOX_ACPI_FREQUENCY; NativeToXbox_FactorForAcpi = t; // Let's initialize the Dpc handling thread too, @@ -496,27 +496,27 @@ XBSYSAPI EXPORTNUM(96) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeCancelTimer IN PKTIMER Timer ) { - LOG_FUNC_ONE_ARG(Timer); - - KIRQL OldIrql; - BOOLEAN Inserted; - - assert(Timer); - - /* Lock the Database and Raise IRQL */ - KiTimerLock(); - KiLockDispatcherDatabase(&OldIrql); - - /* Check if it's inserted, and remove it if it is */ - Inserted = Timer->Header.Inserted; - if (Inserted) { - KxRemoveTreeTimer(Timer); - } - - /* Release Dispatcher Lock */ - KiUnlockDispatcherDatabase(OldIrql); - KiTimerUnlock(); - + LOG_FUNC_ONE_ARG(Timer); + + KIRQL OldIrql; + BOOLEAN Inserted; + + assert(Timer); + + /* Lock the Database and Raise IRQL */ + KiTimerLock(); + KiLockDispatcherDatabase(&OldIrql); + + /* Check if it's inserted, and remove it if it is */ + Inserted = Timer->Header.Inserted; + if (Inserted) { + KxRemoveTreeTimer(Timer); + } + + /* Release Dispatcher Lock */ + KiUnlockDispatcherDatabase(OldIrql); + KiTimerUnlock(); + /* Return the old state */ RETURN(Inserted); } @@ -737,14 +737,14 @@ XBSYSAPI EXPORTNUM(108) xboxkrnl::VOID NTAPI xboxkrnl::KeInitializeEvent LOG_FUNC_ARG(Type) LOG_FUNC_ARG(SignalState) LOG_FUNC_END; - - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, it is already initialized so no need tod o anything - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation((HANDLE)Event, &flags)) { - return; - } + + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, it is already initialized so no need tod o anything + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation((HANDLE)Event, &flags)) { + return; + } // Setup the Xbox event struct Event->Header.Type = Type; @@ -894,8 +894,8 @@ XBSYSAPI EXPORTNUM(113) xboxkrnl::VOID NTAPI xboxkrnl::KeInitializeTimerEx LOG_FUNC_BEGIN LOG_FUNC_ARG(Timer) LOG_FUNC_ARG(Type) - LOG_FUNC_END; - + LOG_FUNC_END; + assert(Timer); // Initialize header : @@ -1137,16 +1137,16 @@ XBSYSAPI EXPORTNUM(123) xboxkrnl::LONG NTAPI xboxkrnl::KePulseEvent LOG_FUNC_END; KIRQL OldIrql; - KiLockDispatcherDatabase(&OldIrql); - - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, we must call the NtDll function - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation((HANDLE)Event, &flags)) { - KiUnlockDispatcherDatabase(OldIrql); - return NtDll::NtPulseEvent((HANDLE)Event, nullptr); - } + KiLockDispatcherDatabase(&OldIrql); + + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, we must call the NtDll function + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation((HANDLE)Event, &flags)) { + KiUnlockDispatcherDatabase(OldIrql); + return NtDll::NtPulseEvent((HANDLE)Event, nullptr); + } LONG OldState = Event->Header.SignalState; if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) { @@ -1214,8 +1214,8 @@ XBSYSAPI EXPORTNUM(125) xboxkrnl::ULONGLONG NTAPI xboxkrnl::KeQueryInterruptTime } // ****************************************************************** -// * 0x007E - KeQueryPerformanceCounter() -// NOTE: The KeQueryPerformance* functions run at the ACPI clock +// * 0x007E - KeQueryPerformanceCounter() +// NOTE: The KeQueryPerformance* functions run at the ACPI clock // The XAPI QueryPerformance* functions run at the TSC clock // ****************************************************************** XBSYSAPI EXPORTNUM(126) xboxkrnl::ULONGLONG NTAPI xboxkrnl::KeQueryPerformanceCounter(void) @@ -1245,8 +1245,8 @@ XBSYSAPI EXPORTNUM(128) xboxkrnl::VOID NTAPI xboxkrnl::KeQuerySystemTime ) { LOG_FUNC_ONE_ARG(CurrentTime); - - LARGE_INTEGER SystemTime; + + LARGE_INTEGER SystemTime; while (true) { @@ -1317,30 +1317,30 @@ XBSYSAPI EXPORTNUM(132) xboxkrnl::LONG NTAPI xboxkrnl::KeReleaseSemaphore LOG_FUNC_ARG(Wait) LOG_FUNC_END; - UCHAR orig_irql = KeRaiseIrqlToDpcLevel(); - LONG initial_state = Semaphore->Header.SignalState; - LONG adjusted_signalstate = Semaphore->Header.SignalState + Adjustment; - - BOOL limit_reached = adjusted_signalstate > Semaphore->Limit; - BOOL signalstate_overflow = adjusted_signalstate < initial_state; - if (limit_reached || signalstate_overflow) { - KiUnlockDispatcherDatabase(orig_irql); - ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED); - } - Semaphore->Header.SignalState = adjusted_signalstate; - - //TODO: Implement KiWaitTest -#if 0 - if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) { + UCHAR orig_irql = KeRaiseIrqlToDpcLevel(); + LONG initial_state = Semaphore->Header.SignalState; + LONG adjusted_signalstate = Semaphore->Header.SignalState + Adjustment; + + BOOL limit_reached = adjusted_signalstate > Semaphore->Limit; + BOOL signalstate_overflow = adjusted_signalstate < initial_state; + if (limit_reached || signalstate_overflow) { + KiUnlockDispatcherDatabase(orig_irql); + ExRaiseStatus(STATUS_SEMAPHORE_LIMIT_EXCEEDED); + } + Semaphore->Header.SignalState = adjusted_signalstate; + + //TODO: Implement KiWaitTest +#if 0 + if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) { KiWaitTest(&Semaphore->Header, Increment); - } -#endif - + } +#endif + if (Wait) { - PKTHREAD current_thread = KeGetCurrentThread(); - current_thread->WaitNext = TRUE; + PKTHREAD current_thread = KeGetCurrentThread(); + current_thread->WaitNext = TRUE; current_thread->WaitIrql = orig_irql; - } + } else { KiUnlockDispatcherDatabase(orig_irql); } @@ -1503,15 +1503,15 @@ XBSYSAPI EXPORTNUM(138) xboxkrnl::LONG NTAPI xboxkrnl::KeResetEvent LOG_FUNC_ONE_ARG(Event); KIRQL OldIrql; - KiLockDispatcherDatabase(&OldIrql); - - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, we must call the NtDll function - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation((HANDLE)Event, &flags)) { - KiUnlockDispatcherDatabase(OldIrql); - return NtDll::NtResetEvent((HANDLE)Event, nullptr); + KiLockDispatcherDatabase(&OldIrql); + + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, we must call the NtDll function + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation((HANDLE)Event, &flags)) { + KiUnlockDispatcherDatabase(OldIrql); + return NtDll::NtResetEvent((HANDLE)Event, nullptr); } LONG OldState = Event->Header.SignalState; @@ -1648,15 +1648,15 @@ XBSYSAPI EXPORTNUM(145) xboxkrnl::LONG NTAPI xboxkrnl::KeSetEvent LOG_FUNC_END; KIRQL OldIrql; - KiLockDispatcherDatabase(&OldIrql); - - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, we must call the NtDll function - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation((HANDLE)Event, &flags)) { - KiUnlockDispatcherDatabase(OldIrql); - return NtDll::NtSetEvent((HANDLE)Event, nullptr); + KiLockDispatcherDatabase(&OldIrql); + + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, we must call the NtDll function + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation((HANDLE)Event, &flags)) { + KiUnlockDispatcherDatabase(OldIrql); + return NtDll::NtSetEvent((HANDLE)Event, nullptr); } LONG OldState = Event->Header.SignalState; @@ -1703,15 +1703,15 @@ XBSYSAPI EXPORTNUM(146) xboxkrnl::VOID NTAPI xboxkrnl::KeSetEventBoostPriority LOG_FUNC_ARG(Thread) LOG_FUNC_END; KIRQL OldIrql; - KiLockDispatcherDatabase(&OldIrql); - - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, we must do nothing. Anything else *will* cause crashes - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation((HANDLE)Event, &flags)) { - KiUnlockDispatcherDatabase(OldIrql); - return; + KiLockDispatcherDatabase(&OldIrql); + + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, we must do nothing. Anything else *will* cause crashes + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation((HANDLE)Event, &flags)) { + KiUnlockDispatcherDatabase(OldIrql); + return; } if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) { @@ -1803,15 +1803,15 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx LOG_FUNC_ARG(Dpc) LOG_FUNC_END; - BOOLEAN Inserted; - BOOLEAN RequestInterrupt = FALSE; - KIRQL OldIrql; + BOOLEAN Inserted; + BOOLEAN RequestInterrupt = FALSE; + KIRQL OldIrql; ULONG Hand; - + assert(Timer); - assert(Timer->Header.Type == TimerNotificationObject || Timer->Header.Type == TimerSynchronizationObject); - - KiTimerLock(); + assert(Timer->Header.Type == TimerNotificationObject || Timer->Header.Type == TimerSynchronizationObject); + + KiTimerLock(); KiLockDispatcherDatabase(&OldIrql); // Same as KeCancelTimer(Timer) : @@ -1820,29 +1820,29 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx // Do some unlinking if already inserted in the linked list KxRemoveTreeTimer(Timer); } - - /* Set Default Timer Data */ - Timer->Dpc = Dpc; - Timer->Period = Period; - if (!KiComputeDueTime(Timer, DueTime, &Hand)) - { - /* Signal the timer */ - RequestInterrupt = KiSignalTimer(Timer); - - /* Check if we need to do an interrupt */ - if (RequestInterrupt) { - HalRequestSoftwareInterrupt(DISPATCH_LEVEL); - } - } - else - { - /* Insert the timer */ - Timer->Header.SignalState = FALSE; - KxInsertTimer(Timer, Hand); - } - - /* Exit the dispatcher */ - KiUnlockDispatcherDatabase(OldIrql); + + /* Set Default Timer Data */ + Timer->Dpc = Dpc; + Timer->Period = Period; + if (!KiComputeDueTime(Timer, DueTime, &Hand)) + { + /* Signal the timer */ + RequestInterrupt = KiSignalTimer(Timer); + + /* Check if we need to do an interrupt */ + if (RequestInterrupt) { + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + } + } + else + { + /* Insert the timer */ + Timer->Header.SignalState = FALSE; + KxInsertTimer(Timer, Hand); + } + + /* Exit the dispatcher */ + KiUnlockDispatcherDatabase(OldIrql); KiTimerUnlock(); RETURN(Inserted); @@ -1998,7 +1998,7 @@ XBSYSAPI EXPORTNUM(158) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForMultipleObje BOOLEAN WaitSatisfied; NTSTATUS WaitStatus; PKMUTANT ObjectMutant; - // Hack variable (remove this when the thread scheduler is here) + // Hack variable (remove this when the thread scheduler is here) bool timeout_set = false; do { // Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase @@ -2074,8 +2074,8 @@ XBSYSAPI EXPORTNUM(158) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForMultipleObje goto NoWait; } - // Setup a timer for the thread but only once (for now) - if (!timeout_set) { + // Setup a timer for the thread but only once (for now) + if (!timeout_set) { KiTimerLock(); PKTIMER Timer = &Thread->Timer; PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock; @@ -2084,25 +2084,25 @@ XBSYSAPI EXPORTNUM(158) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForMultipleObje Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry; WaitTimer->NextWaitBlock = WaitBlock; if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) { - WaitStatus = (NTSTATUS)STATUS_TIMEOUT; + WaitStatus = (NTSTATUS)STATUS_TIMEOUT; KiTimerUnlock(); goto NoWait; } - - // Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same - // thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these - // duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock. - // This can be removed once KiSwapThread and the kernel/user APCs are implemented + + // Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same + // thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these + // duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock. + // This can be removed once KiSwapThread and the kernel/user APCs are implemented timeout_set = true; - DueTime.QuadPart = Timer->DueTime.QuadPart; + DueTime.QuadPart = Timer->DueTime.QuadPart; KiTimerUnlock(); } - // KiTimerExpiration has removed the timer but the objects were not signaled, so we have a timeout - // (remove this when the thread scheduler is here) - if (Thread->Timer.Header.Inserted == FALSE) { + // KiTimerExpiration has removed the timer but the objects were not signaled, so we have a timeout + // (remove this when the thread scheduler is here) + if (Thread->Timer.Header.Inserted == FALSE) { WaitStatus = (NTSTATUS)(STATUS_TIMEOUT); - goto NoWait; + goto NoWait; } } else { @@ -2167,8 +2167,8 @@ XBSYSAPI EXPORTNUM(158) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForMultipleObje } } while (TRUE); - // NOTE: we don't need to remove the wait blocks for the object and/or the timer because InsertTailList is disabled at - // the moment, which means they are never attached to the object wait list. TimerWaitBlock can also stay attached to the timer wait + // NOTE: we don't need to remove the wait blocks for the object and/or the timer because InsertTailList is disabled at + // the moment, which means they are never attached to the object wait list. TimerWaitBlock can also stay attached to the timer wait // list since KiTimerExpiration disregards it for now. // The waiting thead has been alerted, or an APC needs to be delivered @@ -2185,11 +2185,11 @@ NoWait: // Unlock the database and return the status //TODO: KiAdjustQuantumThread(Thread); - // Don't forget to remove the thread timer if the objects were signaled before the timer expired - // (remove this when the thread scheduler is here) + // Don't forget to remove the thread timer if the objects were signaled before the timer expired + // (remove this when the thread scheduler is here) if (timeout_set && Thread->Timer.Header.Inserted == TRUE) { - KiTimerLock(); - KxRemoveTreeTimer(&Thread->Timer); + KiTimerLock(); + KxRemoveTreeTimer(&Thread->Timer); KiTimerUnlock(); } @@ -2238,7 +2238,7 @@ XBSYSAPI EXPORTNUM(159) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForSingleObject KWAIT_BLOCK StackWaitBlock; PKWAIT_BLOCK WaitBlock = &StackWaitBlock; NTSTATUS WaitStatus; - // Hack variable (remove this when the thread scheduler is here) + // Hack variable (remove this when the thread scheduler is here) bool timeout_set = false; do { // Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase @@ -2311,11 +2311,11 @@ XBSYSAPI EXPORTNUM(159) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForSingleObject KiTimerUnlock(); } - // KiTimerExpiration has removed the timer but the object was not signaled, so we have a timeout - // (remove this when the thread scheduler is here) - if (Thread->Timer.Header.Inserted == FALSE) { + // KiTimerExpiration has removed the timer but the object was not signaled, so we have a timeout + // (remove this when the thread scheduler is here) + if (Thread->Timer.Header.Inserted == FALSE) { WaitStatus = (NTSTATUS)(STATUS_TIMEOUT); - goto NoWait; + goto NoWait; } } else { @@ -2372,8 +2372,8 @@ XBSYSAPI EXPORTNUM(159) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForSingleObject } } while (TRUE); - // NOTE: we don't need to remove the wait blocks for the object and/or the timer because InsertTailList is disabled at - // the moment, which means they are never attached to the object wait list. TimerWaitBlock can also stay attached to the timer wait + // NOTE: we don't need to remove the wait blocks for the object and/or the timer because InsertTailList is disabled at + // the moment, which means they are never attached to the object wait list. TimerWaitBlock can also stay attached to the timer wait // list since KiTimerExpiration disregards it for now. // The waiting thead has been alerted, or an APC needs to be delivered @@ -2390,11 +2390,11 @@ NoWait: // Unlock the database and return the status //TODO: KiAdjustQuantumThread(Thread); - // Don't forget to remove the thread timer if the object was signaled before the timer expired - // (remove this when the thread scheduler is here) + // Don't forget to remove the thread timer if the object was signaled before the timer expired + // (remove this when the thread scheduler is here) if (timeout_set && Thread->Timer.Header.Inserted == TRUE) { - KiTimerLock(); - KxRemoveTreeTimer(&Thread->Timer); + KiTimerLock(); + KxRemoveTreeTimer(&Thread->Timer); KiTimerUnlock(); } diff --git a/src/core/kernel/exports/EmuKrnlKe.h b/src/core/kernel/exports/EmuKrnlKe.h index 148104c0f..f17cbedd7 100644 --- a/src/core/kernel/exports/EmuKrnlKe.h +++ b/src/core/kernel/exports/EmuKrnlKe.h @@ -21,20 +21,20 @@ // * // * All rights reserved // * -// ****************************************************************** - -#pragma once - -namespace xboxkrnl -{ - VOID NTAPI KeSetSystemTime - ( - IN PLARGE_INTEGER NewTime, - OUT PLARGE_INTEGER OldTime - ); - - VOID NTAPI KeInitializeTimer - ( - IN PKTIMER Timer - ); -} +// ****************************************************************** + +#pragma once + +namespace xboxkrnl +{ + VOID NTAPI KeSetSystemTime + ( + IN PLARGE_INTEGER NewTime, + OUT PLARGE_INTEGER OldTime + ); + + VOID NTAPI KeInitializeTimer + ( + IN PKTIMER Timer + ); +} diff --git a/src/core/kernel/exports/EmuKrnlKi.cpp b/src/core/kernel/exports/EmuKrnlKi.cpp index fac4564b1..8ce0f69bb 100644 --- a/src/core/kernel/exports/EmuKrnlKi.cpp +++ b/src/core/kernel/exports/EmuKrnlKi.cpp @@ -20,60 +20,60 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2002-2003 Aaron Robinson -// * (c) 2016 Patrick van Logchem +// * (c) 2016 Patrick van Logchem // * (c) 2019 ergo720 // * // * All rights reserved // * -// ****************************************************************** - -// Acknowledgment (timer functions): ReactOS (GPLv2) -// https://github.com/reactos/reactos - -// KiCheckTimerTable, KiTimerExpiration, KiTimerListExpire -/* -* PROJECT: ReactOS Kernel -* LICENSE: GPL - See COPYING in the top level directory -* FILE: ntoskrnl/ke/dpc.c -* PURPOSE: Deferred Procedure Call (DPC) Support -* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) -* Philip Susi (phreak@iag.net) -* Eric Kohl -*/ - -// KiInsertTreeTimer, KiInsertTimerTable, KiSignalTimer, KiCompleteTimer -/* -* PROJECT: ReactOS Kernel -* LICENSE: GPL - See COPYING in the top level directory -* FILE: ntoskrnl/ke/timerobj.c -* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers) -* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) -*/ - -// KiComputeDueTime, KiComputeTimerTableIndex, KxInsertTimer, KxRemoveTreeTimer, KiRemoveEntryTimer -/* -* PROJECT: ReactOS Kernel -* LICENSE: GPL - See COPYING in the top level directory -* FILE: ntoskrnl/include/internal/ke_x.h -* PURPOSE: Internal Inlined Functions for the Kernel -* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) -*/ - -// COPYING file: -/* -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 - -ReactOS may be used, runtime linked, and distributed with non-free software -(meaning that such software has no obligations to open-source, or render -free, their non-free code) such as commercial device drivers and commercial -applications. This exception does not alter any other responsibilities of -the licensee under the GPL (meaning that such software must still obey -the GPL for the free ("open-sourced") code that has been integrated into -the said software). -*/ - -// Changed from ReactOS: slight changes to the Hand parameter usage +// ****************************************************************** + +// Acknowledgment (timer functions): ReactOS (GPLv2) +// https://github.com/reactos/reactos + +// KiCheckTimerTable, KiTimerExpiration, KiTimerListExpire +/* +* PROJECT: ReactOS Kernel +* LICENSE: GPL - See COPYING in the top level directory +* FILE: ntoskrnl/ke/dpc.c +* PURPOSE: Deferred Procedure Call (DPC) Support +* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) +* Philip Susi (phreak@iag.net) +* Eric Kohl +*/ + +// KiInsertTreeTimer, KiInsertTimerTable, KiSignalTimer, KiCompleteTimer +/* +* PROJECT: ReactOS Kernel +* LICENSE: GPL - See COPYING in the top level directory +* FILE: ntoskrnl/ke/timerobj.c +* PURPOSE: Handle Kernel Timers (Kernel-part of Executive Timers) +* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) +*/ + +// KiComputeDueTime, KiComputeTimerTableIndex, KxInsertTimer, KxRemoveTreeTimer, KiRemoveEntryTimer +/* +* PROJECT: ReactOS Kernel +* LICENSE: GPL - See COPYING in the top level directory +* FILE: ntoskrnl/include/internal/ke_x.h +* PURPOSE: Internal Inlined Functions for the Kernel +* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) +*/ + +// COPYING file: +/* +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +ReactOS may be used, runtime linked, and distributed with non-free software +(meaning that such software has no obligations to open-source, or render +free, their non-free code) such as commercial device drivers and commercial +applications. This exception does not alter any other responsibilities of +the licensee under the GPL (meaning that such software must still obey +the GPL for the free ("open-sourced") code that has been integrated into +the said software). +*/ + +// Changed from ReactOS: slight changes to the Hand parameter usage #define LOG_PREFIX CXBXR_MODULE::KI @@ -82,562 +82,562 @@ the said software). #include // For KeBugCheck, etc. #include "Logging.h" // For LOG_FUNC() #include "EmuKrnl.h" // for the list support functions -#include "EmuKrnlKi.h" - -#define MAX_TIMER_DPCS 16 - -#define ASSERT_TIMER_LOCKED assert(KiTimerMtx.Acquired > 0) - -const xboxkrnl::ULONG CLOCK_TIME_INCREMENT = 0x2710; -xboxkrnl::KDPC KiTimerExpireDpc; -xboxkrnl::KI_TIMER_LOCK KiTimerMtx; -xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; -xboxkrnl::LIST_ENTRY KiWaitInListHead; - - -xboxkrnl::VOID xboxkrnl::KiInitSystem() -{ - unsigned int i; - - InitializeListHead(&KiWaitInListHead); - - KiTimerMtx.Acquired = 0; - KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL); - for (i = 0; i < TIMER_TABLE_SIZE; i++) { - InitializeListHead(&KiTimerTableListHead[i].Entry); - KiTimerTableListHead[i].Time.u.HighPart = 0xFFFFFFFF; - KiTimerTableListHead[i].Time.u.LowPart = 0; - } - - InitializeListHead(&IdexChannelObject.DeviceQueue.DeviceListHead); -} - -xboxkrnl::VOID xboxkrnl::KiTimerLock() -{ - KiTimerMtx.Mtx.lock(); - KiTimerMtx.Acquired++; -} - -xboxkrnl::VOID xboxkrnl::KiTimerUnlock() -{ - KiTimerMtx.Acquired--; - KiTimerMtx.Mtx.unlock(); -} - -xboxkrnl::VOID xboxkrnl::KiClockIsr -( - unsigned int ScalingFactor -) -{ - KIRQL OldIrql; - LARGE_INTEGER InterruptTime; - LARGE_INTEGER HostSystemTime; - ULONG Hand; - DWORD OldKeTickCount; - - OldIrql = KfRaiseIrql(CLOCK_LEVEL); - - // Update the interrupt time - InterruptTime.u.LowPart = KeInterruptTime.LowPart; - InterruptTime.u.HighPart = KeInterruptTime.High1Time; - InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * ScalingFactor); - KeInterruptTime.High2Time = InterruptTime.u.HighPart; - KeInterruptTime.LowPart = InterruptTime.u.LowPart; - KeInterruptTime.High1Time = InterruptTime.u.HighPart; - - // Update the system time - // NOTE: I'm not sure if we should round down the host system time to the nearest multiple - // of the Xbox clock increment... - GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime); - HostSystemTime.QuadPart += HostSystemTimeDelta.load(); - KeSystemTime.High2Time = HostSystemTime.u.HighPart; - KeSystemTime.LowPart = HostSystemTime.u.LowPart; - KeSystemTime.High1Time = HostSystemTime.u.HighPart; - - // Update the tick counter - OldKeTickCount = KeTickCount; - KeTickCount += ScalingFactor; - - // Because this function must be fast to continuously update the kernel clocks, if somebody else is currently - // holding the lock, we won't wait and instead skip the check of the timers for this cycle - if (KiTimerMtx.Mtx.try_lock()) { - KiTimerMtx.Acquired++; - // Check if a timer has expired - Hand = OldKeTickCount & (TIMER_TABLE_SIZE - 1); - if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry && - (ULONGLONG)InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) { - KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)Hand, 0); - } - KiTimerMtx.Acquired--; - KiTimerMtx.Mtx.unlock(); - } - - KfLowerIrql(OldIrql); -} - -xboxkrnl::VOID NTAPI xboxkrnl::KiCheckTimerTable -( - IN xboxkrnl::ULARGE_INTEGER CurrentTime -) -{ - ULONG i = 0; - PLIST_ENTRY ListHead, NextEntry; - KIRQL OldIrql; - PKTIMER Timer; - - ASSERT_TIMER_LOCKED; - - /* Raise IRQL to high and loop timers */ - OldIrql = KfRaiseIrql(HIGH_LEVEL); - do - { - /* Loop the current list */ - ListHead = &KiTimerTableListHead[i].Entry; - NextEntry = ListHead->Flink; - while (NextEntry != ListHead) - { - /* Get the timer and move to the next one */ - Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); - NextEntry = NextEntry->Flink; - - /* Check if it expired */ - if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) - { - /* This is bad, breakpoint! */ - EmuLog(LOG_LEVEL::ERROR2, "Invalid timer state!"); - DbgBreakPoint(); - } - } - - /* Move to the next timer */ - i++; - } while (i < TIMER_TABLE_SIZE); - - /* Lower IRQL and return */ - KfLowerIrql(OldIrql); -} - -xboxkrnl::VOID xboxkrnl::KxInsertTimer -( - IN xboxkrnl::PKTIMER Timer, - IN xboxkrnl::ULONG Hand -) -{ - ASSERT_TIMER_LOCKED; - - /* Try to insert the timer */ - if (KiInsertTimerTable(Timer, Hand)) - { - /* Complete it */ - KiCompleteTimer(Timer, Hand); - } -} - -xboxkrnl::VOID FASTCALL xboxkrnl::KiCompleteTimer -( - IN xboxkrnl::PKTIMER Timer, - IN xboxkrnl::ULONG Hand -) -{ - LIST_ENTRY ListHead; - BOOLEAN RequestInterrupt = FALSE; - - ASSERT_TIMER_LOCKED; - - /* Remove it from the timer list */ - KiRemoveEntryTimer(Timer, Hand); - - /* Link the timer list to our stack */ - ListHead.Flink = &Timer->TimerListEntry; - ListHead.Blink = &Timer->TimerListEntry; - Timer->TimerListEntry.Flink = &ListHead; - Timer->TimerListEntry.Blink = &ListHead; - - /* Signal the timer if it's still on our list */ - if (!IsListEmpty(&ListHead)) { - RequestInterrupt = KiSignalTimer(Timer); - } - - /* Request a DPC if needed */ - if (RequestInterrupt) { - HalRequestSoftwareInterrupt(DISPATCH_LEVEL); - } -} - -xboxkrnl::VOID xboxkrnl::KiRemoveEntryTimer -( - IN xboxkrnl::PKTIMER Timer, - IN xboxkrnl::ULONG Hand -) -{ - PKTIMER_TABLE_ENTRY TableEntry; - - ASSERT_TIMER_LOCKED; - - /* Remove the timer from the timer list and check if it's empty */ - if (RemoveEntryList(&Timer->TimerListEntry)) - { - /* Get the respective timer table entry */ - TableEntry = &KiTimerTableListHead[Hand]; - if (&TableEntry->Entry == TableEntry->Entry.Flink) - { - /* Set the entry to an infinite absolute time */ - TableEntry->Time.u.HighPart = 0xFFFFFFFF; - } - } - - /* Clear the list entries so we can tell the timer is gone */ - Timer->TimerListEntry.Flink = NULL; - Timer->TimerListEntry.Blink = NULL; -} - -xboxkrnl::VOID xboxkrnl::KxRemoveTreeTimer -( - IN xboxkrnl::PKTIMER Timer -) -{ - ULONG Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); - PKTIMER_TABLE_ENTRY TimerEntry; - - ASSERT_TIMER_LOCKED; - - /* Set the timer as non-inserted */ - Timer->Header.Inserted = FALSE; - - /* Remove it from the timer list */ - if (RemoveEntryList(&Timer->TimerListEntry)) - { - /* Get the entry and check if it's empty */ - TimerEntry = &KiTimerTableListHead[Hand]; - if (IsListEmpty(&TimerEntry->Entry)) - { - /* Clear the time then */ - TimerEntry->Time.u.HighPart = 0xFFFFFFFF; - } - } -} - -xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable -( - IN xboxkrnl::PKTIMER Timer, - IN xboxkrnl::ULONG Hand -) -{ - LARGE_INTEGER InterruptTime; - LONGLONG DueTime = Timer->DueTime.QuadPart; - BOOLEAN Expired = FALSE; - PLIST_ENTRY ListHead, NextEntry; - PKTIMER CurrentTimer; - - EmuLog(LOG_LEVEL::DEBUG, "%s: inserting Timer %p, Hand: %lu", __func__, Timer, Hand); - - ASSERT_TIMER_LOCKED; - - /* Check if the period is zero */ - if (!Timer->Period) { - Timer->Header.SignalState = FALSE; - } - - /* Loop the timer list backwards */ - ListHead = &KiTimerTableListHead[Hand].Entry; - NextEntry = ListHead->Blink; - while (NextEntry != ListHead) - { - /* Get the timer */ - CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); - - /* Now check if we can fit it before */ - if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break; - - /* Keep looping */ - NextEntry = NextEntry->Blink; - } - - /* Looped all the list, insert it here and get the interrupt time again */ - InsertHeadList(NextEntry, &Timer->TimerListEntry); - - /* Check if we didn't find it in the list */ - if (NextEntry == ListHead) - { - /* Set the time */ - KiTimerTableListHead[Hand].Time.QuadPart = DueTime; - - /* Make sure it hasn't expired already */ - InterruptTime.QuadPart = KeQueryInterruptTime(); - if (DueTime <= InterruptTime.QuadPart) { - EmuLog(LOG_LEVEL::DEBUG, "Timer %p already expired", Timer); - Expired = TRUE; - } - } - - /* Return expired state */ - return Expired; -} - -xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer -( - IN xboxkrnl::PKTIMER Timer, - IN xboxkrnl::LARGE_INTEGER Interval -) -{ - BOOLEAN Inserted = FALSE; - ULONG Hand = 0; - - ASSERT_TIMER_LOCKED; - - /* Setup the timer's due time */ - if (KiComputeDueTime(Timer, Interval, &Hand)) - { - /* Insert the timer */ - if (KiInsertTimerTable(Timer, Hand)) - { - /* It was already there, remove it */ - KiRemoveEntryTimer(Timer, Hand); - Timer->Header.Inserted = FALSE; - } - else - { - /* Otherwise, we're now inserted */ - Inserted = TRUE; - } - } - - return Inserted; -} - -xboxkrnl::ULONG xboxkrnl::KiComputeTimerTableIndex -( - IN xboxkrnl::ULONGLONG Interval -) -{ - return (Interval / CLOCK_TIME_INCREMENT) & (TIMER_TABLE_SIZE - 1); -} - -xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime -( - IN xboxkrnl::PKTIMER Timer, - IN xboxkrnl::LARGE_INTEGER DueTime, - OUT xboxkrnl::PULONG Hand) -{ - LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime; - - ASSERT_TIMER_LOCKED; - - /* Convert to relative time if needed */ - Timer->Header.Absolute = FALSE; - if (DueTime.u.HighPart >= 0) - { - /* Get System Time */ - KeQuerySystemTime(&SystemTime); - - /* Do the conversion */ - DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart; - - /* Make sure it hasn't already expired */ - Timer->Header.Absolute = TRUE; - if (DifferenceTime.u.HighPart >= 0) - { - /* Cancel everything */ - EmuLog(LOG_LEVEL::DEBUG, "Timer %p already expired", Timer); - Timer->Header.SignalState = TRUE; - Timer->DueTime.QuadPart = 0; - *Hand = 0; - return FALSE; - } - - /* Set the time as Absolute */ - DueTime = DifferenceTime; - } - - /* Get the Interrupt Time */ - InterruptTime.QuadPart = KeQueryInterruptTime(); - - /* Recalculate due time */ - Timer->DueTime.QuadPart = InterruptTime.QuadPart - DueTime.QuadPart; - - /* Get the handle */ - *Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); - Timer->Header.Inserted = TRUE; - - return TRUE; -} - -xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiSignalTimer -( - IN xboxkrnl::PKTIMER Timer -) -{ - BOOLEAN RequestInterrupt = FALSE; - PKDPC Dpc = Timer->Dpc; - ULONG Period = Timer->Period; - LARGE_INTEGER Interval, SystemTime; - - ASSERT_TIMER_LOCKED; - - /* Set default values */ - Timer->Header.Inserted = FALSE; - Timer->Header.SignalState = TRUE; - - /* Check if the timer has waiters */ - if (!IsListEmpty(&Timer->Header.WaitListHead)) - { - /* Check the type of event */ - if (Timer->Header.Type == TimerNotificationObject) - { - /* Unwait the thread */ - // KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); - } - else - { - /* Otherwise unwait the thread and signal the timer */ - // KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); - } - } - - /* Check if we have a period */ - if (Period) - { - /* Calculate the interval and insert the timer */ - Interval.QuadPart = Int32x32To64(Period, -10000); - while (!KiInsertTreeTimer(Timer, Interval)); - } - - /* Check if we have a DPC */ - if (Dpc) - { - /* Insert it in the queue */ - KeQuerySystemTime(&SystemTime); - KeInsertQueueDpc(Dpc, - ULongToPtr(SystemTime.u.LowPart), - ULongToPtr(SystemTime.u.HighPart)); - RequestInterrupt = TRUE; - } - - /* Return whether we need to request a DPC interrupt or not */ - return RequestInterrupt; -} - -xboxkrnl::VOID NTAPI xboxkrnl::KiTimerExpiration -( - IN xboxkrnl::PKDPC Dpc, - IN xboxkrnl::PVOID DeferredContext, - IN xboxkrnl::PVOID SystemArgument1, - IN xboxkrnl::PVOID SystemArgument2 -) -{ - ULARGE_INTEGER SystemTime, InterruptTime; - LARGE_INTEGER Interval; - LONG Limit, Index, i; - ULONG Timers, ActiveTimers, DpcCalls; - PLIST_ENTRY ListHead, NextEntry; - KIRQL OldIrql; - PKTIMER Timer; - PKDPC TimerDpc; - ULONG Period; - DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; - - /* Query system and interrupt time */ - KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); - InterruptTime.QuadPart = KeQueryInterruptTime(); - Limit = KeTickCount; - - /* Get the index of the timer and normalize it */ - Index = PtrToLong(SystemArgument1); - if ((Limit - Index) >= TIMER_TABLE_SIZE) - { - /* Normalize it */ - Limit = Index + TIMER_TABLE_SIZE - 1; - } - - /* Setup index and actual limit */ - Index--; - Limit &= (TIMER_TABLE_SIZE - 1); - - /* Setup accounting data */ - DpcCalls = 0; - Timers = 24; - ActiveTimers = 4; - - /* Lock the Database and Raise IRQL */ - KiTimerLock(); - KiLockDispatcherDatabase(&OldIrql); - - /* Start expiration loop */ - do - { - /* Get the current index */ - Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); - - /* Get list pointers and loop the list */ - ListHead = &KiTimerTableListHead[Index].Entry; - while (ListHead != ListHead->Flink) - { - /* Go to the next entry */ - NextEntry = ListHead->Flink; - - /* Get the current timer and check its due time */ - Timers--; - Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); - if ((NextEntry != ListHead) && - (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) - { - /* It's expired, remove it */ - ActiveTimers--; - KiRemoveEntryTimer(Timer, Index); - - /* Make it non-inserted and signal it */ - Timer->Header.Inserted = FALSE; - Timer->Header.SignalState = 1; - - /* Get the DPC and period */ - TimerDpc = Timer->Dpc; - Period = Timer->Period; - - /* Check if there are any waiters */ - if (!IsListEmpty(&Timer->Header.WaitListHead)) - { - /* Check the type of event */ - if (Timer->Header.Type == TimerNotificationObject) - { - /* Unwait the thread */ - // KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); - } - else - { - /* Otherwise unwait the thread and signal the timer */ - // KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); - } - } - - /* Check if we have a period */ - if (Period) - { - /* Calculate the interval and insert the timer */ - Interval.QuadPart = Int32x32To64(Period, -10000); - while (!KiInsertTreeTimer(Timer, Interval)); - } - - /* Check if we have a DPC */ - if (TimerDpc) - { - /* Setup the DPC Entry */ - DpcEntry[DpcCalls].Dpc = TimerDpc; - DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; - DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; - DpcCalls++; - assert(DpcCalls < MAX_TIMER_DPCS); - } - - /* Check if we're done processing */ - if (!(ActiveTimers) || !(Timers)) - { - /* Release the dispatcher while doing DPCs */ - KiUnlockDispatcherDatabase(DISPATCH_LEVEL); - - /* Start looping all DPC Entries */ - for (i = 0; DpcCalls; DpcCalls--, i++) - { - /* Call the DPC */ +#include "EmuKrnlKi.h" + +#define MAX_TIMER_DPCS 16 + +#define ASSERT_TIMER_LOCKED assert(KiTimerMtx.Acquired > 0) + +const xboxkrnl::ULONG CLOCK_TIME_INCREMENT = 0x2710; +xboxkrnl::KDPC KiTimerExpireDpc; +xboxkrnl::KI_TIMER_LOCK KiTimerMtx; +xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; +xboxkrnl::LIST_ENTRY KiWaitInListHead; + + +xboxkrnl::VOID xboxkrnl::KiInitSystem() +{ + unsigned int i; + + InitializeListHead(&KiWaitInListHead); + + KiTimerMtx.Acquired = 0; + KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL); + for (i = 0; i < TIMER_TABLE_SIZE; i++) { + InitializeListHead(&KiTimerTableListHead[i].Entry); + KiTimerTableListHead[i].Time.u.HighPart = 0xFFFFFFFF; + KiTimerTableListHead[i].Time.u.LowPart = 0; + } + + InitializeListHead(&IdexChannelObject.DeviceQueue.DeviceListHead); +} + +xboxkrnl::VOID xboxkrnl::KiTimerLock() +{ + KiTimerMtx.Mtx.lock(); + KiTimerMtx.Acquired++; +} + +xboxkrnl::VOID xboxkrnl::KiTimerUnlock() +{ + KiTimerMtx.Acquired--; + KiTimerMtx.Mtx.unlock(); +} + +xboxkrnl::VOID xboxkrnl::KiClockIsr +( + unsigned int ScalingFactor +) +{ + KIRQL OldIrql; + LARGE_INTEGER InterruptTime; + LARGE_INTEGER HostSystemTime; + ULONG Hand; + DWORD OldKeTickCount; + + OldIrql = KfRaiseIrql(CLOCK_LEVEL); + + // Update the interrupt time + InterruptTime.u.LowPart = KeInterruptTime.LowPart; + InterruptTime.u.HighPart = KeInterruptTime.High1Time; + InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * ScalingFactor); + KeInterruptTime.High2Time = InterruptTime.u.HighPart; + KeInterruptTime.LowPart = InterruptTime.u.LowPart; + KeInterruptTime.High1Time = InterruptTime.u.HighPart; + + // Update the system time + // NOTE: I'm not sure if we should round down the host system time to the nearest multiple + // of the Xbox clock increment... + GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime); + HostSystemTime.QuadPart += HostSystemTimeDelta.load(); + KeSystemTime.High2Time = HostSystemTime.u.HighPart; + KeSystemTime.LowPart = HostSystemTime.u.LowPart; + KeSystemTime.High1Time = HostSystemTime.u.HighPart; + + // Update the tick counter + OldKeTickCount = KeTickCount; + KeTickCount += ScalingFactor; + + // Because this function must be fast to continuously update the kernel clocks, if somebody else is currently + // holding the lock, we won't wait and instead skip the check of the timers for this cycle + if (KiTimerMtx.Mtx.try_lock()) { + KiTimerMtx.Acquired++; + // Check if a timer has expired + Hand = OldKeTickCount & (TIMER_TABLE_SIZE - 1); + if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry && + (ULONGLONG)InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) { + KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)Hand, 0); + } + KiTimerMtx.Acquired--; + KiTimerMtx.Mtx.unlock(); + } + + KfLowerIrql(OldIrql); +} + +xboxkrnl::VOID NTAPI xboxkrnl::KiCheckTimerTable +( + IN xboxkrnl::ULARGE_INTEGER CurrentTime +) +{ + ULONG i = 0; + PLIST_ENTRY ListHead, NextEntry; + KIRQL OldIrql; + PKTIMER Timer; + + ASSERT_TIMER_LOCKED; + + /* Raise IRQL to high and loop timers */ + OldIrql = KfRaiseIrql(HIGH_LEVEL); + do + { + /* Loop the current list */ + ListHead = &KiTimerTableListHead[i].Entry; + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) + { + /* Get the timer and move to the next one */ + Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); + NextEntry = NextEntry->Flink; + + /* Check if it expired */ + if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) + { + /* This is bad, breakpoint! */ + EmuLog(LOG_LEVEL::ERROR2, "Invalid timer state!"); + DbgBreakPoint(); + } + } + + /* Move to the next timer */ + i++; + } while (i < TIMER_TABLE_SIZE); + + /* Lower IRQL and return */ + KfLowerIrql(OldIrql); +} + +xboxkrnl::VOID xboxkrnl::KxInsertTimer +( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + ASSERT_TIMER_LOCKED; + + /* Try to insert the timer */ + if (KiInsertTimerTable(Timer, Hand)) + { + /* Complete it */ + KiCompleteTimer(Timer, Hand); + } +} + +xboxkrnl::VOID FASTCALL xboxkrnl::KiCompleteTimer +( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + LIST_ENTRY ListHead; + BOOLEAN RequestInterrupt = FALSE; + + ASSERT_TIMER_LOCKED; + + /* Remove it from the timer list */ + KiRemoveEntryTimer(Timer, Hand); + + /* Link the timer list to our stack */ + ListHead.Flink = &Timer->TimerListEntry; + ListHead.Blink = &Timer->TimerListEntry; + Timer->TimerListEntry.Flink = &ListHead; + Timer->TimerListEntry.Blink = &ListHead; + + /* Signal the timer if it's still on our list */ + if (!IsListEmpty(&ListHead)) { + RequestInterrupt = KiSignalTimer(Timer); + } + + /* Request a DPC if needed */ + if (RequestInterrupt) { + HalRequestSoftwareInterrupt(DISPATCH_LEVEL); + } +} + +xboxkrnl::VOID xboxkrnl::KiRemoveEntryTimer +( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + PKTIMER_TABLE_ENTRY TableEntry; + + ASSERT_TIMER_LOCKED; + + /* Remove the timer from the timer list and check if it's empty */ + if (RemoveEntryList(&Timer->TimerListEntry)) + { + /* Get the respective timer table entry */ + TableEntry = &KiTimerTableListHead[Hand]; + if (&TableEntry->Entry == TableEntry->Entry.Flink) + { + /* Set the entry to an infinite absolute time */ + TableEntry->Time.u.HighPart = 0xFFFFFFFF; + } + } + + /* Clear the list entries so we can tell the timer is gone */ + Timer->TimerListEntry.Flink = NULL; + Timer->TimerListEntry.Blink = NULL; +} + +xboxkrnl::VOID xboxkrnl::KxRemoveTreeTimer +( + IN xboxkrnl::PKTIMER Timer +) +{ + ULONG Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); + PKTIMER_TABLE_ENTRY TimerEntry; + + ASSERT_TIMER_LOCKED; + + /* Set the timer as non-inserted */ + Timer->Header.Inserted = FALSE; + + /* Remove it from the timer list */ + if (RemoveEntryList(&Timer->TimerListEntry)) + { + /* Get the entry and check if it's empty */ + TimerEntry = &KiTimerTableListHead[Hand]; + if (IsListEmpty(&TimerEntry->Entry)) + { + /* Clear the time then */ + TimerEntry->Time.u.HighPart = 0xFFFFFFFF; + } + } +} + +xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable +( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::ULONG Hand +) +{ + LARGE_INTEGER InterruptTime; + LONGLONG DueTime = Timer->DueTime.QuadPart; + BOOLEAN Expired = FALSE; + PLIST_ENTRY ListHead, NextEntry; + PKTIMER CurrentTimer; + + EmuLog(LOG_LEVEL::DEBUG, "%s: inserting Timer %p, Hand: %lu", __func__, Timer, Hand); + + ASSERT_TIMER_LOCKED; + + /* Check if the period is zero */ + if (!Timer->Period) { + Timer->Header.SignalState = FALSE; + } + + /* Loop the timer list backwards */ + ListHead = &KiTimerTableListHead[Hand].Entry; + NextEntry = ListHead->Blink; + while (NextEntry != ListHead) + { + /* Get the timer */ + CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); + + /* Now check if we can fit it before */ + if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break; + + /* Keep looping */ + NextEntry = NextEntry->Blink; + } + + /* Looped all the list, insert it here and get the interrupt time again */ + InsertHeadList(NextEntry, &Timer->TimerListEntry); + + /* Check if we didn't find it in the list */ + if (NextEntry == ListHead) + { + /* Set the time */ + KiTimerTableListHead[Hand].Time.QuadPart = DueTime; + + /* Make sure it hasn't expired already */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + if (DueTime <= InterruptTime.QuadPart) { + EmuLog(LOG_LEVEL::DEBUG, "Timer %p already expired", Timer); + Expired = TRUE; + } + } + + /* Return expired state */ + return Expired; +} + +xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer +( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::LARGE_INTEGER Interval +) +{ + BOOLEAN Inserted = FALSE; + ULONG Hand = 0; + + ASSERT_TIMER_LOCKED; + + /* Setup the timer's due time */ + if (KiComputeDueTime(Timer, Interval, &Hand)) + { + /* Insert the timer */ + if (KiInsertTimerTable(Timer, Hand)) + { + /* It was already there, remove it */ + KiRemoveEntryTimer(Timer, Hand); + Timer->Header.Inserted = FALSE; + } + else + { + /* Otherwise, we're now inserted */ + Inserted = TRUE; + } + } + + return Inserted; +} + +xboxkrnl::ULONG xboxkrnl::KiComputeTimerTableIndex +( + IN xboxkrnl::ULONGLONG Interval +) +{ + return (Interval / CLOCK_TIME_INCREMENT) & (TIMER_TABLE_SIZE - 1); +} + +xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime +( + IN xboxkrnl::PKTIMER Timer, + IN xboxkrnl::LARGE_INTEGER DueTime, + OUT xboxkrnl::PULONG Hand) +{ + LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime; + + ASSERT_TIMER_LOCKED; + + /* Convert to relative time if needed */ + Timer->Header.Absolute = FALSE; + if (DueTime.u.HighPart >= 0) + { + /* Get System Time */ + KeQuerySystemTime(&SystemTime); + + /* Do the conversion */ + DifferenceTime.QuadPart = SystemTime.QuadPart - DueTime.QuadPart; + + /* Make sure it hasn't already expired */ + Timer->Header.Absolute = TRUE; + if (DifferenceTime.u.HighPart >= 0) + { + /* Cancel everything */ + EmuLog(LOG_LEVEL::DEBUG, "Timer %p already expired", Timer); + Timer->Header.SignalState = TRUE; + Timer->DueTime.QuadPart = 0; + *Hand = 0; + return FALSE; + } + + /* Set the time as Absolute */ + DueTime = DifferenceTime; + } + + /* Get the Interrupt Time */ + InterruptTime.QuadPart = KeQueryInterruptTime(); + + /* Recalculate due time */ + Timer->DueTime.QuadPart = InterruptTime.QuadPart - DueTime.QuadPart; + + /* Get the handle */ + *Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); + Timer->Header.Inserted = TRUE; + + return TRUE; +} + +xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiSignalTimer +( + IN xboxkrnl::PKTIMER Timer +) +{ + BOOLEAN RequestInterrupt = FALSE; + PKDPC Dpc = Timer->Dpc; + ULONG Period = Timer->Period; + LARGE_INTEGER Interval, SystemTime; + + ASSERT_TIMER_LOCKED; + + /* Set default values */ + Timer->Header.Inserted = FALSE; + Timer->Header.SignalState = TRUE; + + /* Check if the timer has waiters */ + if (!IsListEmpty(&Timer->Header.WaitListHead)) + { + /* Check the type of event */ + if (Timer->Header.Type == TimerNotificationObject) + { + /* Unwait the thread */ + // KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); + } + else + { + /* Otherwise unwait the thread and signal the timer */ + // KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); + } + } + + /* Check if we have a period */ + if (Period) + { + /* Calculate the interval and insert the timer */ + Interval.QuadPart = Int32x32To64(Period, -10000); + while (!KiInsertTreeTimer(Timer, Interval)); + } + + /* Check if we have a DPC */ + if (Dpc) + { + /* Insert it in the queue */ + KeQuerySystemTime(&SystemTime); + KeInsertQueueDpc(Dpc, + ULongToPtr(SystemTime.u.LowPart), + ULongToPtr(SystemTime.u.HighPart)); + RequestInterrupt = TRUE; + } + + /* Return whether we need to request a DPC interrupt or not */ + return RequestInterrupt; +} + +xboxkrnl::VOID NTAPI xboxkrnl::KiTimerExpiration +( + IN xboxkrnl::PKDPC Dpc, + IN xboxkrnl::PVOID DeferredContext, + IN xboxkrnl::PVOID SystemArgument1, + IN xboxkrnl::PVOID SystemArgument2 +) +{ + ULARGE_INTEGER SystemTime, InterruptTime; + LARGE_INTEGER Interval; + LONG Limit, Index, i; + ULONG Timers, ActiveTimers, DpcCalls; + PLIST_ENTRY ListHead, NextEntry; + KIRQL OldIrql; + PKTIMER Timer; + PKDPC TimerDpc; + ULONG Period; + DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; + + /* Query system and interrupt time */ + KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); + InterruptTime.QuadPart = KeQueryInterruptTime(); + Limit = KeTickCount; + + /* Get the index of the timer and normalize it */ + Index = PtrToLong(SystemArgument1); + if ((Limit - Index) >= TIMER_TABLE_SIZE) + { + /* Normalize it */ + Limit = Index + TIMER_TABLE_SIZE - 1; + } + + /* Setup index and actual limit */ + Index--; + Limit &= (TIMER_TABLE_SIZE - 1); + + /* Setup accounting data */ + DpcCalls = 0; + Timers = 24; + ActiveTimers = 4; + + /* Lock the Database and Raise IRQL */ + KiTimerLock(); + KiLockDispatcherDatabase(&OldIrql); + + /* Start expiration loop */ + do + { + /* Get the current index */ + Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); + + /* Get list pointers and loop the list */ + ListHead = &KiTimerTableListHead[Index].Entry; + while (ListHead != ListHead->Flink) + { + /* Go to the next entry */ + NextEntry = ListHead->Flink; + + /* Get the current timer and check its due time */ + Timers--; + Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); + if ((NextEntry != ListHead) && + (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) + { + /* It's expired, remove it */ + ActiveTimers--; + KiRemoveEntryTimer(Timer, Index); + + /* Make it non-inserted and signal it */ + Timer->Header.Inserted = FALSE; + Timer->Header.SignalState = 1; + + /* Get the DPC and period */ + TimerDpc = Timer->Dpc; + Period = Timer->Period; + + /* Check if there are any waiters */ + if (!IsListEmpty(&Timer->Header.WaitListHead)) + { + /* Check the type of event */ + if (Timer->Header.Type == TimerNotificationObject) + { + /* Unwait the thread */ + // KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); + } + else + { + /* Otherwise unwait the thread and signal the timer */ + // KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); + } + } + + /* Check if we have a period */ + if (Period) + { + /* Calculate the interval and insert the timer */ + Interval.QuadPart = Int32x32To64(Period, -10000); + while (!KiInsertTreeTimer(Timer, Interval)); + } + + /* Check if we have a DPC */ + if (TimerDpc) + { + /* Setup the DPC Entry */ + DpcEntry[DpcCalls].Dpc = TimerDpc; + DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; + DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; + DpcCalls++; + assert(DpcCalls < MAX_TIMER_DPCS); + } + + /* Check if we're done processing */ + if (!(ActiveTimers) || !(Timers)) + { + /* Release the dispatcher while doing DPCs */ + KiUnlockDispatcherDatabase(DISPATCH_LEVEL); + + /* Start looping all DPC Entries */ + for (i = 0; DpcCalls; DpcCalls--, i++) + { + /* Call the DPC */ EmuLog(LOG_LEVEL::DEBUG, "%s, calling DPC at 0x%.8X", __func__, DpcEntry[i].Routine); // Call the Deferred Procedure : @@ -647,40 +647,40 @@ xboxkrnl::VOID NTAPI xboxkrnl::KiTimerExpiration UlongToPtr(SystemTime.u.LowPart), UlongToPtr(SystemTime.u.HighPart) ); - } - - /* Reset accounting */ - Timers = 24; - ActiveTimers = 4; - - /* Lock the dispatcher database */ - KiLockDispatcherDatabaseAtDpcLevel(); - } - } - else - { - /* Check if the timer list is empty */ - if (NextEntry != ListHead) - { - /* Sanity check */ - assert(KiTimerTableListHead[Index].Time.QuadPart <= - Timer->DueTime.QuadPart); - - /* Update the time */ - KiTimerTableListHead[Index].Time.QuadPart = - Timer->DueTime.QuadPart; - } - - /* Check if we've scanned all the timers we could */ - if (!Timers) - { - /* Release the dispatcher while doing DPCs */ - KiUnlockDispatcherDatabase(DISPATCH_LEVEL); - - /* Start looping all DPC Entries */ - for (i = 0; DpcCalls; DpcCalls--, i++) - { - /* Call the DPC */ + } + + /* Reset accounting */ + Timers = 24; + ActiveTimers = 4; + + /* Lock the dispatcher database */ + KiLockDispatcherDatabaseAtDpcLevel(); + } + } + else + { + /* Check if the timer list is empty */ + if (NextEntry != ListHead) + { + /* Sanity check */ + assert(KiTimerTableListHead[Index].Time.QuadPart <= + Timer->DueTime.QuadPart); + + /* Update the time */ + KiTimerTableListHead[Index].Time.QuadPart = + Timer->DueTime.QuadPart; + } + + /* Check if we've scanned all the timers we could */ + if (!Timers) + { + /* Release the dispatcher while doing DPCs */ + KiUnlockDispatcherDatabase(DISPATCH_LEVEL); + + /* Start looping all DPC Entries */ + for (i = 0; DpcCalls; DpcCalls--, i++) + { + /* Call the DPC */ EmuLog(LOG_LEVEL::DEBUG, "%s, calling DPC at 0x%.8X", __func__, DpcEntry[i].Routine); // Call the Deferred Procedure : @@ -688,39 +688,39 @@ xboxkrnl::VOID NTAPI xboxkrnl::KiTimerExpiration DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.u.LowPart), - UlongToPtr(SystemTime.u.HighPart) + UlongToPtr(SystemTime.u.HighPart) ); - } - - /* Reset accounting */ - Timers = 24; - ActiveTimers = 4; - - /* Lock the dispatcher database */ - KiLockDispatcherDatabaseAtDpcLevel(); - } - - /* Done looping */ - break; - } - } - } while (Index != Limit); - - /* Verify the timer table, on a debug kernel only */ - if (g_bIsDebugKernel) { - KiCheckTimerTable(InterruptTime); - } - - /* Check if we still have DPC entries */ - if (DpcCalls) - { - /* Release the dispatcher while doing DPCs */ - KiUnlockDispatcherDatabase(DISPATCH_LEVEL); - - /* Start looping all DPC Entries */ - for (i = 0; DpcCalls; DpcCalls--, i++) - { - /* Call the DPC */ + } + + /* Reset accounting */ + Timers = 24; + ActiveTimers = 4; + + /* Lock the dispatcher database */ + KiLockDispatcherDatabaseAtDpcLevel(); + } + + /* Done looping */ + break; + } + } + } while (Index != Limit); + + /* Verify the timer table, on a debug kernel only */ + if (g_bIsDebugKernel) { + KiCheckTimerTable(InterruptTime); + } + + /* Check if we still have DPC entries */ + if (DpcCalls) + { + /* Release the dispatcher while doing DPCs */ + KiUnlockDispatcherDatabase(DISPATCH_LEVEL); + + /* Start looping all DPC Entries */ + for (i = 0; DpcCalls; DpcCalls--, i++) + { + /* Call the DPC */ EmuLog(LOG_LEVEL::DEBUG, "%s, calling DPC at 0x%.8X", __func__, DpcEntry[i].Routine); // Call the Deferred Procedure : @@ -730,107 +730,107 @@ xboxkrnl::VOID NTAPI xboxkrnl::KiTimerExpiration UlongToPtr(SystemTime.u.LowPart), UlongToPtr(SystemTime.u.HighPart) ); - } - - /* Lower IRQL if we need to */ - if (OldIrql != DISPATCH_LEVEL) { - KfLowerIrql(OldIrql); - } - KiTimerUnlock(); - } - else - { - /* Unlock the dispatcher */ - KiUnlockDispatcherDatabase(OldIrql); - KiTimerUnlock(); - } -} - -xboxkrnl::VOID FASTCALL xboxkrnl::KiTimerListExpire -( - IN xboxkrnl::PLIST_ENTRY ExpiredListHead, - IN xboxkrnl::KIRQL OldIrql -) -{ - ULARGE_INTEGER SystemTime; - LARGE_INTEGER Interval; - LONG i; - ULONG DpcCalls = 0; - PKTIMER Timer; - PKDPC TimerDpc; - ULONG Period; - DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; - - ASSERT_TIMER_LOCKED; - - /* Query system */ - KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); - - /* Loop expired list */ - while (ExpiredListHead->Flink != ExpiredListHead) - { - /* Get the current timer */ - Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); - - /* Remove it */ - RemoveEntryList(&Timer->TimerListEntry); - - /* Not inserted */ - Timer->Header.Inserted = FALSE; - - /* Signal it */ - Timer->Header.SignalState = 1; - - /* Get the DPC and period */ - TimerDpc = Timer->Dpc; - Period = Timer->Period; - - /* Check if there's any waiters */ - if (!IsListEmpty(&Timer->Header.WaitListHead)) - { - /* Check the type of event */ - if (Timer->Header.Type == TimerNotificationObject) - { - /* Unwait the thread */ - // KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); - } - else - { - /* Otherwise unwait the thread and signal the timer */ - // KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); - } - } - - /* Check if we have a period */ - if (Period) - { - /* Calculate the interval and insert the timer */ - Interval.QuadPart = Int32x32To64(Period, -10000); - while (!KiInsertTreeTimer(Timer, Interval)); - } - - /* Check if we have a DPC */ - if (TimerDpc) - { - /* Setup the DPC Entry */ - DpcEntry[DpcCalls].Dpc = TimerDpc; - DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; - DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; - DpcCalls++; - assert(DpcCalls < MAX_TIMER_DPCS); - } - } - - /* Check if we still have DPC entries */ - if (DpcCalls) - { - /* Release the dispatcher while doing DPCs */ - KiUnlockDispatcherDatabase(DISPATCH_LEVEL); - - /* Start looping all DPC Entries */ - for (i = 0; DpcCalls; DpcCalls--, i++) - { - /* Call the DPC */ + } + + /* Lower IRQL if we need to */ + if (OldIrql != DISPATCH_LEVEL) { + KfLowerIrql(OldIrql); + } + KiTimerUnlock(); + } + else + { + /* Unlock the dispatcher */ + KiUnlockDispatcherDatabase(OldIrql); + KiTimerUnlock(); + } +} + +xboxkrnl::VOID FASTCALL xboxkrnl::KiTimerListExpire +( + IN xboxkrnl::PLIST_ENTRY ExpiredListHead, + IN xboxkrnl::KIRQL OldIrql +) +{ + ULARGE_INTEGER SystemTime; + LARGE_INTEGER Interval; + LONG i; + ULONG DpcCalls = 0; + PKTIMER Timer; + PKDPC TimerDpc; + ULONG Period; + DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; + + ASSERT_TIMER_LOCKED; + + /* Query system */ + KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); + + /* Loop expired list */ + while (ExpiredListHead->Flink != ExpiredListHead) + { + /* Get the current timer */ + Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); + + /* Remove it */ + RemoveEntryList(&Timer->TimerListEntry); + + /* Not inserted */ + Timer->Header.Inserted = FALSE; + + /* Signal it */ + Timer->Header.SignalState = 1; + + /* Get the DPC and period */ + TimerDpc = Timer->Dpc; + Period = Timer->Period; + + /* Check if there's any waiters */ + if (!IsListEmpty(&Timer->Header.WaitListHead)) + { + /* Check the type of event */ + if (Timer->Header.Type == TimerNotificationObject) + { + /* Unwait the thread */ + // KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); + } + else + { + /* Otherwise unwait the thread and signal the timer */ + // KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); + } + } + + /* Check if we have a period */ + if (Period) + { + /* Calculate the interval and insert the timer */ + Interval.QuadPart = Int32x32To64(Period, -10000); + while (!KiInsertTreeTimer(Timer, Interval)); + } + + /* Check if we have a DPC */ + if (TimerDpc) + { + /* Setup the DPC Entry */ + DpcEntry[DpcCalls].Dpc = TimerDpc; + DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; + DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; + DpcCalls++; + assert(DpcCalls < MAX_TIMER_DPCS); + } + } + + /* Check if we still have DPC entries */ + if (DpcCalls) + { + /* Release the dispatcher while doing DPCs */ + KiUnlockDispatcherDatabase(DISPATCH_LEVEL); + + /* Start looping all DPC Entries */ + for (i = 0; DpcCalls; DpcCalls--, i++) + { + /* Call the DPC */ EmuLog(LOG_LEVEL::DEBUG, "%s, calling DPC at 0x%.8X", __func__, DpcEntry[i].Routine); // Call the Deferred Procedure : @@ -840,20 +840,20 @@ xboxkrnl::VOID FASTCALL xboxkrnl::KiTimerListExpire UlongToPtr(SystemTime.u.LowPart), UlongToPtr(SystemTime.u.HighPart) ); - } - - /* Lower IRQL */ - KfLowerIrql(OldIrql); - KiTimerUnlock(); - } - else - { - /* Unlock the dispatcher */ - KiUnlockDispatcherDatabase(OldIrql); - KiTimerUnlock(); - } -} - + } + + /* Lower IRQL */ + KfLowerIrql(OldIrql); + KiTimerUnlock(); + } + else + { + /* Unlock the dispatcher */ + KiUnlockDispatcherDatabase(OldIrql); + KiTimerUnlock(); + } +} + xboxkrnl::VOID FASTCALL xboxkrnl::KiWaitSatisfyAll ( IN xboxkrnl::PKWAIT_BLOCK WaitBlock @@ -875,4 +875,4 @@ xboxkrnl::VOID FASTCALL xboxkrnl::KiWaitSatisfyAll } while (WaitBlock1 != WaitBlock); return; -} +} diff --git a/src/core/kernel/exports/EmuKrnlKi.h b/src/core/kernel/exports/EmuKrnlKi.h index f6e25e869..146ebb2e6 100644 --- a/src/core/kernel/exports/EmuKrnlKi.h +++ b/src/core/kernel/exports/EmuKrnlKi.h @@ -17,138 +17,138 @@ // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * -// * (c) 2018 Patrick van Logchem +// * (c) 2018 Patrick van Logchem // * (c) 2019 ergo720 // * // * All rights reserved // * -// ****************************************************************** +// ****************************************************************** -#pragma once - -#include - -// Workaround to avoid collisions with the VOID provided by Windows and the one of xboxkrnl -#ifdef VOID -#undef VOID -#endif - -// ReactOS uses a size of 512, but disassembling the kernel reveals it to be 32 instead -#define TIMER_TABLE_SIZE 32 - -namespace xboxkrnl -{ - typedef struct _KTIMER_TABLE_ENTRY - { - LIST_ENTRY Entry; - ULARGE_INTEGER Time; - } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; - - // Actually, this lock isn't required, but because raising the irql to dpc level doesn't really prevent thread switching at the - // moment, we need it for now to prevent concurrent access to the timer table - typedef struct _KI_TIMER_LOCK - { - std::recursive_mutex Mtx; - int Acquired; - } KI_TIMER_LOCK; - - - VOID KiInitSystem(); - - VOID KiTimerLock(); - - VOID KiTimerUnlock(); - - VOID KiClockIsr - ( - IN unsigned int ScalingFactor - ); - - VOID NTAPI KiCheckTimerTable - ( - IN ULARGE_INTEGER CurrentTime - ); - - VOID KxInsertTimer - ( - IN PKTIMER Timer, - IN ULONG Hand - ); - - VOID FASTCALL KiCompleteTimer - ( - IN PKTIMER Timer, - IN ULONG Hand - ); - - VOID KiRemoveEntryTimer - ( - IN PKTIMER Timer, - IN ULONG Hand +#pragma once + +#include + +// Workaround to avoid collisions with the VOID provided by Windows and the one of xboxkrnl +#ifdef VOID +#undef VOID +#endif + +// ReactOS uses a size of 512, but disassembling the kernel reveals it to be 32 instead +#define TIMER_TABLE_SIZE 32 + +namespace xboxkrnl +{ + typedef struct _KTIMER_TABLE_ENTRY + { + LIST_ENTRY Entry; + ULARGE_INTEGER Time; + } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; + + // Actually, this lock isn't required, but because raising the irql to dpc level doesn't really prevent thread switching at the + // moment, we need it for now to prevent concurrent access to the timer table + typedef struct _KI_TIMER_LOCK + { + std::recursive_mutex Mtx; + int Acquired; + } KI_TIMER_LOCK; + + + VOID KiInitSystem(); + + VOID KiTimerLock(); + + VOID KiTimerUnlock(); + + VOID KiClockIsr + ( + IN unsigned int ScalingFactor ); - - VOID KxRemoveTreeTimer - ( - IN PKTIMER Timer - ); - - BOOLEAN FASTCALL KiInsertTimerTable - ( - IN PKTIMER Timer, - IN ULONG Hand - ); - BOOLEAN FASTCALL KiInsertTreeTimer + VOID NTAPI KiCheckTimerTable + ( + IN ULARGE_INTEGER CurrentTime + ); + + VOID KxInsertTimer + ( + IN PKTIMER Timer, + IN ULONG Hand + ); + + VOID FASTCALL KiCompleteTimer + ( + IN PKTIMER Timer, + IN ULONG Hand + ); + + VOID KiRemoveEntryTimer + ( + IN PKTIMER Timer, + IN ULONG Hand + ); + + VOID KxRemoveTreeTimer + ( + IN PKTIMER Timer + ); + + BOOLEAN FASTCALL KiInsertTimerTable + ( + IN PKTIMER Timer, + IN ULONG Hand + ); + + BOOLEAN FASTCALL KiInsertTreeTimer ( IN PKTIMER Timer, IN LARGE_INTEGER Interval - ); - - ULONG KiComputeTimerTableIndex - ( - IN ULONGLONG Interval - ); - - BOOLEAN KiComputeDueTime - ( - IN PKTIMER Timer, - IN LARGE_INTEGER DueTime, - OUT PULONG Hand - ); - - BOOLEAN FASTCALL KiSignalTimer - ( - IN PKTIMER Timer - ); - - VOID NTAPI KiTimerExpiration - ( - IN PKDPC Dpc, - IN PVOID DeferredContext, - IN PVOID SystemArgument1, - IN PVOID SystemArgument2 - ); - - VOID FASTCALL KiTimerListExpire - ( - IN PLIST_ENTRY ExpiredListHead, - IN KIRQL OldIrql - ); - - VOID FASTCALL KiWaitSatisfyAll + ); + + ULONG KiComputeTimerTableIndex + ( + IN ULONGLONG Interval + ); + + BOOLEAN KiComputeDueTime + ( + IN PKTIMER Timer, + IN LARGE_INTEGER DueTime, + OUT PULONG Hand + ); + + BOOLEAN FASTCALL KiSignalTimer + ( + IN PKTIMER Timer + ); + + VOID NTAPI KiTimerExpiration + ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + + VOID FASTCALL KiTimerListExpire + ( + IN PLIST_ENTRY ExpiredListHead, + IN KIRQL OldIrql + ); + + VOID FASTCALL KiWaitSatisfyAll ( IN PKWAIT_BLOCK WaitBlock - ); -}; - -extern const xboxkrnl::ULONG CLOCK_TIME_INCREMENT; -extern xboxkrnl::LIST_ENTRY KiWaitInListHead; -extern xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; -extern xboxkrnl::KI_TIMER_LOCK KiTimerMtx; - + ); +}; + +extern const xboxkrnl::ULONG CLOCK_TIME_INCREMENT; +extern xboxkrnl::LIST_ENTRY KiWaitInListHead; +extern xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; +extern xboxkrnl::KI_TIMER_LOCK KiTimerMtx; + #define KiLockDispatcherDatabase(OldIrql) \ - *(OldIrql) = KeRaiseIrqlToDpcLevel() - + *(OldIrql) = KeRaiseIrqlToDpcLevel() + #define KiLockDispatcherDatabaseAtDpcLevel() \ KeRaiseIrqlToDpcLevel() @@ -156,14 +156,14 @@ extern xboxkrnl::KI_TIMER_LOCK KiTimerMtx; *(OldIrql) = KeRaiseIrqlToSynchLevel() #define KiUnlockApcQueue(Thread, OldIrql) \ - KfLowerIrql((OldIrql)) - + KfLowerIrql((OldIrql)) + #define KiInsertWaitList(_WaitMode, _Thread) { \ PLIST_ENTRY _ListHead; \ _ListHead = &KiWaitInListHead; \ InsertTailList(_ListHead, &(_Thread)->WaitListEntry); \ -} - +} + #define KiWaitSatisfyMutant(_Object_, _Thread_) { \ (_Object_)->Header.SignalState -= 1; \ if ((_Object_)->Header.SignalState == 0) { \ @@ -208,4 +208,4 @@ extern xboxkrnl::KI_TIMER_LOCK KiTimerMtx; &(_Object_)->MutantListEntry); \ } \ } \ -} +} diff --git a/src/core/kernel/exports/EmuKrnlLogging.cpp b/src/core/kernel/exports/EmuKrnlLogging.cpp index f85a6fd4d..311bc2713 100644 --- a/src/core/kernel/exports/EmuKrnlLogging.cpp +++ b/src/core/kernel/exports/EmuKrnlLogging.cpp @@ -123,7 +123,7 @@ FLAGS2STR_START(CREATE_OPTION) FLAG2STR(FILE_SEQUENTIAL_ONLY) FLAG2STR(FILE_NO_INTERMEDIATE_BUFFERING) FLAG2STR(FILE_SYNCHRONOUS_IO_ALERT) - FLAG2STR(FILE_SYNCHRONOUS_IO_NONALERT) + FLAG2STR(FILE_SYNCHRONOUS_IO_NONALERT) FLAG2STR(FILE_NON_DIRECTORY_FILE) FLAG2STR(FILE_CREATE_TREE_CONNECTION) FLAG2STR(FILE_COMPLETE_IF_OPLOCKED) @@ -427,4 +427,4 @@ LOGRENDER(UNICODE_STRING) LOGRENDER_MEMBER_SANITIZED(Buffer, wchar_t *, value.Length); } -}; // end of namespace xboxkrnl; +}; // end of namespace xboxkrnl; diff --git a/src/core/kernel/exports/EmuKrnlNt.cpp b/src/core/kernel/exports/EmuKrnlNt.cpp index c8ae0aa44..0ed3bbb02 100644 --- a/src/core/kernel/exports/EmuKrnlNt.cpp +++ b/src/core/kernel/exports/EmuKrnlNt.cpp @@ -1,2229 +1,2229 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2016 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::NT - - -#include // For NtAllocateVirtualMemory, etc. -#include "Logging.h" // For LOG_FUNC() -#include "EmuKrnlLogging.h" - -// prevent name collisions -namespace NtDll -{ -#include "core\kernel\support\EmuNtDll.h" -}; - -#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup -#include "core\kernel\exports\EmuKrnlKe.h" -#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) -#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc. -#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager -#include "CxbxDebugger.h" - -#pragma warning(disable:4005) // Ignore redefined status values -#include -#pragma warning(default:4005) -#include - -#include -#include - -// Used to keep track of duplicate handles created by NtQueueApcThread() -std::unordered_map g_DuplicateHandles; -// Prevent setting the system time from multiple threads at the same time -std::mutex NtSystemTimeMtx; - - -// ****************************************************************** -// * 0x00B8 - NtAllocateVirtualMemory() -// ****************************************************************** -XBSYSAPI EXPORTNUM(184) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtAllocateVirtualMemory -( - IN OUT PVOID *BaseAddress, - IN ULONG ZeroBits, - IN OUT PULONG AllocationSize, - IN DWORD AllocationType, - IN DWORD Protect -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_TYPE(PULONG, BaseAddress) - LOG_FUNC_ARG(ZeroBits) - LOG_FUNC_ARG(AllocationSize) - LOG_FUNC_ARG_TYPE(ALLOCATION_TYPE, AllocationType) - LOG_FUNC_ARG_TYPE(PROTECTION_TYPE, Protect) - LOG_FUNC_END; - - NTSTATUS ret = g_VMManager.XbAllocateVirtualMemory((VAddr*)BaseAddress, ZeroBits, (size_t*)AllocationSize, - AllocationType, Protect); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00B9 - NtCancelTimer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(185) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCancelTimer -( - IN HANDLE TimerHandle, - OUT PBOOLEAN CurrentState OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(TimerHandle) - LOG_FUNC_ARG(CurrentState) - LOG_FUNC_END; - - // redirect to Windows NT - // TODO : Untested - NTSTATUS ret = NtDll::NtCancelTimer( - TimerHandle, - /*OUT*/CurrentState); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtCancelTimer failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00BA - NtClearEvent() -// ****************************************************************** -XBSYSAPI EXPORTNUM(186) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtClearEvent -( - IN HANDLE EventHandle -) -{ - LOG_FUNC_ONE_ARG(EventHandle); - - NTSTATUS ret = NtDll::NtClearEvent(EventHandle); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtClearEvent Failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00BB - NtClose() -// ****************************************************************** -XBSYSAPI EXPORTNUM(187) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtClose -( - IN HANDLE Handle -) -{ - LOG_FUNC_ONE_ARG(Handle); - - NTSTATUS ret = STATUS_SUCCESS; - - if (IsEmuHandle(Handle)) - { - // delete 'special' handles - EmuHandle *iEmuHandle = HandleToEmuHandle(Handle); - ret = iEmuHandle->NtClose(); - - LOG_UNIMPLEMENTED(); // TODO : Base this on the Ob* functions - } - else - { - if (CxbxDebugger::CanReport()) - { - CxbxDebugger::ReportFileClosed(Handle); - } - - // Prevent exceptions when using invalid NTHandle - DWORD flags = 0; - if (GetHandleInformation(Handle, &flags) != 0) { - ret = NtDll::NtClose(Handle); - - // Delete duplicate threads created by our implementation of NtQueueApcThread() - if( GetHandleInformation( g_DuplicateHandles[Handle], &flags ) != 0 ) - { - EmuLog(LOG_LEVEL::DEBUG, "Closing duplicate handle..." ); - - CloseHandle( g_DuplicateHandles[Handle] ); - g_DuplicateHandles.erase(Handle); - } - } - } - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00BC - NtCreateDirectoryObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(188) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateDirectoryObject -( - OUT PHANDLE DirectoryHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(DirectoryHandle) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_END; - - NativeObjectAttributes nativeObjectAttributes; - NTSTATUS ret = CxbxObjectAttributesToNT( - ObjectAttributes, - nativeObjectAttributes, - "NtCreateDirectoryObject"); - - if (ret == STATUS_SUCCESS) - { - // TODO : Is this the correct ACCESS_MASK? : - const ACCESS_MASK DesiredAccess = DIRECTORY_CREATE_OBJECT; - - ret = NtDll::NtCreateDirectoryObject( - /*OUT*/DirectoryHandle, - DesiredAccess, - nativeObjectAttributes.NtObjAttrPtr); - } - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtCreateDirectoryObject Failed!"); - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateDirectoryObject DirectoryHandle = 0x%.8X", *DirectoryHandle); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00BD - NtCreateEvent() -// ****************************************************************** -XBSYSAPI EXPORTNUM(189) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateEvent -( - OUT PHANDLE EventHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN EVENT_TYPE EventType, - IN BOOLEAN InitialState -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(EventHandle) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(EventType) - LOG_FUNC_ARG(InitialState) - LOG_FUNC_END; - -/* - NTSTATUS Status; - - if ((EventType != NotificationEvent) && (EventType != SynchronizationEvent)) { - Status = STATUS_INVALID_PARAMETER; - } - else { - PKEVENT Event; - - Status = ObCreateObject(&ExEventObjectType, ObjectAttributes, sizeof(KEVENT), (PVOID *)&Event); - if (NT_SUCCESS(Status)) { - KeInitializeEvent(Event, EventType, InitialState); - Status = ObInsertObject(Event, ObjectAttributes, 0, EventHandle); - } - } - - RETURN(Status); -*/ - LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeEvent and ObInsertObject instead of this: - - // initialize object attributes - NativeObjectAttributes nativeObjectAttributes; - CxbxObjectAttributesToNT(ObjectAttributes, /*var*/nativeObjectAttributes); - - // TODO : Is this the correct ACCESS_MASK? : - const ACCESS_MASK DesiredAccess = EVENT_ALL_ACCESS; - - // redirect to Win2k/XP - NTSTATUS ret = NtDll::NtCreateEvent( - /*OUT*/EventHandle, - DesiredAccess, - nativeObjectAttributes.NtObjAttrPtr, - (NtDll::EVENT_TYPE)EventType, - InitialState); - - // TODO : Instead of the above, we should consider using the Ke*Event APIs, but - // that would require us to create the event's kernel object with the Ob* api's too! - - if (FAILED(ret)) - { - EmuLog(LOG_LEVEL::WARNING, "Trying fallback (without object attributes)...\nError code 0x%X", ret); - - // If it fails, try again but without the object attributes stucture - // This fixes Panzer Dragoon games on non-Vista OSes. - ret = NtDll::NtCreateEvent( - /*OUT*/EventHandle, - DesiredAccess, - /*nativeObjectAttributes.NtObjAttrPtr*/ NULL, - (NtDll::EVENT_TYPE)EventType, - InitialState); - - if(FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtCreateEvent Failed!"); - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateEvent EventHandle = 0x%.8X", *EventHandle); - } - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateEvent EventHandle = 0x%.8X", *EventHandle); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00BE - NtCreateFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(190) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateFile -( - OUT PHANDLE FileHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PLARGE_INTEGER AllocationSize OPTIONAL, - IN ULONG FileAttributes, - IN ULONG ShareAccess, - IN ULONG CreateDisposition, - IN ULONG CreateOptions -) -{ - LOG_FORWARD("IoCreateFile"); - - // TODO : How to base IoCreateFile on ObCreateObject, KeInitialize and ObInsertObject ? - - return xboxkrnl::IoCreateFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - AllocationSize, - FileAttributes, - ShareAccess, - CreateDisposition, - CreateOptions, - 0); -} - -// ****************************************************************** -// * 0x00BF - NtCreateIoCompletion() -// ****************************************************************** -XBSYSAPI EXPORTNUM(191) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateIoCompletion -( - OUT PHANDLE IoCompletionHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN ULONG Count -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(IoCompletionHandle) - LOG_FUNC_ARG(DesiredAccess) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(Count) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_NOT_IMPLEMENTED); -} - -// ****************************************************************** -// * 0x00C0 - NtCreateMutant() -// ****************************************************************** -XBSYSAPI EXPORTNUM(192) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateMutant -( - OUT PHANDLE MutantHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN BOOLEAN InitialOwner -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(MutantHandle) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(InitialOwner) - LOG_FUNC_END; - -/* - NTSTATUS Status; - - if (!verify arguments) { - Status = STATUS_INVALID_PARAMETER; - } - else { - PKMUTANT Mutant; - - Status = ObCreateObject(&ExMutantObjectType, ObjectAttributes, sizeof(KMUTANT), (PVOID *)&Mutant); - if (NT_SUCCESS(Status)) { - KeInitializeMutant(Mutant, InitialOwner); - Status = ObInsertObject(Mutant, ObjectAttributes, 0, /*OUT* /MutantHandle); - } - } - - RETURN(Status); -*/ - LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeMutant and ObInsertObject instead of this: - - // initialize object attributes - NativeObjectAttributes nativeObjectAttributes; - CxbxObjectAttributesToNT(ObjectAttributes, /*var*/nativeObjectAttributes); - - // TODO : Is this the correct ACCESS_MASK? : - const ACCESS_MASK DesiredAccess = MUTANT_ALL_ACCESS; - - // redirect to Windows Nt - NTSTATUS ret = NtDll::NtCreateMutant( - /*OUT*/MutantHandle, - DesiredAccess, - nativeObjectAttributes.NtObjAttrPtr, - InitialOwner); - - if (FAILED(ret)) - { - EmuLog(LOG_LEVEL::WARNING, "Trying fallback (without object attributes)...\nError code 0x%X", ret); - - // If it fails, try again but without the object attributes stucture - ret = NtDll::NtCreateMutant( - /*OUT*/MutantHandle, - DesiredAccess, - /*nativeObjectAttributes.NtObjAttrPtr*/ NULL, - InitialOwner); - - if(FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtCreateMutant Failed!"); - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateMutant MutantHandle = 0x%.8X", *MutantHandle); - } - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateMutant MutantHandle = 0x%.8X", *MutantHandle); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C1 - NtCreateSemaphore() -// ****************************************************************** -XBSYSAPI EXPORTNUM(193) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateSemaphore -( - OUT PHANDLE SemaphoreHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN ULONG InitialCount, - IN ULONG MaximumCount -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(SemaphoreHandle) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(InitialCount) - LOG_FUNC_ARG(MaximumCount) - LOG_FUNC_END; - -/* - NTSTATUS Status; - - if (!verify arguments) { - Status = STATUS_INVALID_PARAMETER; - } - else { - PKSEMAPHORE Semaphore; - - Status = ObCreateObject(&ExSemaphoreObjectType, ObjectAttributes, sizeof(KSEMAPHORE), (PVOID *)&Semaphore); - if (NT_SUCCESS(Status)) { - KeInitializeSemaphore(Semaphore, InitialCount, /*Limit=* /MaximumCount); - Status = ObInsertObject(Semaphore, ObjectAttributes, 0, /*OUT* /SemaphoreHandle); - } - } - - RETURN(Status); -*/ - LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeSemaphore and ObInsertObject instead of this: - - // TODO : Is this the correct ACCESS_MASK? : - const ACCESS_MASK DesiredAccess = SEMAPHORE_ALL_ACCESS; - - NativeObjectAttributes nativeObjectAttributes; - CxbxObjectAttributesToNT(ObjectAttributes, nativeObjectAttributes); - - // redirect to Win2k/XP - NTSTATUS ret = NtDll::NtCreateSemaphore( - /*OUT*/SemaphoreHandle, - DesiredAccess, - (NtDll::POBJECT_ATTRIBUTES)nativeObjectAttributes.NtObjAttrPtr, - InitialCount, - MaximumCount); - - if (FAILED(ret)) - { - EmuLog(LOG_LEVEL::WARNING, "Trying fallback (without object attributes)...\nError code 0x%X", ret); - - // If it fails, try again but without the object attributes stucture - ret = NtDll::NtCreateSemaphore( - /*OUT*/SemaphoreHandle, - DesiredAccess, - /*(NtDll::POBJECT_ATTRIBUTES)nativeObjectAttributes.NtObjAttrPtr*/ NULL, - InitialCount, - MaximumCount); - - if(FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtCreateSemaphore failed!"); - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateSemaphore SemaphoreHandle = 0x%.8X", *SemaphoreHandle); - } - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateSemaphore SemaphoreHandle = 0x%.8X", *SemaphoreHandle); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C2 - NtCreateTimer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(194) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateTimer -( - OUT PHANDLE TimerHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN TIMER_TYPE TimerType -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(TimerHandle) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(TimerType) - LOG_FUNC_END; - -/* - NTSTATUS Status; - - if (!verify arguments) { - Status = STATUS_INVALID_PARAMETER; - } - else { - PKTIMER Timer; - - Status = ObCreateObject(&ExTimerType, ObjectAttributes, sizeof(KTIMER), (PVOID *)&Timer); - if (NT_SUCCESS(Status)) { - KeInitializeTimerEx(Timer, TimerType); - Status = ObInsertObject(Timer, ObjectAttributes, 0, /*OUT* /TimerHandle); - } - } - - RETURN(Status); -*/ - LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeTimerEx and ObInsertObject instead of this: - - // TODO : Is this the correct ACCESS_MASK? : - const ACCESS_MASK DesiredAccess = TIMER_ALL_ACCESS; - - NativeObjectAttributes nativeObjectAttributes; - CxbxObjectAttributesToNT(ObjectAttributes, nativeObjectAttributes); - - // redirect to Windows NT - // TODO : Untested - NTSTATUS ret = NtDll::NtCreateTimer - ( - /*OUT*/TimerHandle, - DesiredAccess, - (NtDll::POBJECT_ATTRIBUTES)nativeObjectAttributes.NtObjAttrPtr, - (NtDll::TIMER_TYPE)TimerType - ); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtCreateTimer failed!"); - else - EmuLog(LOG_LEVEL::DEBUG, "NtCreateTimer TimerHandle = 0x%.8X", *TimerHandle); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C3 - NtDeleteFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(195) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDeleteFile -( - IN POBJECT_ATTRIBUTES ObjectAttributes -) -{ - LOG_FUNC_ONE_ARG(ObjectAttributes); - - NativeObjectAttributes nativeObjectAttributes; - NTSTATUS ret = CxbxObjectAttributesToNT( - ObjectAttributes, - nativeObjectAttributes, - "NtDeleteFile"); - - if (ret == STATUS_SUCCESS) - { - ret = NtDll::NtDeleteFile( - nativeObjectAttributes.NtObjAttrPtr); - } - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtDeleteFile Failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C4 - NtDeviceIoControlFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(196) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDeviceIoControlFile -( - IN HANDLE FileHandle, - IN HANDLE Event OPTIONAL, - IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, - IN PVOID ApcContext OPTIONAL, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN ULONG IoControlCode, - IN PVOID InputBuffer OPTIONAL, - IN ULONG InputBufferLength, - OUT PVOID OutputBuffer OPTIONAL, - IN ULONG OutputBufferLength -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(IoControlCode) - LOG_FUNC_ARG(InputBuffer) - LOG_FUNC_ARG(InputBufferLength) - LOG_FUNC_ARG_OUT(OutputBuffer) - LOG_FUNC_ARG(OutputBufferLength) - LOG_FUNC_END; - - NTSTATUS ret = STATUS_SUCCESS; - - switch (IoControlCode) - { - case 0x4D014: // IOCTL_SCSI_PASS_THROUGH_DIRECT - { - PSCSI_PASS_THROUGH_DIRECT PassThrough = (PSCSI_PASS_THROUGH_DIRECT)InputBuffer; - PDVDX2_AUTHENTICATION Authentication = (PDVDX2_AUTHENTICATION)PassThrough->DataBuffer; - - // Should be just enough info to pass XapiVerifyMediaInDrive - Authentication->AuthenticationPage.CDFValid = 1; - Authentication->AuthenticationPage.PartitionArea = 1; - Authentication->AuthenticationPage.Authentication = 1; - break; - } - case 0x70000: // IOCTL_DISK_GET_DRIVE_GEOMETRY - { - PDISK_GEOMETRY DiskGeometry = (PDISK_GEOMETRY)OutputBuffer; - - DiskGeometry->MediaType = FixedMedia; - DiskGeometry->TracksPerCylinder = 1; - DiskGeometry->SectorsPerTrack = 1; - DiskGeometry->BytesPerSector = 512; - DiskGeometry->Cylinders.QuadPart = 0x1400000; // Around 10GB, size of stock xbox HDD - break; - } - case 0x74004: // IOCTL_DISK_GET_PARTITION_INFO - { - PPARTITION_INFORMATION partitioninfo = (PPARTITION_INFORMATION)OutputBuffer; - - XboxPartitionTable partitionTable = CxbxGetPartitionTable(); - int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); - - // Now we read from the partition table, to fill in the partitionInfo struct - partitioninfo->PartitionNumber = partitionNumber; - partitioninfo->StartingOffset.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBAStart * 512; - partitioninfo->PartitionLength.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize * 512; - partitioninfo->HiddenSectors = partitionTable.TableEntries[partitionNumber - 1].Reserved; - partitioninfo->RecognizedPartition = true; - break; - } - default: - LOG_UNIMPLEMENTED(); - } - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C5 - NtDuplicateObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(197) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDuplicateObject -( - HANDLE SourceHandle, - HANDLE *TargetHandle, - DWORD Options -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(SourceHandle) - LOG_FUNC_ARG(TargetHandle) - LOG_FUNC_ARG(Options) - LOG_FUNC_END; - - NTSTATUS ret = STATUS_SUCCESS; - - if (IsEmuHandle(SourceHandle)) { - EmuHandle* iEmuHandle = HandleToEmuHandle(SourceHandle); - ret = iEmuHandle->NtDuplicateObject(TargetHandle, Options); -/* - PVOID Object; - - ret = ObReferenceObjectByHandle(SourceHandle, /*ObjectType=* /NULL, &Object); - if (NT_SUCCESS(ret)) { - if (ObpIsFlagSet(Options, DUPLICATE_CLOSE_SOURCE)) - NtClose(SourceHandle); - - status = ObOpenObjectByPointer(Object, OBJECT_TO_OBJECT_HEADER(Object)->Type, /*OUT* /TargetHandle); - ObDereferenceObject(Object); - } - else - *TargetHandle = NULL; -*/ - } - else - { - // TODO : What arguments should we use? - const ACCESS_MASK DesiredAccess = 0; - const ULONG Attributes = 0; - - // redirect to Win2k/XP - ret = NtDll::NtDuplicateObject( - /*SourceProcessHandle=*/g_CurrentProcessHandle, - SourceHandle, - /*TargetProcessHandle=*/g_CurrentProcessHandle, - TargetHandle, - DesiredAccess, - Attributes, - Options); - } - - if (ret != STATUS_SUCCESS) - EmuLog(LOG_LEVEL::WARNING, "Object was not duplicated!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C6 - NtFlushBuffersFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(198) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtFlushBuffersFile -( - PVOID FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_END; - NTSTATUS ret = STATUS_SUCCESS; - - if (IsEmuHandle(FileHandle)) - LOG_UNIMPLEMENTED(); - else - ret = NtDll::NtFlushBuffersFile(FileHandle, (NtDll::IO_STATUS_BLOCK*)IoStatusBlock); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C7 - NtFreeVirtualMemory() -// ****************************************************************** -// Frees virtual memory. -// -// Differences from NT: There is no ProcessHandle parameter. -XBSYSAPI EXPORTNUM(199) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtFreeVirtualMemory -( - IN OUT PVOID *BaseAddress, - IN OUT PULONG FreeSize, - IN ULONG FreeType -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(BaseAddress) - LOG_FUNC_ARG(FreeSize) - LOG_FUNC_ARG_TYPE(ALLOCATION_TYPE, FreeType) - LOG_FUNC_END; - - NTSTATUS ret = g_VMManager.XbFreeVirtualMemory((VAddr*)BaseAddress, (size_t*)FreeSize, FreeType); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C8 - NtFsControlFile -// ****************************************************************** -XBSYSAPI EXPORTNUM(200) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtFsControlFile -( - IN HANDLE FileHandle, - IN HANDLE Event OPTIONAL, - IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, - IN PVOID ApcContext OPTIONAL, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN ULONG FsControlCode, - IN PVOID InputBuffer OPTIONAL, - IN ULONG InputBufferLength, - OUT PVOID OutputBuffer OPTIONAL, - IN ULONG OutputBufferLength -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(FsControlCode) - LOG_FUNC_ARG(InputBuffer) - LOG_FUNC_ARG(InputBufferLength) - LOG_FUNC_ARG(OutputBuffer) - LOG_FUNC_ARG(OutputBufferLength) - LOG_FUNC_END; - - NTSTATUS ret = STATUS_INVALID_PARAMETER; - - switch (FsControlCode) { - case 0x00090020: // FSCTL_DISMOUNT_VOLUME - int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); - if (partitionNumber > 0) { - CxbxFormatPartitionByHandle(FileHandle); - ret = STATUS_SUCCESS; - } - break; - } - - LOG_UNIMPLEMENTED(); - RETURN(ret); -} - -// ****************************************************************** -// * 0x00C9 - NtOpenDirectoryObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(201) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenDirectoryObject -( - OUT PHANDLE DirectoryHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes -) -{ - LOG_FORWARD("ObOpenObjectByName"); - - return ObOpenObjectByName(ObjectAttributes, &ObDirectoryObjectType, NULL, DirectoryHandle); -} - -// ****************************************************************** -// * 0x00CA - NtOpenFile() -// ****************************************************************** -// Opens a file or device object. Same as calling: -// NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, -// IoStatusBlock, NULL, 0, ShareAccess, OPEN_EXISTING, OpenOptions); -// -// Differences from NT: See NtCreateFile. -XBSYSAPI EXPORTNUM(202) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenFile -( - OUT PHANDLE FileHandle, - IN ACCESS_MASK DesiredAccess, - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN ULONG ShareAccess, - IN ULONG OpenOptions -) -{ - LOG_FORWARD("IoCreateFile"); - - return xboxkrnl::IoCreateFile( - FileHandle, - DesiredAccess, - ObjectAttributes, - IoStatusBlock, - /*AllocationSize=*/NULL, - /*FileAttributes=*/0, - ShareAccess, - /*Disposition=*/FILE_OPEN, - /*CreateOptions=*/OpenOptions, - /*Options=*/0); -} - -// ****************************************************************** -// * 0x00CB - NtOpenSymbolicLinkObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(203) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenSymbolicLinkObject -( - OUT PHANDLE LinkHandle, - IN POBJECT_ATTRIBUTES ObjectAttributes -) -{ - /* TODO : - LOG_FORWARD("ObOpenObjectByName"); - - return ObOpenObjectByName(ObjectAttributes, &ObSymbolicLinkObjectType, NULL, LinkHandle); - */ - LOG_FUNC_BEGIN - LOG_FUNC_ARG_OUT(LinkHandle) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_END; - - NTSTATUS ret = STATUS_OBJECT_PATH_NOT_FOUND; - EmuNtSymbolicLinkObject* symbolicLinkObject = - FindNtSymbolicLinkObjectByName(PSTRING_to_string(ObjectAttributes->ObjectName)); - - if (symbolicLinkObject != NULL) - { - // Return a new handle (which is an EmuHandle, actually) : - *LinkHandle = symbolicLinkObject->NewHandle(); - ret = STATUS_SUCCESS; - } - - if (ret != STATUS_SUCCESS) - EmuLog(LOG_LEVEL::WARNING, "NtOpenSymbolicLinkObject failed! (%s)", NtStatusToString(ret)); - else - EmuLog(LOG_LEVEL::DEBUG, "NtOpenSymbolicLinkObject LinkHandle^ = 0x%.8X", *LinkHandle); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00CC - NtProtectVirtualMemory() -// ****************************************************************** -XBSYSAPI EXPORTNUM(204) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtProtectVirtualMemory -( - IN OUT PVOID *BaseAddress, - IN OUT PSIZE_T RegionSize, - IN ULONG NewProtect, - OUT PULONG OldProtect -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(BaseAddress) - LOG_FUNC_ARG(RegionSize) - LOG_FUNC_ARG_TYPE(PROTECTION_TYPE, NewProtect) - LOG_FUNC_ARG_OUT(OldProtect) - LOG_FUNC_END; - - - DWORD Perms = NewProtect; - NTSTATUS ret = g_VMManager.XbVirtualProtect((VAddr*)BaseAddress, (size_t*)RegionSize, &Perms); - *OldProtect = Perms; - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00CD - NtPulseEvent() -// ****************************************************************** -XBSYSAPI EXPORTNUM(205) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtPulseEvent -( - IN HANDLE EventHandle, - OUT PLONG PreviousState OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(EventHandle) - LOG_FUNC_ARG_OUT(PreviousState) - LOG_FUNC_END; - - // redirect to Windows NT - // TODO : Untested - NTSTATUS ret = NtDll::NtPulseEvent( - EventHandle, - /*OUT*/PreviousState); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtPulseEvent failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00CE - NtQueueApcThread() -// ****************************************************************** -XBSYSAPI EXPORTNUM(206) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueueApcThread -( - IN HANDLE ThreadHandle, - IN PIO_APC_ROUTINE ApcRoutine, - IN PVOID ApcRoutineContext OPTIONAL, - IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL, - IN ULONG ApcReserved OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ThreadHandle) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcRoutineContext) - LOG_FUNC_ARG(ApcStatusBlock) - LOG_FUNC_ARG(ApcReserved) - LOG_FUNC_END; - - // In order for NtQueueApcThread or QueueUserAPC to work, you must... I repeat... - // YOU MUST duplicate the handle with the appropriate permissions first! So far, - // the only game that I know of using this is Metal Slug 3, and it won't launch - // without it. Other SNK games might use it also, beware. - - // TODO: Use our implementation of NtDuplicateObject instead? - - HANDLE hApcThread = NULL; - - // Just to be safe, let's see if the appropriate permissions are even set for the - // target thread first... - - NTSTATUS ret = NtDll::NtQueueApcThread( - (NtDll::HANDLE)ThreadHandle, - (NtDll::PIO_APC_ROUTINE)ApcRoutine, - ApcRoutineContext, - (NtDll::PIO_STATUS_BLOCK)ApcStatusBlock, - ApcReserved); - - if( FAILED( ret ) ) - { - EmuLog(LOG_LEVEL::WARNING, "Duplicating handle with THREAD_SET_CONTEXT..." ); - - // If we get here, then attempt to duplicate the thread. - if(!DuplicateHandle(g_CurrentProcessHandle, ThreadHandle, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0)) - EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!"); - else - { - g_DuplicateHandles[ThreadHandle] = hApcThread; // Save this thread because we'll need to de-reference it later - EmuLog(LOG_LEVEL::DEBUG, "DuplicateHandle returned 0x%X (ThreadId 0x%.4X)", hApcThread, GetThreadId( hApcThread ) ); - } - - - ret = NtDll::NtQueueApcThread( - (NtDll::HANDLE)hApcThread, - (NtDll::PIO_APC_ROUTINE)ApcRoutine, - ApcRoutineContext, - (NtDll::PIO_STATUS_BLOCK)ApcStatusBlock, - ApcReserved); - } - if (FAILED(ret)) - { - EmuLog(LOG_LEVEL::WARNING, "NtQueueApcThread failed!"); - CloseHandle( g_DuplicateHandles[ThreadHandle] ); - g_DuplicateHandles.erase( ThreadHandle ); - } - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00CF - NtQueryDirectoryFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(207) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryDirectoryFile -( - IN HANDLE FileHandle, - IN HANDLE Event OPTIONAL, - IN PVOID ApcRoutine, // Todo: define this routine's prototype - IN PVOID ApcContext, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT FILE_DIRECTORY_INFORMATION *FileInformation, - IN ULONG Length, - IN FILE_INFORMATION_CLASS FileInformationClass, - IN PSTRING FileMask, - IN BOOLEAN RestartScan -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG_OUT(FileInformation) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(FileInformationClass) - LOG_FUNC_ARG(FileMask) - LOG_FUNC_ARG(RestartScan) - LOG_FUNC_END; - - NTSTATUS ret; - - if (FileInformationClass != FileDirectoryInformation) // Due to unicode->string conversion - CxbxKrnlCleanup("Unsupported FileInformationClass"); - - NtDll::UNICODE_STRING NtFileMask; - - wchar_t wszObjectName[MAX_PATH]; - - // initialize FileMask - { - if (FileMask != 0) { - // Xbox expects directories to be listed when *.* is passed - if (strncmp(FileMask->Buffer, "*.*", FileMask->Length) == 0) { - FileMask->Length = 1; - FileMask->Buffer = "*"; - } - - mbstowcs(/*Dest=*/wszObjectName, /*Source=*/FileMask->Buffer, /*MaxCount=*/MAX_PATH); - } else - mbstowcs(/*Dest=*/wszObjectName, /*Source=*/"", /*MaxCount=*/MAX_PATH); - - NtDll::RtlInitUnicodeString(&NtFileMask, wszObjectName); - } - - NtDll::FILE_DIRECTORY_INFORMATION *NtFileDirInfo = - (NtDll::FILE_DIRECTORY_INFORMATION *) malloc(NtFileDirectoryInformationSize + NtPathBufferSize); - - // Short-hand pointer to Nt filename : - wchar_t *wcstr = NtFileDirInfo->FileName; - char *mbstr = FileInformation->FileName; - - // Go, query that directory : - do - { - ZeroMemory(wcstr, MAX_PATH * sizeof(wchar_t)); - - ret = NtDll::NtQueryDirectoryFile( - FileHandle, - Event, - (NtDll::PIO_APC_ROUTINE)ApcRoutine, - ApcContext, - (NtDll::IO_STATUS_BLOCK*)IoStatusBlock, - /*FileInformation=*/NtFileDirInfo, - NtFileDirectoryInformationSize + NtPathBufferSize, - (NtDll::FILE_INFORMATION_CLASS)FileInformationClass, - /*ReturnSingleEntry=*/TRUE, - &NtFileMask, - RestartScan - ); - - RestartScan = FALSE; - } - // Xbox does not return . and .. - while (wcscmp(wcstr, L".") == 0 || wcscmp(wcstr, L"..") == 0); - - // convert from PC to Xbox - { - // TODO : assert that NtDll::FILE_DIRECTORY_INFORMATION has same members and size as xboxkrnl::FILE_DIRECTORY_INFORMATION - memcpy(/*Dst=*/FileInformation, /*Src=*/NtFileDirInfo, /*Size=*/NtFileDirectoryInformationSize); - wcstombs(/*Dest=*/mbstr, /*Source=*/wcstr, MAX_PATH); - FileInformation->FileNameLength /= sizeof(wchar_t); - } - - // TODO: Cache the last search result for quicker access with CreateFile (xbox does this internally!) - free(NtFileDirInfo); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D0 - NtQueryDirectoryObject -// ****************************************************************** -XBSYSAPI EXPORTNUM(208) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryDirectoryObject -( - IN HANDLE DirectoryHandle, - OUT PVOID Buffer, - IN ULONG Length, - IN BOOLEAN RestartScan, - IN OUT PULONG Context, - OUT PULONG ReturnedLength OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(DirectoryHandle) - LOG_FUNC_ARG_OUT(Buffer) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(RestartScan) - LOG_FUNC_ARG(Context) - LOG_FUNC_ARG_OUT(ReturnedLength) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00D1 - NtQueryEvent() -// ****************************************************************** -XBSYSAPI EXPORTNUM(209) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryEvent -( - IN HANDLE EventHandle, - OUT PEVENT_BASIC_INFORMATION EventInformation -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(EventHandle) - LOG_FUNC_ARG_OUT(EventInformation) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtQueryEvent( - (NtDll::HANDLE)EventHandle, - /*EventInformationClass*/NtDll::EVENT_INFORMATION_CLASS::EventBasicInformation, - EventInformation, - sizeof(EVENT_BASIC_INFORMATION), - /*ReturnLength=*/nullptr); - - if (ret != STATUS_SUCCESS) - EmuLog(LOG_LEVEL::WARNING, "NtQueryEvent failed! (%s)", NtStatusToString(ret)); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D2 - NtQueryFullAttributesFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(210) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryFullAttributesFile -( - IN POBJECT_ATTRIBUTES ObjectAttributes, - OUT xboxkrnl::PFILE_NETWORK_OPEN_INFORMATION Attributes -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG_OUT(Attributes) - LOG_FUNC_END; - - // __asm int 3; - NativeObjectAttributes nativeObjectAttributes; - NtDll::FILE_NETWORK_OPEN_INFORMATION nativeNetOpenInfo; - - NTSTATUS ret = CxbxObjectAttributesToNT( - ObjectAttributes, - /*var*/nativeObjectAttributes, - "NtQueryFullAttributesFile"); - - if (ret == STATUS_SUCCESS) - ret = NtDll::NtQueryFullAttributesFile( - nativeObjectAttributes.NtObjAttrPtr, - &nativeNetOpenInfo); - - // Convert Attributes to Xbox - NTToXboxFileInformation(&nativeNetOpenInfo, Attributes, FileNetworkOpenInformation, sizeof(xboxkrnl::FILE_NETWORK_OPEN_INFORMATION)); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtQueryFullAttributesFile failed! (0x%.08X)", ret); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D3 - NtQueryInformationFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(211) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryInformationFile -( - IN HANDLE FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PVOID FileInformation, - IN ULONG Length, - IN FILE_INFORMATION_CLASS FileInformationClass -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG_OUT(FileInformation) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(FileInformationClass) - LOG_FUNC_END; - - NTSTATUS ret; - PVOID ntFileInfo; - - // Start with sizeof(corresponding struct) - size_t bufferSize = XboxFileInfoStructSizes[FileInformationClass]; - - // We need to retry the operation in case the buffer is too small to fit the data - do - { - ntFileInfo = malloc(bufferSize); - - ret = NtDll::NtQueryInformationFile( - FileHandle, - (NtDll::PIO_STATUS_BLOCK)IoStatusBlock, - ntFileInfo, - bufferSize, - (NtDll::FILE_INFORMATION_CLASS)FileInformationClass); - - // Buffer is too small; make a larger one - if (ret == STATUS_BUFFER_OVERFLOW) - { - free(ntFileInfo); - - bufferSize *= 2; - // Bail out if the buffer gets too big - if (bufferSize > 65536) - return STATUS_INVALID_PARAMETER; // TODO: what's the appropriate error code to return here? - } - } while (ret == STATUS_BUFFER_OVERFLOW); - - // Convert and copy NT data to the given Xbox struct - NTSTATUS convRet = NTToXboxFileInformation(ntFileInfo, FileInformation, FileInformationClass, Length); - - // Make sure to free the memory first - free(ntFileInfo); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtQueryInformationFile failed! (0x%.08X)", ret); - - // Prioritize the buffer overflow over real return code, - // in case the Xbox program decides to follow the same procedure above - if (convRet == STATUS_BUFFER_OVERFLOW) - return convRet; - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D4 - NtQueryIoCompletion -// ****************************************************************** -XBSYSAPI EXPORTNUM(212) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryIoCompletion -( - IN HANDLE IoCompletionHandle, - OUT PIO_COMPLETION_BASIC_INFORMATION IoCompletionInformation -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(IoCompletionHandle) - LOG_FUNC_ARG_OUT(IoCompletionInformation) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00D5 - NtQueryMutant() -// ****************************************************************** -XBSYSAPI EXPORTNUM(213) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryMutant -( - IN HANDLE MutantHandle, - OUT PMUTANT_BASIC_INFORMATION MutantInformation -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(MutantHandle) - LOG_FUNC_ARG_OUT(MutantInformation) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtQueryMutant( - (NtDll::HANDLE)MutantHandle, - /*MutantInformationClass*/NtDll::MUTANT_INFORMATION_CLASS::MutantBasicInformation, - MutantInformation, - sizeof(MUTANT_BASIC_INFORMATION), - /*ReturnLength=*/nullptr); - - if (ret != STATUS_SUCCESS) - EmuLog(LOG_LEVEL::WARNING, "NtQueryMutant failed! (%s)", NtStatusToString(ret)); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D6 - NtQuerySemaphore() -// ****************************************************************** -XBSYSAPI EXPORTNUM(214) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQuerySemaphore -( - IN HANDLE SemaphoreHandle, - OUT PSEMAPHORE_BASIC_INFORMATION SemaphoreInformation -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(SemaphoreHandle) - LOG_FUNC_ARG_OUT(SemaphoreInformation) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtQuerySemaphore( - (NtDll::HANDLE)SemaphoreHandle, - /*SemaphoreInformationClass*/NtDll::SEMAPHORE_INFORMATION_CLASS::SemaphoreBasicInformation, - SemaphoreInformation, - sizeof(SEMAPHORE_BASIC_INFORMATION), - /*ReturnLength=*/nullptr); - - if (ret != STATUS_SUCCESS) - EmuLog(LOG_LEVEL::WARNING, "NtQuerySemaphore failed! (%s)", NtStatusToString(ret)); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D7 - NtQuerySymbolicLinkObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(215) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQuerySymbolicLinkObject -( - HANDLE LinkHandle, - OUT PSTRING LinkTarget, - OUT PULONG ReturnedLength OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(LinkHandle) - LOG_FUNC_ARG_OUT(LinkTarget) - LOG_FUNC_ARG_OUT(ReturnedLength) - LOG_FUNC_END; - - NTSTATUS ret = 0; - EmuNtSymbolicLinkObject* symbolicLinkObject = NULL; - - // Check that we actually got an EmuHandle : - ret = STATUS_INVALID_HANDLE; - - EmuHandle* iEmuHandle = HandleToEmuHandle(LinkHandle); - // Retrieve the NtSymbolicLinkObject and populate the output arguments : - ret = STATUS_SUCCESS; - symbolicLinkObject = (EmuNtSymbolicLinkObject*)iEmuHandle->NtObject; - - if (symbolicLinkObject->IsHostBasedPath) { - // TODO : What should we do with symbolic links - ret = STATUS_UNRECOGNIZED_VOLUME; - } else { - if (LinkTarget != NULL) - { - if (LinkTarget->MaximumLength >= symbolicLinkObject->XboxSymbolicLinkPath.length() + sizeof(char)) { - copy_string_to_PSTRING_to(symbolicLinkObject->XboxSymbolicLinkPath, LinkTarget); - } - else { - ret = STATUS_BUFFER_TOO_SMALL; - } - } - - if (ReturnedLength != NULL) - { - *ReturnedLength = symbolicLinkObject->XboxSymbolicLinkPath.length(); // Return full length (even if buffer was too small) - } - } - if (ret != STATUS_SUCCESS) - EmuLog(LOG_LEVEL::WARNING, "NtQuerySymbolicLinkObject failed! (%s)", NtStatusToString(ret)); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D8 - NtQueryTimer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(216) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryTimer -( - IN HANDLE TimerHandle, - OUT PTIMER_BASIC_INFORMATION TimerInformation -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(TimerHandle) - LOG_FUNC_ARG_OUT(TimerInformation) - LOG_FUNC_END; - - // redirect to Windows NT - // TODO : Untested - NTSTATUS ret = NtDll::NtQueryTimer( - TimerHandle, - /*TIMER_INFORMATION_CLASS*/NtDll::TimerBasicInformation, - /*OUT*/TimerInformation, - /*TimerInformationLength=*/sizeof(TIMER_BASIC_INFORMATION), - /*OUT ReturnLength*/nullptr - ); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00D9 - NtQueryVirtualMemory() -// ****************************************************************** -XBSYSAPI EXPORTNUM(217) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryVirtualMemory -( - IN PVOID BaseAddress, - OUT PMEMORY_BASIC_INFORMATION Buffer -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(BaseAddress) - LOG_FUNC_ARG_OUT(Buffer) - LOG_FUNC_END; - - if (!Buffer) - { - EmuLog(LOG_LEVEL::WARNING, "NtQueryVirtualMemory : PMEMORY_BASIC_INFORMATION Buffer is nullptr!\n"); - LOG_IGNORED(); - RETURN(STATUS_INVALID_PARAMETER); - } - - NTSTATUS ret = g_VMManager.XbVirtualMemoryStatistics((VAddr)BaseAddress, Buffer); - - if (ret == STATUS_SUCCESS) - { - EmuLog(LOG_LEVEL::DEBUG, " Buffer->AllocationBase = 0x%.08X", Buffer->AllocationBase); - EmuLog(LOG_LEVEL::DEBUG, " Buffer->AllocationProtect = 0x%.08X", Buffer->AllocationProtect); - EmuLog(LOG_LEVEL::DEBUG, " Buffer->BaseAddress = 0x%.08X", Buffer->BaseAddress); - EmuLog(LOG_LEVEL::DEBUG, " Buffer->RegionSize = 0x%.08X", Buffer->RegionSize); - EmuLog(LOG_LEVEL::DEBUG, " Buffer->State = 0x%.08X", Buffer->State); - EmuLog(LOG_LEVEL::DEBUG, " Buffer->Protect = 0x%.08X", Buffer->Protect); - EmuLog(LOG_LEVEL::DEBUG, " Buffer->Type = 0x%.08X", Buffer->Type); - } - - #if 0 - if (FAILED(ret)) { - EmuLog(LOG_LEVEL::WARNING, "NtQueryVirtualMemory failed (%s)!", NtStatusToString(ret)); - - // Bugfix for "Forza Motorsport", which iterates over 2 Gb of memory in 64kb chunks, - // but fails on this last query. It's not done though, as after this Forza tries to - // NtAllocateVirtualMemory at address 0x00000000 (3 times, actually) which fails too... - // - // Ported back from dxbx, translator PatrickvL - - if (BaseAddress == (PVOID)0x7FFF0000) { - Buffer->BaseAddress = BaseAddress; - Buffer->AllocationBase = BaseAddress; - Buffer->AllocationProtect = PAGE_READONLY; - Buffer->RegionSize = 64 * 1024; // size, in bytes, of the region beginning at the base address in which all pages have identical attributes - Buffer->State = 4096; // MEM_DECOMMIT | PAGE_EXECUTE_WRITECOPY etc - Buffer->Protect = PAGE_READONLY; // One of the flags listed for the AllocationProtect member is specified - Buffer->Type = 262144; // Specifies the type of pages in the region. (MEM_IMAGE, MEM_MAPPED or MEM_PRIVATE) - - ret = STATUS_SUCCESS; - - EmuLog(LOG_LEVEL::DEBUG, "NtQueryVirtualMemory: Applied fix for Forza Motorsport!"); - } - } - #endif - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00DA - NtQueryVolumeInformationFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(218) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryVolumeInformationFile -( - IN HANDLE FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PFILE_FS_SIZE_INFORMATION FileInformation, - IN ULONG Length, - IN FS_INFORMATION_CLASS FileInformationClass -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG_OUT(FileInformation) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(FileInformationClass) - LOG_FUNC_END; - - // FileFsSizeInformation is a special case that should read from our emulated partition table - if ((DWORD)FileInformationClass == FileFsSizeInformation) { - PFILE_FS_SIZE_INFORMATION XboxSizeInfo = (PFILE_FS_SIZE_INFORMATION)FileInformation; - - XboxPartitionTable partitionTable = CxbxGetPartitionTable(); - int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); - FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber); - - XboxSizeInfo->BytesPerSector = 512; - - // In some cases, the emulated partition hasn't been formatted yet, as these are forwarded to a real folder, this doesn't actually matter. - // We just pretend they are valid by defaulting the SectorsPerAllocationUnit value to the most common for system partitions - XboxSizeInfo->SectorsPerAllocationUnit = 32; - - // If there is a valid cluster size, we calculate SectorsPerAllocationUnit from that instead - if (superBlock.ClusterSize > 0) { - XboxSizeInfo->SectorsPerAllocationUnit = superBlock.ClusterSize; - } - - XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; - XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; - - RETURN(STATUS_SUCCESS); - } - - // Get the required size for the host buffer - // This may differ than the xbox buffer size so we also need to handle conversions! - ULONG HostBufferSize = 0; - switch ((DWORD)FileInformationClass) { - case FileFsVolumeInformation: - // Reserve a large enough buffer for the file information - // including the variable length path field - HostBufferSize = sizeof(NtDll::FILE_FS_VOLUME_INFORMATION) + MAX_PATH; - break; - case FileFsLabelInformation: - HostBufferSize = sizeof(NtDll::FILE_FS_LABEL_INFORMATION); - break; - case FileFsDeviceInformation: - HostBufferSize = sizeof(NtDll::FILE_FS_DEVICE_INFORMATION); - break; - case FileFsAttributeInformation: - HostBufferSize = sizeof(NtDll::FILE_FS_ATTRIBUTE_INFORMATION); - break; - case FileFsFullSizeInformation: - HostBufferSize = sizeof(NtDll::FILE_FS_FULL_SIZE_INFORMATION); - break; - case FileFsObjectIdInformation: - HostBufferSize = sizeof(NtDll::FILE_FS_OBJECTID_INFORMATION); - break; - } - - PVOID NativeFileInformation = _aligned_malloc(HostBufferSize, 8); - - NTSTATUS ret = NtDll::NtQueryVolumeInformationFile( - FileHandle, - (NtDll::PIO_STATUS_BLOCK)IoStatusBlock, - (NtDll::PFILE_FS_SIZE_INFORMATION)NativeFileInformation, HostBufferSize, - (NtDll::FS_INFORMATION_CLASS)FileInformationClass); - - // Convert Xbox NativeFileInformation to FileInformation - if (ret == STATUS_SUCCESS) { - switch ((DWORD)FileInformationClass) { - case FileFsVolumeInformation: { - PFILE_FS_VOLUME_INFORMATION XboxVolumeInfo = (PFILE_FS_VOLUME_INFORMATION)FileInformation; - NtDll::PFILE_FS_VOLUME_INFORMATION HostVolumeInfo = (NtDll::PFILE_FS_VOLUME_INFORMATION)NativeFileInformation; - - // Most options can just be directly copied to the Xbox version, only the strings differ - XboxVolumeInfo->VolumeCreationTime.QuadPart = HostVolumeInfo->VolumeCreationTime.QuadPart; - XboxVolumeInfo->VolumeSerialNumber = HostVolumeInfo->VolumeSerialNumber; - XboxVolumeInfo->VolumeLabelLength = HostVolumeInfo->VolumeLabelLength; - XboxVolumeInfo->SupportsObjects = HostVolumeInfo->SupportsObjects; - - // Convert strings to the Xbox format - wcstombs(XboxVolumeInfo->VolumeLabel, HostVolumeInfo->VolumeLabel, HostVolumeInfo->VolumeLabelLength); - } - break; - default: - // For all other types, just do a memcpy and hope for the best! - EmuLog(LOG_LEVEL::WARNING, "NtQueryVolumeInformationFile: Unknown FileInformationClass"); - memcpy_s(FileInformation, Length, NativeFileInformation, HostBufferSize); - break; - } - } - - _aligned_free(NativeFileInformation); - - if (FAILED(ret)) { - EmuLog(LOG_LEVEL::WARNING, "NtQueryVolumeInformationFile failed! (%s)\n", NtStatusToString(ret)); - } - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00DB - NtReadFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(219) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReadFile -( - IN HANDLE FileHandle, // TODO: correct paramters - IN HANDLE Event OPTIONAL, - IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, - IN PVOID ApcContext, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG_OUT(Buffer) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(ByteOffset) - LOG_FUNC_END; - - // Halo... - // if(ByteOffset != 0 && ByteOffset->QuadPart == 0x00120800) - // _asm int 3 - - if (CxbxDebugger::CanReport()) - { - uint64_t Offset = ~0; - if (ByteOffset) - Offset = ByteOffset->QuadPart; - - CxbxDebugger::ReportFileRead(FileHandle, Length, Offset); - } - - NTSTATUS ret = NtDll::NtReadFile( - FileHandle, - Event, - (PVOID)ApcRoutine, - ApcContext, - IoStatusBlock, - Buffer, - Length, - (NtDll::LARGE_INTEGER*)ByteOffset, - /*Key=*/nullptr); - - if (FAILED(ret)) { - EmuLog(LOG_LEVEL::WARNING, "NtReadFile Failed! (0x%.08X)", ret); - } - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00DC - NtReadFileScatter -// ****************************************************************** -XBSYSAPI EXPORTNUM(220) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReadFileScatter -( - IN HANDLE FileHandle, - IN HANDLE Event OPTIONAL, - IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, - IN PVOID ApcContext OPTIONAL, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PFILE_SEGMENT_ELEMENT SegmentArray, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(SegmentArray) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(ByteOffset) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00DD - NtReleaseMutant() -// ****************************************************************** -XBSYSAPI EXPORTNUM(221) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReleaseMutant -( - IN HANDLE MutantHandle, - OUT PLONG PreviousCount -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(MutantHandle) - LOG_FUNC_ARG_OUT(PreviousCount) - LOG_FUNC_END; - - // redirect to NtCreateMutant - NTSTATUS ret = NtDll::NtReleaseMutant(MutantHandle, PreviousCount); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtReleaseMutant Failed!"); - - RETURN(STATUS_SUCCESS); // TODO : RETURN(ret); -} - -// ****************************************************************** -// * 0x00DE - NtReleaseSemaphore() -// ****************************************************************** -XBSYSAPI EXPORTNUM(222) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReleaseSemaphore -( - IN HANDLE SemaphoreHandle, - IN ULONG ReleaseCount, - OUT PULONG PreviousCount OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(SemaphoreHandle) - LOG_FUNC_ARG(ReleaseCount) - LOG_FUNC_ARG_OUT(PreviousCount) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtReleaseSemaphore( - SemaphoreHandle, - ReleaseCount, - PreviousCount); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtReleaseSemaphore failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00DF - NtRemoveIoCompletion -// ****************************************************************** -XBSYSAPI EXPORTNUM(223) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtRemoveIoCompletion -( - IN HANDLE IoCompletionHandle, - OUT PVOID *KeyContext, - OUT PVOID *ApcContext, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PLARGE_INTEGER Timeout OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(IoCompletionHandle) - LOG_FUNC_ARG_OUT(KeyContext) - LOG_FUNC_ARG_OUT(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(Timeout) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00E0 - NtResumeThread() -// ****************************************************************** -XBSYSAPI EXPORTNUM(224) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtResumeThread -( - IN HANDLE ThreadHandle, - OUT PULONG PreviousSuspendCount -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ThreadHandle) - LOG_FUNC_ARG_OUT(PreviousSuspendCount) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtResumeThread( - ThreadHandle, - PreviousSuspendCount); - - // TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread - - //Sleep(10); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00E1 - NtSetEvent() -// ****************************************************************** -XBSYSAPI EXPORTNUM(225) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetEvent -( - IN HANDLE EventHandle, - OUT PLONG PreviousState -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(EventHandle) - LOG_FUNC_ARG_OUT(PreviousState) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtSetEvent( - EventHandle, - PreviousState); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtSetEvent Failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00E2 - NtSetInformationFile() -// ****************************************************************** -XBSYSAPI EXPORTNUM(226) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetInformationFile -( - IN HANDLE FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PVOID FileInformation, - IN ULONG Length, - IN FILE_INFORMATION_CLASS FileInformationClass -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(FileInformation) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(FileInformationClass) - LOG_FUNC_END; - - XboxToNTFileInformation(convertedFileInfo, FileInformation, FileInformationClass, &Length); - - NTSTATUS ret = NtDll::NtSetInformationFile( - FileHandle, - IoStatusBlock, - convertedFileInfo, - Length, - FileInformationClass); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00E3 - NtSetIoCompletion -// ****************************************************************** -XBSYSAPI EXPORTNUM(227) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetIoCompletion -( - IN HANDLE IoCompletionHandle, - IN PVOID KeyContext, - IN PVOID ApcContext, - IN NTSTATUS IoStatus, - IN ULONG_PTR IoStatusInformation -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(IoCompletionHandle) - LOG_FUNC_ARG(KeyContext) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG(IoStatus) - LOG_FUNC_ARG(IoStatusInformation) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00E4 - NtSetSystemTime() -// ****************************************************************** -XBSYSAPI EXPORTNUM(228) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetSystemTime -( - IN PLARGE_INTEGER SystemTime, - OUT PLARGE_INTEGER PreviousTime OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(SystemTime) - LOG_FUNC_ARG_OUT(PreviousTime) - LOG_FUNC_END; - - NTSTATUS ret = STATUS_SUCCESS; - LARGE_INTEGER NewSystemTime, OldSystemTime; - // LARGE_INTEGER LocalTime; - // TIME_FIELDS TimeFields; - - if (SystemTime == nullptr) { - ret = STATUS_ACCESS_VIOLATION; - } - else { - NtSystemTimeMtx.lock(); - NewSystemTime = *SystemTime; - if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) { - /* Convert the time and set it in HAL */ - // NOTE: disabled, as this requires emulating the RTC, which we don't yet - // ExSystemTimeToLocalTime(&NewSystemTime, &LocalTime); - // RtlTimeToTimeFields(&LocalTime, &TimeFields); - // HalSetRealTimeClock(&TimeFields); - - /* Now set system time */ - KeSetSystemTime(&NewSystemTime, &OldSystemTime); - - // Is the previous time requested? - if (PreviousTime != nullptr) { - PreviousTime->QuadPart = OldSystemTime.QuadPart; - } - } - else { - ret = STATUS_INVALID_PARAMETER; - } - NtSystemTimeMtx.unlock(); - } - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00E5 - NtSetTimerEx() -// ****************************************************************** -XBSYSAPI EXPORTNUM(229) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetTimerEx -( - IN HANDLE TimerHandle, - IN PLARGE_INTEGER DueTime, - IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL, - IN KPROCESSOR_MODE ApcMode, - IN PVOID TimerContext OPTIONAL, - IN BOOLEAN WakeTimer, - IN LONG Period OPTIONAL, - OUT PBOOLEAN PreviousState OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(TimerHandle) - LOG_FUNC_ARG(DueTime) - LOG_FUNC_ARG(TimerApcRoutine) - LOG_FUNC_ARG(ApcMode) - LOG_FUNC_ARG(TimerContext) - LOG_FUNC_ARG(WakeTimer) - LOG_FUNC_ARG(Period) - LOG_FUNC_ARG_OUT(PreviousState) - LOG_FUNC_END; - - // redirect to Windows NT - // TODO : Untested - NTSTATUS ret = NtDll::NtSetTimer( - TimerHandle, - (NtDll::PLARGE_INTEGER)DueTime, - (NtDll::PTIMER_APC_ROUTINE)TimerApcRoutine, - (NtDll::PVOID)TimerContext, - WakeTimer, - Period, - /*OUT*/PreviousState); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtSetTimerEx failed!"); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00E6 - NtSignalAndWaitForSingleObjectEx -// ****************************************************************** -XBSYSAPI EXPORTNUM(230) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSignalAndWaitForSingleObjectEx -( - IN HANDLE SignalHandle, - IN HANDLE WaitHandle, - IN KPROCESSOR_MODE WaitMode, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Timeout OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(SignalHandle) - LOG_FUNC_ARG(WaitHandle) - LOG_FUNC_ARG(WaitMode) - LOG_FUNC_ARG(Alertable) - LOG_FUNC_ARG(Timeout) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00E7 - NtSuspendThread() -// ****************************************************************** -XBSYSAPI EXPORTNUM(231) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSuspendThread -( - IN HANDLE ThreadHandle, - OUT PULONG PreviousSuspendCount OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ThreadHandle) - LOG_FUNC_ARG_OUT(PreviousSuspendCount) - LOG_FUNC_END; - - NTSTATUS ret = NtDll::NtSuspendThread( - ThreadHandle, - PreviousSuspendCount); - - // TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00E8 - NtUserIoApcDispatcher() -// ****************************************************************** -XBSYSAPI EXPORTNUM(232) xboxkrnl::VOID NTAPI xboxkrnl::NtUserIoApcDispatcher -( - PVOID ApcContext, - PIO_STATUS_BLOCK IoStatusBlock, - ULONG Reserved -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG(IoStatusBlock) - LOG_FUNC_ARG(Reserved) - LOG_FUNC_END; - - ULONG dwErrorCode = 0; - ULONG dwTransferred = 0; - - if (NT_SUCCESS(IoStatusBlock->Status)) { - dwTransferred = (ULONG)IoStatusBlock->Information; - } else { - dwErrorCode = RtlNtStatusToDosError(IoStatusBlock->Status); - } - - LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine = (LPOVERLAPPED_COMPLETION_ROUTINE)ApcContext; - LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)CONTAINING_RECORD(IoStatusBlock, OVERLAPPED, Internal); - - (CompletionRoutine)(dwErrorCode, dwTransferred, lpOverlapped); - - EmuLog(LOG_LEVEL::DEBUG, "NtUserIoApcDispatcher Completed"); -} - -// ****************************************************************** -// * 0x00E9 - NtWaitForSingleObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(233) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForSingleObject -( - IN HANDLE Handle, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Timeout -) -{ - LOG_FORWARD("NtWaitForMultipleObjectsEx"); - - return xboxkrnl::NtWaitForMultipleObjectsEx( - /*Count=*/1, - &Handle, - /*WaitType=*/WaitAll, - /*WaitMode=*/KernelMode, - Alertable, - Timeout - ); -} - -// ****************************************************************** -// * 0x00EA - NtWaitForSingleObjectEx() -// ****************************************************************** -XBSYSAPI EXPORTNUM(234) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForSingleObjectEx -( - IN HANDLE Handle, - IN KPROCESSOR_MODE WaitMode, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Timeout -) -{ - LOG_FORWARD("NtWaitForMultipleObjectsEx"); - - return xboxkrnl::NtWaitForMultipleObjectsEx( - /*Count=*/1, - &Handle, - /*WaitType=*/WaitAll, - WaitMode, - Alertable, - Timeout - ); -} - -// ****************************************************************** -// * 0x00EB - NtWaitForMultipleObjectsEx() -// ****************************************************************** -XBSYSAPI EXPORTNUM(235) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForMultipleObjectsEx -( - IN ULONG Count, - IN HANDLE *Handles, - IN WAIT_TYPE WaitType, - IN CHAR WaitMode, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Timeout -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Count) - LOG_FUNC_ARG(Handles) - LOG_FUNC_ARG(WaitType) - LOG_FUNC_ARG(WaitMode) - LOG_FUNC_ARG(Alertable) - LOG_FUNC_ARG(Timeout) - LOG_FUNC_END; - - return NtDll::NtWaitForMultipleObjects( - Count, - Handles, - (NtDll::OBJECT_WAIT_TYPE)WaitType, - Alertable, - (NtDll::PLARGE_INTEGER)Timeout); -} - -// ****************************************************************** -// * 0x00EC - NtWriteFile() -// ****************************************************************** -// Writes a file. -// -// Differences from NT: There is no Key parameter. -XBSYSAPI EXPORTNUM(236) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWriteFile -( - IN HANDLE FileHandle, - IN HANDLE Event, - IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, - IN PVOID ApcContext OPTIONAL, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(Buffer) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(ByteOffset) - LOG_FUNC_END; - - // Halo.. - // if(ByteOffset != 0 && ByteOffset->QuadPart == 0x01C00800) - // _asm int 3 - - if (CxbxDebugger::CanReport()) - { - uint64_t Offset = ~0; - if (ByteOffset) - Offset = ByteOffset->QuadPart; - - CxbxDebugger::ReportFileWrite(FileHandle, Length, Offset); - } - - NTSTATUS ret = NtDll::NtWriteFile( - FileHandle, - Event, - (PVOID)ApcRoutine, - ApcContext, - IoStatusBlock, - Buffer, - Length, - (NtDll::LARGE_INTEGER*)ByteOffset, - /*Key=*/nullptr); - - if (FAILED(ret)) - EmuLog(LOG_LEVEL::WARNING, "NtWriteFile Failed! (0x%.08X)", ret); - - RETURN(ret); -} - -// ****************************************************************** -// * 0x00ED - NtWriteFileGather -// ****************************************************************** -XBSYSAPI EXPORTNUM(237) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWriteFileGather -( - IN HANDLE FileHandle, - IN HANDLE Event OPTIONAL, - IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, - IN PVOID ApcContext OPTIONAL, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PFILE_SEGMENT_ELEMENT SegmentArray, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset OPTIONAL -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(FileHandle) - LOG_FUNC_ARG(Event) - LOG_FUNC_ARG(ApcRoutine) - LOG_FUNC_ARG(ApcContext) - LOG_FUNC_ARG_OUT(IoStatusBlock) - LOG_FUNC_ARG(SegmentArray) - LOG_FUNC_ARG(Length) - LOG_FUNC_ARG(ByteOffset) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00EE - NtYieldExecution() -// ****************************************************************** -XBSYSAPI EXPORTNUM(238) xboxkrnl::VOID NTAPI xboxkrnl::NtYieldExecution() -{ - // NOTE: Logging this fills the debug log far too quickly, so don't. - // LOG_FUNC(); - - NtDll::NtYieldExecution(); -} - +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2016 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::NT + + +#include // For NtAllocateVirtualMemory, etc. +#include "Logging.h" // For LOG_FUNC() +#include "EmuKrnlLogging.h" + +// prevent name collisions +namespace NtDll +{ +#include "core\kernel\support\EmuNtDll.h" +}; + +#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup +#include "core\kernel\exports\EmuKrnlKe.h" +#include "core\kernel\support\Emu.h" // For EmuLog(LOG_LEVEL::WARNING, ) +#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc. +#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager +#include "CxbxDebugger.h" + +#pragma warning(disable:4005) // Ignore redefined status values +#include +#pragma warning(default:4005) +#include + +#include +#include + +// Used to keep track of duplicate handles created by NtQueueApcThread() +std::unordered_map g_DuplicateHandles; +// Prevent setting the system time from multiple threads at the same time +std::mutex NtSystemTimeMtx; + + +// ****************************************************************** +// * 0x00B8 - NtAllocateVirtualMemory() +// ****************************************************************** +XBSYSAPI EXPORTNUM(184) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtAllocateVirtualMemory +( + IN OUT PVOID *BaseAddress, + IN ULONG ZeroBits, + IN OUT PULONG AllocationSize, + IN DWORD AllocationType, + IN DWORD Protect +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_TYPE(PULONG, BaseAddress) + LOG_FUNC_ARG(ZeroBits) + LOG_FUNC_ARG(AllocationSize) + LOG_FUNC_ARG_TYPE(ALLOCATION_TYPE, AllocationType) + LOG_FUNC_ARG_TYPE(PROTECTION_TYPE, Protect) + LOG_FUNC_END; + + NTSTATUS ret = g_VMManager.XbAllocateVirtualMemory((VAddr*)BaseAddress, ZeroBits, (size_t*)AllocationSize, + AllocationType, Protect); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00B9 - NtCancelTimer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(185) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCancelTimer +( + IN HANDLE TimerHandle, + OUT PBOOLEAN CurrentState OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(TimerHandle) + LOG_FUNC_ARG(CurrentState) + LOG_FUNC_END; + + // redirect to Windows NT + // TODO : Untested + NTSTATUS ret = NtDll::NtCancelTimer( + TimerHandle, + /*OUT*/CurrentState); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtCancelTimer failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00BA - NtClearEvent() +// ****************************************************************** +XBSYSAPI EXPORTNUM(186) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtClearEvent +( + IN HANDLE EventHandle +) +{ + LOG_FUNC_ONE_ARG(EventHandle); + + NTSTATUS ret = NtDll::NtClearEvent(EventHandle); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtClearEvent Failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00BB - NtClose() +// ****************************************************************** +XBSYSAPI EXPORTNUM(187) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtClose +( + IN HANDLE Handle +) +{ + LOG_FUNC_ONE_ARG(Handle); + + NTSTATUS ret = STATUS_SUCCESS; + + if (IsEmuHandle(Handle)) + { + // delete 'special' handles + EmuHandle *iEmuHandle = HandleToEmuHandle(Handle); + ret = iEmuHandle->NtClose(); + + LOG_UNIMPLEMENTED(); // TODO : Base this on the Ob* functions + } + else + { + if (CxbxDebugger::CanReport()) + { + CxbxDebugger::ReportFileClosed(Handle); + } + + // Prevent exceptions when using invalid NTHandle + DWORD flags = 0; + if (GetHandleInformation(Handle, &flags) != 0) { + ret = NtDll::NtClose(Handle); + + // Delete duplicate threads created by our implementation of NtQueueApcThread() + if( GetHandleInformation( g_DuplicateHandles[Handle], &flags ) != 0 ) + { + EmuLog(LOG_LEVEL::DEBUG, "Closing duplicate handle..." ); + + CloseHandle( g_DuplicateHandles[Handle] ); + g_DuplicateHandles.erase(Handle); + } + } + } + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00BC - NtCreateDirectoryObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(188) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateDirectoryObject +( + OUT PHANDLE DirectoryHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(DirectoryHandle) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_END; + + NativeObjectAttributes nativeObjectAttributes; + NTSTATUS ret = CxbxObjectAttributesToNT( + ObjectAttributes, + nativeObjectAttributes, + "NtCreateDirectoryObject"); + + if (ret == STATUS_SUCCESS) + { + // TODO : Is this the correct ACCESS_MASK? : + const ACCESS_MASK DesiredAccess = DIRECTORY_CREATE_OBJECT; + + ret = NtDll::NtCreateDirectoryObject( + /*OUT*/DirectoryHandle, + DesiredAccess, + nativeObjectAttributes.NtObjAttrPtr); + } + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtCreateDirectoryObject Failed!"); + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateDirectoryObject DirectoryHandle = 0x%.8X", *DirectoryHandle); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00BD - NtCreateEvent() +// ****************************************************************** +XBSYSAPI EXPORTNUM(189) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateEvent +( + OUT PHANDLE EventHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN EVENT_TYPE EventType, + IN BOOLEAN InitialState +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(EventHandle) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(EventType) + LOG_FUNC_ARG(InitialState) + LOG_FUNC_END; + +/* + NTSTATUS Status; + + if ((EventType != NotificationEvent) && (EventType != SynchronizationEvent)) { + Status = STATUS_INVALID_PARAMETER; + } + else { + PKEVENT Event; + + Status = ObCreateObject(&ExEventObjectType, ObjectAttributes, sizeof(KEVENT), (PVOID *)&Event); + if (NT_SUCCESS(Status)) { + KeInitializeEvent(Event, EventType, InitialState); + Status = ObInsertObject(Event, ObjectAttributes, 0, EventHandle); + } + } + + RETURN(Status); +*/ + LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeEvent and ObInsertObject instead of this: + + // initialize object attributes + NativeObjectAttributes nativeObjectAttributes; + CxbxObjectAttributesToNT(ObjectAttributes, /*var*/nativeObjectAttributes); + + // TODO : Is this the correct ACCESS_MASK? : + const ACCESS_MASK DesiredAccess = EVENT_ALL_ACCESS; + + // redirect to Win2k/XP + NTSTATUS ret = NtDll::NtCreateEvent( + /*OUT*/EventHandle, + DesiredAccess, + nativeObjectAttributes.NtObjAttrPtr, + (NtDll::EVENT_TYPE)EventType, + InitialState); + + // TODO : Instead of the above, we should consider using the Ke*Event APIs, but + // that would require us to create the event's kernel object with the Ob* api's too! + + if (FAILED(ret)) + { + EmuLog(LOG_LEVEL::WARNING, "Trying fallback (without object attributes)...\nError code 0x%X", ret); + + // If it fails, try again but without the object attributes stucture + // This fixes Panzer Dragoon games on non-Vista OSes. + ret = NtDll::NtCreateEvent( + /*OUT*/EventHandle, + DesiredAccess, + /*nativeObjectAttributes.NtObjAttrPtr*/ NULL, + (NtDll::EVENT_TYPE)EventType, + InitialState); + + if(FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtCreateEvent Failed!"); + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateEvent EventHandle = 0x%.8X", *EventHandle); + } + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateEvent EventHandle = 0x%.8X", *EventHandle); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00BE - NtCreateFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(190) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateFile +( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER AllocationSize OPTIONAL, + IN ULONG FileAttributes, + IN ULONG ShareAccess, + IN ULONG CreateDisposition, + IN ULONG CreateOptions +) +{ + LOG_FORWARD("IoCreateFile"); + + // TODO : How to base IoCreateFile on ObCreateObject, KeInitialize and ObInsertObject ? + + return xboxkrnl::IoCreateFile( + FileHandle, + DesiredAccess, + ObjectAttributes, + IoStatusBlock, + AllocationSize, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + 0); +} + +// ****************************************************************** +// * 0x00BF - NtCreateIoCompletion() +// ****************************************************************** +XBSYSAPI EXPORTNUM(191) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateIoCompletion +( + OUT PHANDLE IoCompletionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG Count +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(IoCompletionHandle) + LOG_FUNC_ARG(DesiredAccess) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(Count) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_NOT_IMPLEMENTED); +} + +// ****************************************************************** +// * 0x00C0 - NtCreateMutant() +// ****************************************************************** +XBSYSAPI EXPORTNUM(192) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateMutant +( + OUT PHANDLE MutantHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN BOOLEAN InitialOwner +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(MutantHandle) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(InitialOwner) + LOG_FUNC_END; + +/* + NTSTATUS Status; + + if (!verify arguments) { + Status = STATUS_INVALID_PARAMETER; + } + else { + PKMUTANT Mutant; + + Status = ObCreateObject(&ExMutantObjectType, ObjectAttributes, sizeof(KMUTANT), (PVOID *)&Mutant); + if (NT_SUCCESS(Status)) { + KeInitializeMutant(Mutant, InitialOwner); + Status = ObInsertObject(Mutant, ObjectAttributes, 0, /*OUT* /MutantHandle); + } + } + + RETURN(Status); +*/ + LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeMutant and ObInsertObject instead of this: + + // initialize object attributes + NativeObjectAttributes nativeObjectAttributes; + CxbxObjectAttributesToNT(ObjectAttributes, /*var*/nativeObjectAttributes); + + // TODO : Is this the correct ACCESS_MASK? : + const ACCESS_MASK DesiredAccess = MUTANT_ALL_ACCESS; + + // redirect to Windows Nt + NTSTATUS ret = NtDll::NtCreateMutant( + /*OUT*/MutantHandle, + DesiredAccess, + nativeObjectAttributes.NtObjAttrPtr, + InitialOwner); + + if (FAILED(ret)) + { + EmuLog(LOG_LEVEL::WARNING, "Trying fallback (without object attributes)...\nError code 0x%X", ret); + + // If it fails, try again but without the object attributes stucture + ret = NtDll::NtCreateMutant( + /*OUT*/MutantHandle, + DesiredAccess, + /*nativeObjectAttributes.NtObjAttrPtr*/ NULL, + InitialOwner); + + if(FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtCreateMutant Failed!"); + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateMutant MutantHandle = 0x%.8X", *MutantHandle); + } + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateMutant MutantHandle = 0x%.8X", *MutantHandle); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C1 - NtCreateSemaphore() +// ****************************************************************** +XBSYSAPI EXPORTNUM(193) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateSemaphore +( + OUT PHANDLE SemaphoreHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN ULONG InitialCount, + IN ULONG MaximumCount +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(SemaphoreHandle) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(InitialCount) + LOG_FUNC_ARG(MaximumCount) + LOG_FUNC_END; + +/* + NTSTATUS Status; + + if (!verify arguments) { + Status = STATUS_INVALID_PARAMETER; + } + else { + PKSEMAPHORE Semaphore; + + Status = ObCreateObject(&ExSemaphoreObjectType, ObjectAttributes, sizeof(KSEMAPHORE), (PVOID *)&Semaphore); + if (NT_SUCCESS(Status)) { + KeInitializeSemaphore(Semaphore, InitialCount, /*Limit=* /MaximumCount); + Status = ObInsertObject(Semaphore, ObjectAttributes, 0, /*OUT* /SemaphoreHandle); + } + } + + RETURN(Status); +*/ + LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeSemaphore and ObInsertObject instead of this: + + // TODO : Is this the correct ACCESS_MASK? : + const ACCESS_MASK DesiredAccess = SEMAPHORE_ALL_ACCESS; + + NativeObjectAttributes nativeObjectAttributes; + CxbxObjectAttributesToNT(ObjectAttributes, nativeObjectAttributes); + + // redirect to Win2k/XP + NTSTATUS ret = NtDll::NtCreateSemaphore( + /*OUT*/SemaphoreHandle, + DesiredAccess, + (NtDll::POBJECT_ATTRIBUTES)nativeObjectAttributes.NtObjAttrPtr, + InitialCount, + MaximumCount); + + if (FAILED(ret)) + { + EmuLog(LOG_LEVEL::WARNING, "Trying fallback (without object attributes)...\nError code 0x%X", ret); + + // If it fails, try again but without the object attributes stucture + ret = NtDll::NtCreateSemaphore( + /*OUT*/SemaphoreHandle, + DesiredAccess, + /*(NtDll::POBJECT_ATTRIBUTES)nativeObjectAttributes.NtObjAttrPtr*/ NULL, + InitialCount, + MaximumCount); + + if(FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtCreateSemaphore failed!"); + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateSemaphore SemaphoreHandle = 0x%.8X", *SemaphoreHandle); + } + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateSemaphore SemaphoreHandle = 0x%.8X", *SemaphoreHandle); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C2 - NtCreateTimer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(194) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtCreateTimer +( + OUT PHANDLE TimerHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN TIMER_TYPE TimerType +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(TimerHandle) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(TimerType) + LOG_FUNC_END; + +/* + NTSTATUS Status; + + if (!verify arguments) { + Status = STATUS_INVALID_PARAMETER; + } + else { + PKTIMER Timer; + + Status = ObCreateObject(&ExTimerType, ObjectAttributes, sizeof(KTIMER), (PVOID *)&Timer); + if (NT_SUCCESS(Status)) { + KeInitializeTimerEx(Timer, TimerType); + Status = ObInsertObject(Timer, ObjectAttributes, 0, /*OUT* /TimerHandle); + } + } + + RETURN(Status); +*/ + LOG_INCOMPLETE(); // TODO : Verify arguments, use ObCreateObject, KeInitializeTimerEx and ObInsertObject instead of this: + + // TODO : Is this the correct ACCESS_MASK? : + const ACCESS_MASK DesiredAccess = TIMER_ALL_ACCESS; + + NativeObjectAttributes nativeObjectAttributes; + CxbxObjectAttributesToNT(ObjectAttributes, nativeObjectAttributes); + + // redirect to Windows NT + // TODO : Untested + NTSTATUS ret = NtDll::NtCreateTimer + ( + /*OUT*/TimerHandle, + DesiredAccess, + (NtDll::POBJECT_ATTRIBUTES)nativeObjectAttributes.NtObjAttrPtr, + (NtDll::TIMER_TYPE)TimerType + ); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtCreateTimer failed!"); + else + EmuLog(LOG_LEVEL::DEBUG, "NtCreateTimer TimerHandle = 0x%.8X", *TimerHandle); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C3 - NtDeleteFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(195) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDeleteFile +( + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + LOG_FUNC_ONE_ARG(ObjectAttributes); + + NativeObjectAttributes nativeObjectAttributes; + NTSTATUS ret = CxbxObjectAttributesToNT( + ObjectAttributes, + nativeObjectAttributes, + "NtDeleteFile"); + + if (ret == STATUS_SUCCESS) + { + ret = NtDll::NtDeleteFile( + nativeObjectAttributes.NtObjAttrPtr); + } + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtDeleteFile Failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C4 - NtDeviceIoControlFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(196) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDeviceIoControlFile +( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG IoControlCode, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(IoControlCode) + LOG_FUNC_ARG(InputBuffer) + LOG_FUNC_ARG(InputBufferLength) + LOG_FUNC_ARG_OUT(OutputBuffer) + LOG_FUNC_ARG(OutputBufferLength) + LOG_FUNC_END; + + NTSTATUS ret = STATUS_SUCCESS; + + switch (IoControlCode) + { + case 0x4D014: // IOCTL_SCSI_PASS_THROUGH_DIRECT + { + PSCSI_PASS_THROUGH_DIRECT PassThrough = (PSCSI_PASS_THROUGH_DIRECT)InputBuffer; + PDVDX2_AUTHENTICATION Authentication = (PDVDX2_AUTHENTICATION)PassThrough->DataBuffer; + + // Should be just enough info to pass XapiVerifyMediaInDrive + Authentication->AuthenticationPage.CDFValid = 1; + Authentication->AuthenticationPage.PartitionArea = 1; + Authentication->AuthenticationPage.Authentication = 1; + break; + } + case 0x70000: // IOCTL_DISK_GET_DRIVE_GEOMETRY + { + PDISK_GEOMETRY DiskGeometry = (PDISK_GEOMETRY)OutputBuffer; + + DiskGeometry->MediaType = FixedMedia; + DiskGeometry->TracksPerCylinder = 1; + DiskGeometry->SectorsPerTrack = 1; + DiskGeometry->BytesPerSector = 512; + DiskGeometry->Cylinders.QuadPart = 0x1400000; // Around 10GB, size of stock xbox HDD + break; + } + case 0x74004: // IOCTL_DISK_GET_PARTITION_INFO + { + PPARTITION_INFORMATION partitioninfo = (PPARTITION_INFORMATION)OutputBuffer; + + XboxPartitionTable partitionTable = CxbxGetPartitionTable(); + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); + + // Now we read from the partition table, to fill in the partitionInfo struct + partitioninfo->PartitionNumber = partitionNumber; + partitioninfo->StartingOffset.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBAStart * 512; + partitioninfo->PartitionLength.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize * 512; + partitioninfo->HiddenSectors = partitionTable.TableEntries[partitionNumber - 1].Reserved; + partitioninfo->RecognizedPartition = true; + break; + } + default: + LOG_UNIMPLEMENTED(); + } + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C5 - NtDuplicateObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(197) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtDuplicateObject +( + HANDLE SourceHandle, + HANDLE *TargetHandle, + DWORD Options +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(SourceHandle) + LOG_FUNC_ARG(TargetHandle) + LOG_FUNC_ARG(Options) + LOG_FUNC_END; + + NTSTATUS ret = STATUS_SUCCESS; + + if (IsEmuHandle(SourceHandle)) { + EmuHandle* iEmuHandle = HandleToEmuHandle(SourceHandle); + ret = iEmuHandle->NtDuplicateObject(TargetHandle, Options); +/* + PVOID Object; + + ret = ObReferenceObjectByHandle(SourceHandle, /*ObjectType=* /NULL, &Object); + if (NT_SUCCESS(ret)) { + if (ObpIsFlagSet(Options, DUPLICATE_CLOSE_SOURCE)) + NtClose(SourceHandle); + + status = ObOpenObjectByPointer(Object, OBJECT_TO_OBJECT_HEADER(Object)->Type, /*OUT* /TargetHandle); + ObDereferenceObject(Object); + } + else + *TargetHandle = NULL; +*/ + } + else + { + // TODO : What arguments should we use? + const ACCESS_MASK DesiredAccess = 0; + const ULONG Attributes = 0; + + // redirect to Win2k/XP + ret = NtDll::NtDuplicateObject( + /*SourceProcessHandle=*/g_CurrentProcessHandle, + SourceHandle, + /*TargetProcessHandle=*/g_CurrentProcessHandle, + TargetHandle, + DesiredAccess, + Attributes, + Options); + } + + if (ret != STATUS_SUCCESS) + EmuLog(LOG_LEVEL::WARNING, "Object was not duplicated!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C6 - NtFlushBuffersFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(198) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtFlushBuffersFile +( + PVOID FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_END; + NTSTATUS ret = STATUS_SUCCESS; + + if (IsEmuHandle(FileHandle)) + LOG_UNIMPLEMENTED(); + else + ret = NtDll::NtFlushBuffersFile(FileHandle, (NtDll::IO_STATUS_BLOCK*)IoStatusBlock); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C7 - NtFreeVirtualMemory() +// ****************************************************************** +// Frees virtual memory. +// +// Differences from NT: There is no ProcessHandle parameter. +XBSYSAPI EXPORTNUM(199) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtFreeVirtualMemory +( + IN OUT PVOID *BaseAddress, + IN OUT PULONG FreeSize, + IN ULONG FreeType +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(BaseAddress) + LOG_FUNC_ARG(FreeSize) + LOG_FUNC_ARG_TYPE(ALLOCATION_TYPE, FreeType) + LOG_FUNC_END; + + NTSTATUS ret = g_VMManager.XbFreeVirtualMemory((VAddr*)BaseAddress, (size_t*)FreeSize, FreeType); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C8 - NtFsControlFile +// ****************************************************************** +XBSYSAPI EXPORTNUM(200) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtFsControlFile +( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG FsControlCode, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(FsControlCode) + LOG_FUNC_ARG(InputBuffer) + LOG_FUNC_ARG(InputBufferLength) + LOG_FUNC_ARG(OutputBuffer) + LOG_FUNC_ARG(OutputBufferLength) + LOG_FUNC_END; + + NTSTATUS ret = STATUS_INVALID_PARAMETER; + + switch (FsControlCode) { + case 0x00090020: // FSCTL_DISMOUNT_VOLUME + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); + if (partitionNumber > 0) { + CxbxFormatPartitionByHandle(FileHandle); + ret = STATUS_SUCCESS; + } + break; + } + + LOG_UNIMPLEMENTED(); + RETURN(ret); +} + +// ****************************************************************** +// * 0x00C9 - NtOpenDirectoryObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(201) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenDirectoryObject +( + OUT PHANDLE DirectoryHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + LOG_FORWARD("ObOpenObjectByName"); + + return ObOpenObjectByName(ObjectAttributes, &ObDirectoryObjectType, NULL, DirectoryHandle); +} + +// ****************************************************************** +// * 0x00CA - NtOpenFile() +// ****************************************************************** +// Opens a file or device object. Same as calling: +// NtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, +// IoStatusBlock, NULL, 0, ShareAccess, OPEN_EXISTING, OpenOptions); +// +// Differences from NT: See NtCreateFile. +XBSYSAPI EXPORTNUM(202) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenFile +( + OUT PHANDLE FileHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN ULONG ShareAccess, + IN ULONG OpenOptions +) +{ + LOG_FORWARD("IoCreateFile"); + + return xboxkrnl::IoCreateFile( + FileHandle, + DesiredAccess, + ObjectAttributes, + IoStatusBlock, + /*AllocationSize=*/NULL, + /*FileAttributes=*/0, + ShareAccess, + /*Disposition=*/FILE_OPEN, + /*CreateOptions=*/OpenOptions, + /*Options=*/0); +} + +// ****************************************************************** +// * 0x00CB - NtOpenSymbolicLinkObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(203) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtOpenSymbolicLinkObject +( + OUT PHANDLE LinkHandle, + IN POBJECT_ATTRIBUTES ObjectAttributes +) +{ + /* TODO : + LOG_FORWARD("ObOpenObjectByName"); + + return ObOpenObjectByName(ObjectAttributes, &ObSymbolicLinkObjectType, NULL, LinkHandle); + */ + LOG_FUNC_BEGIN + LOG_FUNC_ARG_OUT(LinkHandle) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_END; + + NTSTATUS ret = STATUS_OBJECT_PATH_NOT_FOUND; + EmuNtSymbolicLinkObject* symbolicLinkObject = + FindNtSymbolicLinkObjectByName(PSTRING_to_string(ObjectAttributes->ObjectName)); + + if (symbolicLinkObject != NULL) + { + // Return a new handle (which is an EmuHandle, actually) : + *LinkHandle = symbolicLinkObject->NewHandle(); + ret = STATUS_SUCCESS; + } + + if (ret != STATUS_SUCCESS) + EmuLog(LOG_LEVEL::WARNING, "NtOpenSymbolicLinkObject failed! (%s)", NtStatusToString(ret)); + else + EmuLog(LOG_LEVEL::DEBUG, "NtOpenSymbolicLinkObject LinkHandle^ = 0x%.8X", *LinkHandle); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00CC - NtProtectVirtualMemory() +// ****************************************************************** +XBSYSAPI EXPORTNUM(204) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtProtectVirtualMemory +( + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T RegionSize, + IN ULONG NewProtect, + OUT PULONG OldProtect +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(BaseAddress) + LOG_FUNC_ARG(RegionSize) + LOG_FUNC_ARG_TYPE(PROTECTION_TYPE, NewProtect) + LOG_FUNC_ARG_OUT(OldProtect) + LOG_FUNC_END; + + + DWORD Perms = NewProtect; + NTSTATUS ret = g_VMManager.XbVirtualProtect((VAddr*)BaseAddress, (size_t*)RegionSize, &Perms); + *OldProtect = Perms; + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00CD - NtPulseEvent() +// ****************************************************************** +XBSYSAPI EXPORTNUM(205) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtPulseEvent +( + IN HANDLE EventHandle, + OUT PLONG PreviousState OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(EventHandle) + LOG_FUNC_ARG_OUT(PreviousState) + LOG_FUNC_END; + + // redirect to Windows NT + // TODO : Untested + NTSTATUS ret = NtDll::NtPulseEvent( + EventHandle, + /*OUT*/PreviousState); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtPulseEvent failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00CE - NtQueueApcThread() +// ****************************************************************** +XBSYSAPI EXPORTNUM(206) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueueApcThread +( + IN HANDLE ThreadHandle, + IN PIO_APC_ROUTINE ApcRoutine, + IN PVOID ApcRoutineContext OPTIONAL, + IN PIO_STATUS_BLOCK ApcStatusBlock OPTIONAL, + IN ULONG ApcReserved OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ThreadHandle) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcRoutineContext) + LOG_FUNC_ARG(ApcStatusBlock) + LOG_FUNC_ARG(ApcReserved) + LOG_FUNC_END; + + // In order for NtQueueApcThread or QueueUserAPC to work, you must... I repeat... + // YOU MUST duplicate the handle with the appropriate permissions first! So far, + // the only game that I know of using this is Metal Slug 3, and it won't launch + // without it. Other SNK games might use it also, beware. + + // TODO: Use our implementation of NtDuplicateObject instead? + + HANDLE hApcThread = NULL; + + // Just to be safe, let's see if the appropriate permissions are even set for the + // target thread first... + + NTSTATUS ret = NtDll::NtQueueApcThread( + (NtDll::HANDLE)ThreadHandle, + (NtDll::PIO_APC_ROUTINE)ApcRoutine, + ApcRoutineContext, + (NtDll::PIO_STATUS_BLOCK)ApcStatusBlock, + ApcReserved); + + if( FAILED( ret ) ) + { + EmuLog(LOG_LEVEL::WARNING, "Duplicating handle with THREAD_SET_CONTEXT..." ); + + // If we get here, then attempt to duplicate the thread. + if(!DuplicateHandle(g_CurrentProcessHandle, ThreadHandle, g_CurrentProcessHandle, &hApcThread, THREAD_SET_CONTEXT,FALSE,0)) + EmuLog(LOG_LEVEL::WARNING, "DuplicateHandle failed!"); + else + { + g_DuplicateHandles[ThreadHandle] = hApcThread; // Save this thread because we'll need to de-reference it later + EmuLog(LOG_LEVEL::DEBUG, "DuplicateHandle returned 0x%X (ThreadId 0x%.4X)", hApcThread, GetThreadId( hApcThread ) ); + } + + + ret = NtDll::NtQueueApcThread( + (NtDll::HANDLE)hApcThread, + (NtDll::PIO_APC_ROUTINE)ApcRoutine, + ApcRoutineContext, + (NtDll::PIO_STATUS_BLOCK)ApcStatusBlock, + ApcReserved); + } + if (FAILED(ret)) + { + EmuLog(LOG_LEVEL::WARNING, "NtQueueApcThread failed!"); + CloseHandle( g_DuplicateHandles[ThreadHandle] ); + g_DuplicateHandles.erase( ThreadHandle ); + } + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00CF - NtQueryDirectoryFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(207) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryDirectoryFile +( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PVOID ApcRoutine, // Todo: define this routine's prototype + IN PVOID ApcContext, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT FILE_DIRECTORY_INFORMATION *FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN PSTRING FileMask, + IN BOOLEAN RestartScan +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG_OUT(FileInformation) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(FileInformationClass) + LOG_FUNC_ARG(FileMask) + LOG_FUNC_ARG(RestartScan) + LOG_FUNC_END; + + NTSTATUS ret; + + if (FileInformationClass != FileDirectoryInformation) // Due to unicode->string conversion + CxbxKrnlCleanup("Unsupported FileInformationClass"); + + NtDll::UNICODE_STRING NtFileMask; + + wchar_t wszObjectName[MAX_PATH]; + + // initialize FileMask + { + if (FileMask != 0) { + // Xbox expects directories to be listed when *.* is passed + if (strncmp(FileMask->Buffer, "*.*", FileMask->Length) == 0) { + FileMask->Length = 1; + FileMask->Buffer = "*"; + } + + mbstowcs(/*Dest=*/wszObjectName, /*Source=*/FileMask->Buffer, /*MaxCount=*/MAX_PATH); + } else + mbstowcs(/*Dest=*/wszObjectName, /*Source=*/"", /*MaxCount=*/MAX_PATH); + + NtDll::RtlInitUnicodeString(&NtFileMask, wszObjectName); + } + + NtDll::FILE_DIRECTORY_INFORMATION *NtFileDirInfo = + (NtDll::FILE_DIRECTORY_INFORMATION *) malloc(NtFileDirectoryInformationSize + NtPathBufferSize); + + // Short-hand pointer to Nt filename : + wchar_t *wcstr = NtFileDirInfo->FileName; + char *mbstr = FileInformation->FileName; + + // Go, query that directory : + do + { + ZeroMemory(wcstr, MAX_PATH * sizeof(wchar_t)); + + ret = NtDll::NtQueryDirectoryFile( + FileHandle, + Event, + (NtDll::PIO_APC_ROUTINE)ApcRoutine, + ApcContext, + (NtDll::IO_STATUS_BLOCK*)IoStatusBlock, + /*FileInformation=*/NtFileDirInfo, + NtFileDirectoryInformationSize + NtPathBufferSize, + (NtDll::FILE_INFORMATION_CLASS)FileInformationClass, + /*ReturnSingleEntry=*/TRUE, + &NtFileMask, + RestartScan + ); + + RestartScan = FALSE; + } + // Xbox does not return . and .. + while (wcscmp(wcstr, L".") == 0 || wcscmp(wcstr, L"..") == 0); + + // convert from PC to Xbox + { + // TODO : assert that NtDll::FILE_DIRECTORY_INFORMATION has same members and size as xboxkrnl::FILE_DIRECTORY_INFORMATION + memcpy(/*Dst=*/FileInformation, /*Src=*/NtFileDirInfo, /*Size=*/NtFileDirectoryInformationSize); + wcstombs(/*Dest=*/mbstr, /*Source=*/wcstr, MAX_PATH); + FileInformation->FileNameLength /= sizeof(wchar_t); + } + + // TODO: Cache the last search result for quicker access with CreateFile (xbox does this internally!) + free(NtFileDirInfo); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D0 - NtQueryDirectoryObject +// ****************************************************************** +XBSYSAPI EXPORTNUM(208) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryDirectoryObject +( + IN HANDLE DirectoryHandle, + OUT PVOID Buffer, + IN ULONG Length, + IN BOOLEAN RestartScan, + IN OUT PULONG Context, + OUT PULONG ReturnedLength OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(DirectoryHandle) + LOG_FUNC_ARG_OUT(Buffer) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(RestartScan) + LOG_FUNC_ARG(Context) + LOG_FUNC_ARG_OUT(ReturnedLength) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00D1 - NtQueryEvent() +// ****************************************************************** +XBSYSAPI EXPORTNUM(209) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryEvent +( + IN HANDLE EventHandle, + OUT PEVENT_BASIC_INFORMATION EventInformation +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(EventHandle) + LOG_FUNC_ARG_OUT(EventInformation) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtQueryEvent( + (NtDll::HANDLE)EventHandle, + /*EventInformationClass*/NtDll::EVENT_INFORMATION_CLASS::EventBasicInformation, + EventInformation, + sizeof(EVENT_BASIC_INFORMATION), + /*ReturnLength=*/nullptr); + + if (ret != STATUS_SUCCESS) + EmuLog(LOG_LEVEL::WARNING, "NtQueryEvent failed! (%s)", NtStatusToString(ret)); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D2 - NtQueryFullAttributesFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(210) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryFullAttributesFile +( + IN POBJECT_ATTRIBUTES ObjectAttributes, + OUT xboxkrnl::PFILE_NETWORK_OPEN_INFORMATION Attributes +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG_OUT(Attributes) + LOG_FUNC_END; + + // __asm int 3; + NativeObjectAttributes nativeObjectAttributes; + NtDll::FILE_NETWORK_OPEN_INFORMATION nativeNetOpenInfo; + + NTSTATUS ret = CxbxObjectAttributesToNT( + ObjectAttributes, + /*var*/nativeObjectAttributes, + "NtQueryFullAttributesFile"); + + if (ret == STATUS_SUCCESS) + ret = NtDll::NtQueryFullAttributesFile( + nativeObjectAttributes.NtObjAttrPtr, + &nativeNetOpenInfo); + + // Convert Attributes to Xbox + NTToXboxFileInformation(&nativeNetOpenInfo, Attributes, FileNetworkOpenInformation, sizeof(xboxkrnl::FILE_NETWORK_OPEN_INFORMATION)); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtQueryFullAttributesFile failed! (0x%.08X)", ret); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D3 - NtQueryInformationFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(211) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryInformationFile +( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG_OUT(FileInformation) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(FileInformationClass) + LOG_FUNC_END; + + NTSTATUS ret; + PVOID ntFileInfo; + + // Start with sizeof(corresponding struct) + size_t bufferSize = XboxFileInfoStructSizes[FileInformationClass]; + + // We need to retry the operation in case the buffer is too small to fit the data + do + { + ntFileInfo = malloc(bufferSize); + + ret = NtDll::NtQueryInformationFile( + FileHandle, + (NtDll::PIO_STATUS_BLOCK)IoStatusBlock, + ntFileInfo, + bufferSize, + (NtDll::FILE_INFORMATION_CLASS)FileInformationClass); + + // Buffer is too small; make a larger one + if (ret == STATUS_BUFFER_OVERFLOW) + { + free(ntFileInfo); + + bufferSize *= 2; + // Bail out if the buffer gets too big + if (bufferSize > 65536) + return STATUS_INVALID_PARAMETER; // TODO: what's the appropriate error code to return here? + } + } while (ret == STATUS_BUFFER_OVERFLOW); + + // Convert and copy NT data to the given Xbox struct + NTSTATUS convRet = NTToXboxFileInformation(ntFileInfo, FileInformation, FileInformationClass, Length); + + // Make sure to free the memory first + free(ntFileInfo); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtQueryInformationFile failed! (0x%.08X)", ret); + + // Prioritize the buffer overflow over real return code, + // in case the Xbox program decides to follow the same procedure above + if (convRet == STATUS_BUFFER_OVERFLOW) + return convRet; + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D4 - NtQueryIoCompletion +// ****************************************************************** +XBSYSAPI EXPORTNUM(212) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryIoCompletion +( + IN HANDLE IoCompletionHandle, + OUT PIO_COMPLETION_BASIC_INFORMATION IoCompletionInformation +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(IoCompletionHandle) + LOG_FUNC_ARG_OUT(IoCompletionInformation) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00D5 - NtQueryMutant() +// ****************************************************************** +XBSYSAPI EXPORTNUM(213) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryMutant +( + IN HANDLE MutantHandle, + OUT PMUTANT_BASIC_INFORMATION MutantInformation +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(MutantHandle) + LOG_FUNC_ARG_OUT(MutantInformation) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtQueryMutant( + (NtDll::HANDLE)MutantHandle, + /*MutantInformationClass*/NtDll::MUTANT_INFORMATION_CLASS::MutantBasicInformation, + MutantInformation, + sizeof(MUTANT_BASIC_INFORMATION), + /*ReturnLength=*/nullptr); + + if (ret != STATUS_SUCCESS) + EmuLog(LOG_LEVEL::WARNING, "NtQueryMutant failed! (%s)", NtStatusToString(ret)); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D6 - NtQuerySemaphore() +// ****************************************************************** +XBSYSAPI EXPORTNUM(214) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQuerySemaphore +( + IN HANDLE SemaphoreHandle, + OUT PSEMAPHORE_BASIC_INFORMATION SemaphoreInformation +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(SemaphoreHandle) + LOG_FUNC_ARG_OUT(SemaphoreInformation) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtQuerySemaphore( + (NtDll::HANDLE)SemaphoreHandle, + /*SemaphoreInformationClass*/NtDll::SEMAPHORE_INFORMATION_CLASS::SemaphoreBasicInformation, + SemaphoreInformation, + sizeof(SEMAPHORE_BASIC_INFORMATION), + /*ReturnLength=*/nullptr); + + if (ret != STATUS_SUCCESS) + EmuLog(LOG_LEVEL::WARNING, "NtQuerySemaphore failed! (%s)", NtStatusToString(ret)); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D7 - NtQuerySymbolicLinkObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(215) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQuerySymbolicLinkObject +( + HANDLE LinkHandle, + OUT PSTRING LinkTarget, + OUT PULONG ReturnedLength OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(LinkHandle) + LOG_FUNC_ARG_OUT(LinkTarget) + LOG_FUNC_ARG_OUT(ReturnedLength) + LOG_FUNC_END; + + NTSTATUS ret = 0; + EmuNtSymbolicLinkObject* symbolicLinkObject = NULL; + + // Check that we actually got an EmuHandle : + ret = STATUS_INVALID_HANDLE; + + EmuHandle* iEmuHandle = HandleToEmuHandle(LinkHandle); + // Retrieve the NtSymbolicLinkObject and populate the output arguments : + ret = STATUS_SUCCESS; + symbolicLinkObject = (EmuNtSymbolicLinkObject*)iEmuHandle->NtObject; + + if (symbolicLinkObject->IsHostBasedPath) { + // TODO : What should we do with symbolic links + ret = STATUS_UNRECOGNIZED_VOLUME; + } else { + if (LinkTarget != NULL) + { + if (LinkTarget->MaximumLength >= symbolicLinkObject->XboxSymbolicLinkPath.length() + sizeof(char)) { + copy_string_to_PSTRING_to(symbolicLinkObject->XboxSymbolicLinkPath, LinkTarget); + } + else { + ret = STATUS_BUFFER_TOO_SMALL; + } + } + + if (ReturnedLength != NULL) + { + *ReturnedLength = symbolicLinkObject->XboxSymbolicLinkPath.length(); // Return full length (even if buffer was too small) + } + } + if (ret != STATUS_SUCCESS) + EmuLog(LOG_LEVEL::WARNING, "NtQuerySymbolicLinkObject failed! (%s)", NtStatusToString(ret)); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D8 - NtQueryTimer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(216) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryTimer +( + IN HANDLE TimerHandle, + OUT PTIMER_BASIC_INFORMATION TimerInformation +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(TimerHandle) + LOG_FUNC_ARG_OUT(TimerInformation) + LOG_FUNC_END; + + // redirect to Windows NT + // TODO : Untested + NTSTATUS ret = NtDll::NtQueryTimer( + TimerHandle, + /*TIMER_INFORMATION_CLASS*/NtDll::TimerBasicInformation, + /*OUT*/TimerInformation, + /*TimerInformationLength=*/sizeof(TIMER_BASIC_INFORMATION), + /*OUT ReturnLength*/nullptr + ); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00D9 - NtQueryVirtualMemory() +// ****************************************************************** +XBSYSAPI EXPORTNUM(217) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryVirtualMemory +( + IN PVOID BaseAddress, + OUT PMEMORY_BASIC_INFORMATION Buffer +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(BaseAddress) + LOG_FUNC_ARG_OUT(Buffer) + LOG_FUNC_END; + + if (!Buffer) + { + EmuLog(LOG_LEVEL::WARNING, "NtQueryVirtualMemory : PMEMORY_BASIC_INFORMATION Buffer is nullptr!\n"); + LOG_IGNORED(); + RETURN(STATUS_INVALID_PARAMETER); + } + + NTSTATUS ret = g_VMManager.XbVirtualMemoryStatistics((VAddr)BaseAddress, Buffer); + + if (ret == STATUS_SUCCESS) + { + EmuLog(LOG_LEVEL::DEBUG, " Buffer->AllocationBase = 0x%.08X", Buffer->AllocationBase); + EmuLog(LOG_LEVEL::DEBUG, " Buffer->AllocationProtect = 0x%.08X", Buffer->AllocationProtect); + EmuLog(LOG_LEVEL::DEBUG, " Buffer->BaseAddress = 0x%.08X", Buffer->BaseAddress); + EmuLog(LOG_LEVEL::DEBUG, " Buffer->RegionSize = 0x%.08X", Buffer->RegionSize); + EmuLog(LOG_LEVEL::DEBUG, " Buffer->State = 0x%.08X", Buffer->State); + EmuLog(LOG_LEVEL::DEBUG, " Buffer->Protect = 0x%.08X", Buffer->Protect); + EmuLog(LOG_LEVEL::DEBUG, " Buffer->Type = 0x%.08X", Buffer->Type); + } + + #if 0 + if (FAILED(ret)) { + EmuLog(LOG_LEVEL::WARNING, "NtQueryVirtualMemory failed (%s)!", NtStatusToString(ret)); + + // Bugfix for "Forza Motorsport", which iterates over 2 Gb of memory in 64kb chunks, + // but fails on this last query. It's not done though, as after this Forza tries to + // NtAllocateVirtualMemory at address 0x00000000 (3 times, actually) which fails too... + // + // Ported back from dxbx, translator PatrickvL + + if (BaseAddress == (PVOID)0x7FFF0000) { + Buffer->BaseAddress = BaseAddress; + Buffer->AllocationBase = BaseAddress; + Buffer->AllocationProtect = PAGE_READONLY; + Buffer->RegionSize = 64 * 1024; // size, in bytes, of the region beginning at the base address in which all pages have identical attributes + Buffer->State = 4096; // MEM_DECOMMIT | PAGE_EXECUTE_WRITECOPY etc + Buffer->Protect = PAGE_READONLY; // One of the flags listed for the AllocationProtect member is specified + Buffer->Type = 262144; // Specifies the type of pages in the region. (MEM_IMAGE, MEM_MAPPED or MEM_PRIVATE) + + ret = STATUS_SUCCESS; + + EmuLog(LOG_LEVEL::DEBUG, "NtQueryVirtualMemory: Applied fix for Forza Motorsport!"); + } + } + #endif + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00DA - NtQueryVolumeInformationFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(218) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtQueryVolumeInformationFile +( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PFILE_FS_SIZE_INFORMATION FileInformation, + IN ULONG Length, + IN FS_INFORMATION_CLASS FileInformationClass +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG_OUT(FileInformation) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(FileInformationClass) + LOG_FUNC_END; + + // FileFsSizeInformation is a special case that should read from our emulated partition table + if ((DWORD)FileInformationClass == FileFsSizeInformation) { + PFILE_FS_SIZE_INFORMATION XboxSizeInfo = (PFILE_FS_SIZE_INFORMATION)FileInformation; + + XboxPartitionTable partitionTable = CxbxGetPartitionTable(); + int partitionNumber = CxbxGetPartitionNumberFromHandle(FileHandle); + FATX_SUPERBLOCK superBlock = CxbxGetFatXSuperBlock(partitionNumber); + + XboxSizeInfo->BytesPerSector = 512; + + // In some cases, the emulated partition hasn't been formatted yet, as these are forwarded to a real folder, this doesn't actually matter. + // We just pretend they are valid by defaulting the SectorsPerAllocationUnit value to the most common for system partitions + XboxSizeInfo->SectorsPerAllocationUnit = 32; + + // If there is a valid cluster size, we calculate SectorsPerAllocationUnit from that instead + if (superBlock.ClusterSize > 0) { + XboxSizeInfo->SectorsPerAllocationUnit = superBlock.ClusterSize; + } + + XboxSizeInfo->TotalAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; + XboxSizeInfo->AvailableAllocationUnits.QuadPart = partitionTable.TableEntries[partitionNumber - 1].LBASize / XboxSizeInfo->SectorsPerAllocationUnit; + + RETURN(STATUS_SUCCESS); + } + + // Get the required size for the host buffer + // This may differ than the xbox buffer size so we also need to handle conversions! + ULONG HostBufferSize = 0; + switch ((DWORD)FileInformationClass) { + case FileFsVolumeInformation: + // Reserve a large enough buffer for the file information + // including the variable length path field + HostBufferSize = sizeof(NtDll::FILE_FS_VOLUME_INFORMATION) + MAX_PATH; + break; + case FileFsLabelInformation: + HostBufferSize = sizeof(NtDll::FILE_FS_LABEL_INFORMATION); + break; + case FileFsDeviceInformation: + HostBufferSize = sizeof(NtDll::FILE_FS_DEVICE_INFORMATION); + break; + case FileFsAttributeInformation: + HostBufferSize = sizeof(NtDll::FILE_FS_ATTRIBUTE_INFORMATION); + break; + case FileFsFullSizeInformation: + HostBufferSize = sizeof(NtDll::FILE_FS_FULL_SIZE_INFORMATION); + break; + case FileFsObjectIdInformation: + HostBufferSize = sizeof(NtDll::FILE_FS_OBJECTID_INFORMATION); + break; + } + + PVOID NativeFileInformation = _aligned_malloc(HostBufferSize, 8); + + NTSTATUS ret = NtDll::NtQueryVolumeInformationFile( + FileHandle, + (NtDll::PIO_STATUS_BLOCK)IoStatusBlock, + (NtDll::PFILE_FS_SIZE_INFORMATION)NativeFileInformation, HostBufferSize, + (NtDll::FS_INFORMATION_CLASS)FileInformationClass); + + // Convert Xbox NativeFileInformation to FileInformation + if (ret == STATUS_SUCCESS) { + switch ((DWORD)FileInformationClass) { + case FileFsVolumeInformation: { + PFILE_FS_VOLUME_INFORMATION XboxVolumeInfo = (PFILE_FS_VOLUME_INFORMATION)FileInformation; + NtDll::PFILE_FS_VOLUME_INFORMATION HostVolumeInfo = (NtDll::PFILE_FS_VOLUME_INFORMATION)NativeFileInformation; + + // Most options can just be directly copied to the Xbox version, only the strings differ + XboxVolumeInfo->VolumeCreationTime.QuadPart = HostVolumeInfo->VolumeCreationTime.QuadPart; + XboxVolumeInfo->VolumeSerialNumber = HostVolumeInfo->VolumeSerialNumber; + XboxVolumeInfo->VolumeLabelLength = HostVolumeInfo->VolumeLabelLength; + XboxVolumeInfo->SupportsObjects = HostVolumeInfo->SupportsObjects; + + // Convert strings to the Xbox format + wcstombs(XboxVolumeInfo->VolumeLabel, HostVolumeInfo->VolumeLabel, HostVolumeInfo->VolumeLabelLength); + } + break; + default: + // For all other types, just do a memcpy and hope for the best! + EmuLog(LOG_LEVEL::WARNING, "NtQueryVolumeInformationFile: Unknown FileInformationClass"); + memcpy_s(FileInformation, Length, NativeFileInformation, HostBufferSize); + break; + } + } + + _aligned_free(NativeFileInformation); + + if (FAILED(ret)) { + EmuLog(LOG_LEVEL::WARNING, "NtQueryVolumeInformationFile failed! (%s)\n", NtStatusToString(ret)); + } + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00DB - NtReadFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(219) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReadFile +( + IN HANDLE FileHandle, // TODO: correct paramters + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG_OUT(Buffer) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(ByteOffset) + LOG_FUNC_END; + + // Halo... + // if(ByteOffset != 0 && ByteOffset->QuadPart == 0x00120800) + // _asm int 3 + + if (CxbxDebugger::CanReport()) + { + uint64_t Offset = ~0; + if (ByteOffset) + Offset = ByteOffset->QuadPart; + + CxbxDebugger::ReportFileRead(FileHandle, Length, Offset); + } + + NTSTATUS ret = NtDll::NtReadFile( + FileHandle, + Event, + (PVOID)ApcRoutine, + ApcContext, + IoStatusBlock, + Buffer, + Length, + (NtDll::LARGE_INTEGER*)ByteOffset, + /*Key=*/nullptr); + + if (FAILED(ret)) { + EmuLog(LOG_LEVEL::WARNING, "NtReadFile Failed! (0x%.08X)", ret); + } + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00DC - NtReadFileScatter +// ****************************************************************** +XBSYSAPI EXPORTNUM(220) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReadFileScatter +( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PFILE_SEGMENT_ELEMENT SegmentArray, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(SegmentArray) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(ByteOffset) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00DD - NtReleaseMutant() +// ****************************************************************** +XBSYSAPI EXPORTNUM(221) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReleaseMutant +( + IN HANDLE MutantHandle, + OUT PLONG PreviousCount +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(MutantHandle) + LOG_FUNC_ARG_OUT(PreviousCount) + LOG_FUNC_END; + + // redirect to NtCreateMutant + NTSTATUS ret = NtDll::NtReleaseMutant(MutantHandle, PreviousCount); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtReleaseMutant Failed!"); + + RETURN(STATUS_SUCCESS); // TODO : RETURN(ret); +} + +// ****************************************************************** +// * 0x00DE - NtReleaseSemaphore() +// ****************************************************************** +XBSYSAPI EXPORTNUM(222) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtReleaseSemaphore +( + IN HANDLE SemaphoreHandle, + IN ULONG ReleaseCount, + OUT PULONG PreviousCount OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(SemaphoreHandle) + LOG_FUNC_ARG(ReleaseCount) + LOG_FUNC_ARG_OUT(PreviousCount) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtReleaseSemaphore( + SemaphoreHandle, + ReleaseCount, + PreviousCount); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtReleaseSemaphore failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00DF - NtRemoveIoCompletion +// ****************************************************************** +XBSYSAPI EXPORTNUM(223) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtRemoveIoCompletion +( + IN HANDLE IoCompletionHandle, + OUT PVOID *KeyContext, + OUT PVOID *ApcContext, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PLARGE_INTEGER Timeout OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(IoCompletionHandle) + LOG_FUNC_ARG_OUT(KeyContext) + LOG_FUNC_ARG_OUT(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(Timeout) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00E0 - NtResumeThread() +// ****************************************************************** +XBSYSAPI EXPORTNUM(224) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtResumeThread +( + IN HANDLE ThreadHandle, + OUT PULONG PreviousSuspendCount +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ThreadHandle) + LOG_FUNC_ARG_OUT(PreviousSuspendCount) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtResumeThread( + ThreadHandle, + PreviousSuspendCount); + + // TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread + + //Sleep(10); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00E1 - NtSetEvent() +// ****************************************************************** +XBSYSAPI EXPORTNUM(225) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetEvent +( + IN HANDLE EventHandle, + OUT PLONG PreviousState +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(EventHandle) + LOG_FUNC_ARG_OUT(PreviousState) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtSetEvent( + EventHandle, + PreviousState); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtSetEvent Failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00E2 - NtSetInformationFile() +// ****************************************************************** +XBSYSAPI EXPORTNUM(226) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetInformationFile +( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(FileInformation) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(FileInformationClass) + LOG_FUNC_END; + + XboxToNTFileInformation(convertedFileInfo, FileInformation, FileInformationClass, &Length); + + NTSTATUS ret = NtDll::NtSetInformationFile( + FileHandle, + IoStatusBlock, + convertedFileInfo, + Length, + FileInformationClass); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00E3 - NtSetIoCompletion +// ****************************************************************** +XBSYSAPI EXPORTNUM(227) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetIoCompletion +( + IN HANDLE IoCompletionHandle, + IN PVOID KeyContext, + IN PVOID ApcContext, + IN NTSTATUS IoStatus, + IN ULONG_PTR IoStatusInformation +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(IoCompletionHandle) + LOG_FUNC_ARG(KeyContext) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG(IoStatus) + LOG_FUNC_ARG(IoStatusInformation) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00E4 - NtSetSystemTime() +// ****************************************************************** +XBSYSAPI EXPORTNUM(228) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetSystemTime +( + IN PLARGE_INTEGER SystemTime, + OUT PLARGE_INTEGER PreviousTime OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(SystemTime) + LOG_FUNC_ARG_OUT(PreviousTime) + LOG_FUNC_END; + + NTSTATUS ret = STATUS_SUCCESS; + LARGE_INTEGER NewSystemTime, OldSystemTime; + // LARGE_INTEGER LocalTime; + // TIME_FIELDS TimeFields; + + if (SystemTime == nullptr) { + ret = STATUS_ACCESS_VIOLATION; + } + else { + NtSystemTimeMtx.lock(); + NewSystemTime = *SystemTime; + if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) { + /* Convert the time and set it in HAL */ + // NOTE: disabled, as this requires emulating the RTC, which we don't yet + // ExSystemTimeToLocalTime(&NewSystemTime, &LocalTime); + // RtlTimeToTimeFields(&LocalTime, &TimeFields); + // HalSetRealTimeClock(&TimeFields); + + /* Now set system time */ + KeSetSystemTime(&NewSystemTime, &OldSystemTime); + + // Is the previous time requested? + if (PreviousTime != nullptr) { + PreviousTime->QuadPart = OldSystemTime.QuadPart; + } + } + else { + ret = STATUS_INVALID_PARAMETER; + } + NtSystemTimeMtx.unlock(); + } + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00E5 - NtSetTimerEx() +// ****************************************************************** +XBSYSAPI EXPORTNUM(229) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSetTimerEx +( + IN HANDLE TimerHandle, + IN PLARGE_INTEGER DueTime, + IN PTIMER_APC_ROUTINE TimerApcRoutine OPTIONAL, + IN KPROCESSOR_MODE ApcMode, + IN PVOID TimerContext OPTIONAL, + IN BOOLEAN WakeTimer, + IN LONG Period OPTIONAL, + OUT PBOOLEAN PreviousState OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(TimerHandle) + LOG_FUNC_ARG(DueTime) + LOG_FUNC_ARG(TimerApcRoutine) + LOG_FUNC_ARG(ApcMode) + LOG_FUNC_ARG(TimerContext) + LOG_FUNC_ARG(WakeTimer) + LOG_FUNC_ARG(Period) + LOG_FUNC_ARG_OUT(PreviousState) + LOG_FUNC_END; + + // redirect to Windows NT + // TODO : Untested + NTSTATUS ret = NtDll::NtSetTimer( + TimerHandle, + (NtDll::PLARGE_INTEGER)DueTime, + (NtDll::PTIMER_APC_ROUTINE)TimerApcRoutine, + (NtDll::PVOID)TimerContext, + WakeTimer, + Period, + /*OUT*/PreviousState); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtSetTimerEx failed!"); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00E6 - NtSignalAndWaitForSingleObjectEx +// ****************************************************************** +XBSYSAPI EXPORTNUM(230) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSignalAndWaitForSingleObjectEx +( + IN HANDLE SignalHandle, + IN HANDLE WaitHandle, + IN KPROCESSOR_MODE WaitMode, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(SignalHandle) + LOG_FUNC_ARG(WaitHandle) + LOG_FUNC_ARG(WaitMode) + LOG_FUNC_ARG(Alertable) + LOG_FUNC_ARG(Timeout) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00E7 - NtSuspendThread() +// ****************************************************************** +XBSYSAPI EXPORTNUM(231) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtSuspendThread +( + IN HANDLE ThreadHandle, + OUT PULONG PreviousSuspendCount OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ThreadHandle) + LOG_FUNC_ARG_OUT(PreviousSuspendCount) + LOG_FUNC_END; + + NTSTATUS ret = NtDll::NtSuspendThread( + ThreadHandle, + PreviousSuspendCount); + + // TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00E8 - NtUserIoApcDispatcher() +// ****************************************************************** +XBSYSAPI EXPORTNUM(232) xboxkrnl::VOID NTAPI xboxkrnl::NtUserIoApcDispatcher +( + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG Reserved +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG(IoStatusBlock) + LOG_FUNC_ARG(Reserved) + LOG_FUNC_END; + + ULONG dwErrorCode = 0; + ULONG dwTransferred = 0; + + if (NT_SUCCESS(IoStatusBlock->Status)) { + dwTransferred = (ULONG)IoStatusBlock->Information; + } else { + dwErrorCode = RtlNtStatusToDosError(IoStatusBlock->Status); + } + + LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine = (LPOVERLAPPED_COMPLETION_ROUTINE)ApcContext; + LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)CONTAINING_RECORD(IoStatusBlock, OVERLAPPED, Internal); + + (CompletionRoutine)(dwErrorCode, dwTransferred, lpOverlapped); + + EmuLog(LOG_LEVEL::DEBUG, "NtUserIoApcDispatcher Completed"); +} + +// ****************************************************************** +// * 0x00E9 - NtWaitForSingleObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(233) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForSingleObject +( + IN HANDLE Handle, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout +) +{ + LOG_FORWARD("NtWaitForMultipleObjectsEx"); + + return xboxkrnl::NtWaitForMultipleObjectsEx( + /*Count=*/1, + &Handle, + /*WaitType=*/WaitAll, + /*WaitMode=*/KernelMode, + Alertable, + Timeout + ); +} + +// ****************************************************************** +// * 0x00EA - NtWaitForSingleObjectEx() +// ****************************************************************** +XBSYSAPI EXPORTNUM(234) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForSingleObjectEx +( + IN HANDLE Handle, + IN KPROCESSOR_MODE WaitMode, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout +) +{ + LOG_FORWARD("NtWaitForMultipleObjectsEx"); + + return xboxkrnl::NtWaitForMultipleObjectsEx( + /*Count=*/1, + &Handle, + /*WaitType=*/WaitAll, + WaitMode, + Alertable, + Timeout + ); +} + +// ****************************************************************** +// * 0x00EB - NtWaitForMultipleObjectsEx() +// ****************************************************************** +XBSYSAPI EXPORTNUM(235) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWaitForMultipleObjectsEx +( + IN ULONG Count, + IN HANDLE *Handles, + IN WAIT_TYPE WaitType, + IN CHAR WaitMode, + IN BOOLEAN Alertable, + IN PLARGE_INTEGER Timeout +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Count) + LOG_FUNC_ARG(Handles) + LOG_FUNC_ARG(WaitType) + LOG_FUNC_ARG(WaitMode) + LOG_FUNC_ARG(Alertable) + LOG_FUNC_ARG(Timeout) + LOG_FUNC_END; + + return NtDll::NtWaitForMultipleObjects( + Count, + Handles, + (NtDll::OBJECT_WAIT_TYPE)WaitType, + Alertable, + (NtDll::PLARGE_INTEGER)Timeout); +} + +// ****************************************************************** +// * 0x00EC - NtWriteFile() +// ****************************************************************** +// Writes a file. +// +// Differences from NT: There is no Key parameter. +XBSYSAPI EXPORTNUM(236) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWriteFile +( + IN HANDLE FileHandle, + IN HANDLE Event, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID Buffer, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(Buffer) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(ByteOffset) + LOG_FUNC_END; + + // Halo.. + // if(ByteOffset != 0 && ByteOffset->QuadPart == 0x01C00800) + // _asm int 3 + + if (CxbxDebugger::CanReport()) + { + uint64_t Offset = ~0; + if (ByteOffset) + Offset = ByteOffset->QuadPart; + + CxbxDebugger::ReportFileWrite(FileHandle, Length, Offset); + } + + NTSTATUS ret = NtDll::NtWriteFile( + FileHandle, + Event, + (PVOID)ApcRoutine, + ApcContext, + IoStatusBlock, + Buffer, + Length, + (NtDll::LARGE_INTEGER*)ByteOffset, + /*Key=*/nullptr); + + if (FAILED(ret)) + EmuLog(LOG_LEVEL::WARNING, "NtWriteFile Failed! (0x%.08X)", ret); + + RETURN(ret); +} + +// ****************************************************************** +// * 0x00ED - NtWriteFileGather +// ****************************************************************** +XBSYSAPI EXPORTNUM(237) xboxkrnl::NTSTATUS NTAPI xboxkrnl::NtWriteFileGather +( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PFILE_SEGMENT_ELEMENT SegmentArray, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset OPTIONAL +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(FileHandle) + LOG_FUNC_ARG(Event) + LOG_FUNC_ARG(ApcRoutine) + LOG_FUNC_ARG(ApcContext) + LOG_FUNC_ARG_OUT(IoStatusBlock) + LOG_FUNC_ARG(SegmentArray) + LOG_FUNC_ARG(Length) + LOG_FUNC_ARG(ByteOffset) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00EE - NtYieldExecution() +// ****************************************************************** +XBSYSAPI EXPORTNUM(238) xboxkrnl::VOID NTAPI xboxkrnl::NtYieldExecution() +{ + // NOTE: Logging this fills the debug log far too quickly, so don't. + // LOG_FUNC(); + + NtDll::NtYieldExecution(); +} + diff --git a/src/core/kernel/exports/EmuKrnlOb.cpp b/src/core/kernel/exports/EmuKrnlOb.cpp index 2795383de..ca03a98d0 100644 --- a/src/core/kernel/exports/EmuKrnlOb.cpp +++ b/src/core/kernel/exports/EmuKrnlOb.cpp @@ -1,982 +1,982 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2016 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::OB - - -#include // For ObDirectoryObjectType, etc. -#include "Logging.h" // For LOG_FUNC() -#include "EmuKrnlLogging.h" -#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup -#include "EmuKrnl.h" // For OBJECT_TO_OBJECT_HEADER() -#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc. -#include - -#pragma warning(disable:4005) // Ignore redefined status values -#include -#pragma warning(default:4005) - -#define FIELD_OFFSET(type,field) ((ULONG)&(((type *)0)->field)) - -#define INITIALIZED_OBJECT_STRING(ObjectString, Value) \ - xboxkrnl::CHAR ObjectString##Buffer[] = Value; \ - xboxkrnl::OBJECT_STRING ObjectString = { \ - sizeof(Value) - sizeof(CHAR), \ - sizeof(Value), \ - ObjectString##Buffer \ -} - -INITIALIZED_OBJECT_STRING(ObpDosDevicesString, "\\??"); -INITIALIZED_OBJECT_STRING(ObpIoDevicesString, "\\Device"); -INITIALIZED_OBJECT_STRING(ObpWin32NamedObjectsString, "\\Win32NamedObjects"); - -xboxkrnl::POBJECT_DIRECTORY ObpDosDevicesDirectoryObject; -xboxkrnl::POBJECT_DIRECTORY ObpWin32NamedObjectsDirectoryObject; -xboxkrnl::POBJECT_DIRECTORY ObpRootDirectoryObject; -xboxkrnl::POBJECT_DIRECTORY ObpIoDevicesDirectoryObject; - -XBSYSAPI EXPORTNUM(245) xboxkrnl::OBJECT_HANDLE_TABLE xboxkrnl::ObpObjectHandleTable = { - -}; - -xboxkrnl::PVOID ObpDosDevicesDriveLetterMap['Z' - 'A' + 1]; - -xboxkrnl::BOOLEAN xboxkrnl::ObpCreatePermanentDirectoryObject( - IN xboxkrnl::POBJECT_STRING DirectoryName OPTIONAL, - OUT xboxkrnl::POBJECT_DIRECTORY *DirectoryObject -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(DirectoryName) - LOG_FUNC_ARG(DirectoryObject) - LOG_FUNC_END; - - OBJECT_ATTRIBUTES ObjectAttributes; - XB_InitializeObjectAttributes(&ObjectAttributes, DirectoryName, OBJ_PERMANENT, NULL, NULL); - - HANDLE Handle; - NTSTATUS status = NtCreateDirectoryObject(&Handle, &ObjectAttributes); - - if (!NT_SUCCESS(status)) { - RETURN(FALSE); - } - - status = ObReferenceObjectByHandle(Handle, &ObDirectoryObjectType, (PVOID *)DirectoryObject); - - if (!NT_SUCCESS(status)) { - RETURN(FALSE); - } - - NtClose(Handle); - - RETURN(TRUE); -} - -xboxkrnl::NTSTATUS xboxkrnl::ObpReferenceObjectByName( - IN HANDLE RootDirectoryHandle, - IN POBJECT_STRING ObjectName, - IN ULONG Attributes, - IN POBJECT_TYPE ObjectType, - IN OUT PVOID ParseContext OPTIONAL, - OUT PVOID *ReturnedObject -) -{ - NTSTATUS status; - *ReturnedObject = NULL; - - KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); - - OBJECT_STRING RemainingName; - if (ObjectName != NULL) { - RemainingName = *ObjectName; - } else { - RtlZeroMemory(&RemainingName, sizeof(OBJECT_STRING)); - } - - BOOLEAN ResolveSymbolicLink = TRUE; - PVOID FoundObject; - - if (RootDirectoryHandle != NULL) { - if (RootDirectoryHandle == ObDosDevicesDirectory()) { - FoundObject = ObpDosDevicesDirectoryObject; - } else if (RootDirectoryHandle == ObWin32NamedObjectsDirectory()) { - FoundObject = ObpWin32NamedObjectsDirectoryObject; - } else { - FoundObject = (POBJECT_DIRECTORY)ObpGetObjectHandleContents(RootDirectoryHandle); - - if (FoundObject == NULL) { - status = STATUS_INVALID_HANDLE; - goto CleanupAndExit; - } - } - - if ((RemainingName.Length != 0) && - (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { - status = STATUS_OBJECT_NAME_INVALID; - goto CleanupAndExit; - } - - goto OpenRootDirectory; - } - - if ((RemainingName.Length == 0) || - (RemainingName.Buffer[0] != OBJ_NAME_PATH_SEPARATOR)) { - status = STATUS_OBJECT_NAME_INVALID; - goto CleanupAndExit; - } - - if (RemainingName.Length == sizeof(CHAR)) { - RemainingName.Buffer++; - RemainingName.Length = 0; - RemainingName.MaximumLength = 0; - goto OpenRootDirectory; - } - - OBJECT_STRING ElementName; - for (;;) { - POBJECT_DIRECTORY Directory = (POBJECT_DIRECTORY)FoundObject; - ObDissectName(RemainingName, &ElementName, &RemainingName); - - if (RemainingName.Length != 0) { - if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { - status = STATUS_OBJECT_NAME_INVALID; - goto CleanupAndExit; - } - } else { - if (ObjectType == &ObSymbolicLinkObjectType) { - ResolveSymbolicLink = FALSE; - } - } - - if (!ObpLookupElementNameInDirectory(Directory, &ElementName, - ResolveSymbolicLink, &FoundObject)) { - status = (RemainingName.Length != 0) ? STATUS_OBJECT_PATH_NOT_FOUND : STATUS_OBJECT_NAME_NOT_FOUND; - goto CleanupAndExit; - } - -OpenRootDirectory: - POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(FoundObject); - - if (RemainingName.Length == 0) { - if (ObjectHeader->Type->ParseProcedure != NULL) { - goto InvokeParseProcedure; - } - - if ((ObjectType != NULL) && (ObjectType != ObjectHeader->Type)) { - status = STATUS_OBJECT_TYPE_MISMATCH; - goto CleanupAndExit; - } - - ObjectHeader->PointerCount++; - *ReturnedObject = FoundObject; - status = STATUS_SUCCESS; - goto CleanupAndExit; - } - - if (ObjectHeader->Type != &ObDirectoryObjectType) { - if (ObjectHeader->Type->ParseProcedure == NULL) { - status = STATUS_OBJECT_PATH_NOT_FOUND; - goto CleanupAndExit; - } - -InvokeParseProcedure: - ObjectHeader->PointerCount++; - KfLowerIrql(OldIrql); - - if (ObjectHeader->Type != &IoFileObjectType && (RemainingName.Buffer > ObjectName->Buffer)) { - RemainingName.Buffer--; - RemainingName.Length += sizeof(CHAR); - RemainingName.MaximumLength = RemainingName.Length; - } - - PVOID ParsedObject = NULL; - status = ObjectHeader->Type->ParseProcedure(FoundObject, ObjectType, Attributes, ObjectName, &RemainingName, ParseContext, &ParsedObject); - ObfDereferenceObject(FoundObject); - - if (NT_SUCCESS(status)) { - if ((ObjectType == NULL) || (ObjectType == OBJECT_TO_OBJECT_HEADER(ParsedObject)->Type)) { - *ReturnedObject = ParsedObject; - status = STATUS_SUCCESS; - } else { - ObfDereferenceObject(ParsedObject); - status = STATUS_OBJECT_TYPE_MISMATCH; - } - } - - return status; - } - } -CleanupAndExit: - KfLowerIrql(OldIrql); - return status; -} - -xboxkrnl::BOOLEAN xboxkrnl::ObInitSystem() -{ - ObpObjectHandleTable.HandleCount = 0; - ObpObjectHandleTable.FirstFreeTableEntry = -1; - ObpObjectHandleTable.NextHandleNeedingPool = 0; - ObpObjectHandleTable.RootTable = NULL; - - RtlZeroMemory(ObpDosDevicesDriveLetterMap, sizeof(ObpDosDevicesDriveLetterMap)); - - if (!ObpCreatePermanentDirectoryObject(NULL, &ObpRootDirectoryObject)) { - return FALSE; - } - - if (!ObpCreatePermanentDirectoryObject(&ObpDosDevicesString, &ObpDosDevicesDirectoryObject)) { - return FALSE; - } - - if (!ObpCreatePermanentDirectoryObject(&ObpIoDevicesString, &ObpIoDevicesDirectoryObject)) { - return FALSE; - } - - if (!ObpCreatePermanentDirectoryObject(&ObpWin32NamedObjectsString, &ObpWin32NamedObjectsDirectoryObject)) { - return FALSE; - } - - return TRUE; -} - -xboxkrnl::BOOLEAN xboxkrnl::ObpExtendObjectHandleTable() -{ - PVOID* NewTable = (PVOID*)ExAllocatePoolWithTag(sizeof(PVOID) * OB_HANDLES_PER_TABLE, 'tHbO'); - if (NewTable == NULL) { - return FALSE; - } - - PVOID **NewRootTable; - SIZE_T NewRootTableSize; - if ((HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool) & (sizeof(PVOID) * OB_HANDLES_PER_SEGMENT - 1)) == 0) { - if (ObpObjectHandleTable.NextHandleNeedingPool == NULL) { - NewRootTable = ObpObjectHandleTable.BuiltinRootTable; - } else { - SIZE_T OldRootTableSize = HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool) / (sizeof(PVOID*) * OB_HANDLES_PER_TABLE); - NewRootTableSize = OldRootTableSize + OB_TABLES_PER_SEGMENT; - - NewRootTable = (PVOID**)ExAllocatePoolWithTag(sizeof(PVOID*) * NewRootTableSize, 'rHbO'); - if (NewRootTable == NULL) { - ExFreePool(NewTable); - return FALSE; - } - - RtlCopyMemory(NewRootTable, ObpObjectHandleTable.RootTable, sizeof(PVOID*) * OldRootTableSize); - - if (ObpObjectHandleTable.RootTable != ObpObjectHandleTable.BuiltinRootTable) { - ExFreePool(ObpObjectHandleTable.RootTable); - } - } - - ObpObjectHandleTable.RootTable = NewRootTable; - } - - ObpGetTableFromHandle(ObpObjectHandleTable.NextHandleNeedingPool) = NewTable; - HANDLE Handle = ObpObjectHandleTable.NextHandleNeedingPool; - PVOID *HandleContents = NewTable; - LONG_PTR FreeHandleLink = ObpEncodeFreeHandleLink(Handle); - ObpObjectHandleTable.FirstFreeTableEntry = FreeHandleLink; - - for (ULONG Index = 0; Index < OB_HANDLES_PER_TABLE - 1; Index++) { - FreeHandleLink += sizeof(PVOID); - *HandleContents++ = (PVOID)FreeHandleLink; - } - - *HandleContents = (PVOID)-1; - - if (Handle == NULL) { - HandleContents = NewTable; - ObpObjectHandleTable.FirstFreeTableEntry = (LONG_PTR)*HandleContents; - *HandleContents = NULL; - } - - ObpObjectHandleTable.NextHandleNeedingPool = (HANDLE)(HandleToLong(Handle) + (sizeof(PVOID) * OB_HANDLES_PER_TABLE)); - - return TRUE; -} - -xboxkrnl::HANDLE xboxkrnl::ObpCreateObjectHandle(xboxkrnl::PVOID Object) -{ - LOG_FUNC_ONE_ARG(Object); - - HANDLE Handle; - PVOID *HandleContents; - - if (ObpObjectHandleTable.FirstFreeTableEntry == -1) { - if (!ObpExtendObjectHandleTable()) { - return NULL; - } - } - - Handle = (HANDLE)ObpDecodeFreeHandleLink(ObpObjectHandleTable.FirstFreeTableEntry); - HandleContents = ObpGetHandleContentsPointer(Handle); - ObpObjectHandleTable.FirstFreeTableEntry = (LONG_PTR)*HandleContents; - ObpObjectHandleTable.HandleCount++; - OBJECT_TO_OBJECT_HEADER(Object)->HandleCount++; - *HandleContents = Object; - return Handle; -} - -xboxkrnl::VOID xboxkrnl::ObDissectName(OBJECT_STRING Path, POBJECT_STRING FirstName, POBJECT_STRING RemainingName) -{ - ULONG i = 0; - - FirstName->Length = 0; - FirstName->MaximumLength = 0; - FirstName->Buffer = NULL; - - RemainingName->Length = 0; - RemainingName->MaximumLength = 0; - RemainingName->Buffer = NULL; - - ULONG PathLength = Path.Length / sizeof(CHAR); - - if (PathLength == 0) { - return; - } - - if (Path.Buffer[0] == '\\') { - i = 1; - } - - ULONG FirstNameStart; - for (FirstNameStart = i; (i < PathLength) && (Path.Buffer[i] != '\\'); i += 1) { - ; - } - - FirstName->Length = (USHORT)((i - FirstNameStart) * sizeof(CHAR)); - FirstName->MaximumLength = FirstName->Length; - FirstName->Buffer = &Path.Buffer[FirstNameStart]; - - if (i < PathLength) { - RemainingName->Length = (USHORT)((PathLength - (i + 1)) * sizeof(CHAR)); - RemainingName->MaximumLength = RemainingName->Length; - RemainingName->Buffer = &Path.Buffer[i + 1]; - } - - return; -} - -// ****************************************************************** -// * 0x00EF - ObCreateObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(239) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObCreateObject -( - IN POBJECT_TYPE ObjectType, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN ULONG ObjectBodySize, - OUT PVOID *Object -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ObjectType) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(ObjectBodySize) - LOG_FUNC_ARG_OUT(Object) - LOG_FUNC_END; - - if (ObjectAttributes == NULL || ObjectAttributes->ObjectName == NULL) { - POBJECT_HEADER ObjectHeader = (POBJECT_HEADER)ObjectType->AllocateProcedure(FIELD_OFFSET(OBJECT_HEADER, Body) + ObjectBodySize, ObjectType->PoolTag); - - if (ObjectHeader == nullptr) { - RETURN(STATUS_INSUFFICIENT_RESOURCES); - } - - ObjectHeader->PointerCount = 1; - ObjectHeader->HandleCount = 0; - ObjectHeader->Type = ObjectType; - ObjectHeader->Flags = 0; - - *Object = &ObjectHeader->Body; - - RETURN(STATUS_SUCCESS); - } - - OBJECT_STRING RemainingName = *ObjectAttributes->ObjectName; - OBJECT_STRING ElementName; - ElementName.Buffer = NULL; - ElementName.Length = 0; - - while (RemainingName.Length != 0) { - ObDissectName(RemainingName, &ElementName, &RemainingName); - if ((RemainingName.Length != 0) && (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { - return STATUS_OBJECT_NAME_INVALID; - } - } - - if (ElementName.Length == 0) { - return STATUS_OBJECT_NAME_INVALID; - } - - ObjectBodySize = ALIGN_UP(ObjectBodySize, ULONG); - - POBJECT_HEADER_NAME_INFO ObjectNameInfo = (POBJECT_HEADER_NAME_INFO)ObjectType->AllocateProcedure( - sizeof(OBJECT_HEADER_NAME_INFO) + FIELD_OFFSET(OBJECT_HEADER, Body) + - ObjectBodySize + ElementName.Length, ObjectType->PoolTag); - - if (ObjectNameInfo == NULL) { - return STATUS_INSUFFICIENT_RESOURCES; - } - - POBJECT_HEADER ObjectHeader = (POBJECT_HEADER)(ObjectNameInfo + 1); - ObjectNameInfo->ChainLink = NULL; - ObjectNameInfo->Directory = NULL; - ObjectNameInfo->Name.Buffer = (PSTR)((PUCHAR)&ObjectHeader->Body + ObjectBodySize); - ObjectNameInfo->Name.Length = ElementName.Length; - ObjectNameInfo->Name.MaximumLength = ElementName.Length; - - RtlCopyMemory(ObjectNameInfo->Name.Buffer, ElementName.Buffer, ElementName.Length); - - ObjectHeader->PointerCount = 1; - ObjectHeader->HandleCount = 0; - ObjectHeader->Type = ObjectType; - ObjectHeader->Flags = OB_FLAG_NAMED_OBJECT; - - *Object = &ObjectHeader->Body; - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00F0 - ObDirectoryObjectType -// ****************************************************************** -XBSYSAPI EXPORTNUM(240) xboxkrnl::OBJECT_TYPE xboxkrnl::ObDirectoryObjectType = -{ - xboxkrnl::ExAllocatePoolWithTag, - xboxkrnl::ExFreePool, - NULL, - NULL, - NULL, - NULL, // TODO : &xboxkrnl::ObpDefaultObject, - 'eriD' // = first four characters of "Directory" in reverse -}; - -xboxkrnl::PVOID xboxkrnl::ObpGetObjectHandleContents(HANDLE Handle) -{ - PVOID *HandleContents; - PVOID Object; - Handle = ObpMaskOffApplicationBits(Handle); - - if (HandleToUlong(Handle) < HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool)) { - HandleContents = ObpGetHandleContentsPointer(Handle); - Object = *HandleContents; - - if (Object != NULL && !ObpIsFreeHandleLink(Object)) { - return Object; - } - } - - return NULL; -} - -xboxkrnl::ULONG FASTCALL xboxkrnl::ObpComputeHashIndex( - IN POBJECT_STRING ElementName -) -{ - - ULONG HashIndex = 0; - PUCHAR Buffer = (PUCHAR)ElementName->Buffer; - PUCHAR BufferEnd = Buffer + ElementName->Length; - - // Calculate hash of string data - UCHAR Char; - while (Buffer < BufferEnd) { - Char = *Buffer++; - // Skip special characters - if (Char >= 0x80) { - continue; - } - - // Force all characters to be lowercase - Char |= 0x20; - - HashIndex += (HashIndex << 1) + (HashIndex >> 1) + Char; - } - - return HashIndex % OB_NUMBER_HASH_BUCKETS; -} - -xboxkrnl::PVOID xboxkrnl::ObpGetObjectHandleReference(HANDLE Handle) -{ - PVOID *HandleContents; - Handle = ObpMaskOffApplicationBits(Handle); - KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); - - if (HandleToUlong(Handle) < HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool)) { - HandleContents = ObpGetHandleContentsPointer(Handle); - PVOID Object = *HandleContents; - - if (Object != NULL && !ObpIsFreeHandleLink(Object)) { - OBJECT_TO_OBJECT_HEADER(Object)->PointerCount++; - KfLowerIrql(OldIrql); - return Object; - } - } - - KfLowerIrql(OldIrql); - return NULL; -} - -xboxkrnl::BOOLEAN xboxkrnl::ObpLookupElementNameInDirectory( - IN POBJECT_DIRECTORY Directory, - IN POBJECT_STRING ElementName, - IN BOOLEAN ResolveSymbolicLink, - OUT PVOID *ReturnedObject -) -{ - PVOID Object = NULL; - - if ((Directory == ObpDosDevicesDirectoryObject) && ResolveSymbolicLink && (ElementName->Length == sizeof(CHAR) * 2) && (ElementName->Buffer[1] == (CHAR)':')) { - CHAR DriveLetter = ElementName->Buffer[0]; - - if (DriveLetter >= 'a' && DriveLetter <= 'z') { - Object = ObpDosDevicesDriveLetterMap[DriveLetter - 'a']; - } else if (DriveLetter >= 'A' && DriveLetter <= 'Z') { - Object = ObpDosDevicesDriveLetterMap[DriveLetter - 'A']; - } - - if (Object != NULL) { - *ReturnedObject = Object; - return TRUE; - } - } - - ULONG HashIndex = ObpComputeHashIndex(ElementName); - POBJECT_HEADER_NAME_INFO ObjectHeaderNameInfo = Directory->HashBuckets[HashIndex]; - - while (ObjectHeaderNameInfo != NULL) { - if (RtlEqualString(&ObjectHeaderNameInfo->Name, ElementName, TRUE)) { - Object = OBJECT_HEADER_NAME_INFO_TO_OBJECT(ObjectHeaderNameInfo); - if (ResolveSymbolicLink && (OBJECT_TO_OBJECT_HEADER(Object)->Type == &ObSymbolicLinkObjectType)) { - Object = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTargetObject; - } - - *ReturnedObject = Object; - return TRUE; - } - - ObjectHeaderNameInfo = ObjectHeaderNameInfo->ChainLink; - } - - *ReturnedObject = NULL; - return FALSE; -} - -// ****************************************************************** -// * 0x00F1 - ObInsertObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(241) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObInsertObject -( - IN PVOID Object, - IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, - IN ULONG ObjectPointerBias, - OUT PHANDLE ReturnedHandle -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Object) - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(ObjectPointerBias) - LOG_FUNC_ARG_OUT(ReturnedHandle) - LOG_FUNC_END; - - NTSTATUS status; - POBJECT_DIRECTORY Directory; - - KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); - - HANDLE Handle = NULL; - PVOID InsertObject = Object; - if (ObjectAttributes != NULL && ObjectAttributes->ObjectName != NULL) { - OBJECT_STRING RemainingName = *ObjectAttributes->ObjectName; - HANDLE RootDirectoryHandle = ObjectAttributes->RootDirectory; - - if (RootDirectoryHandle != NULL) { - if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { - status = STATUS_OBJECT_NAME_INVALID; - goto CleanupAndExit; - } - - if (RootDirectoryHandle == ObDosDevicesDirectory()) { - Directory = ObpDosDevicesDirectoryObject; - } else if (RootDirectoryHandle == ObWin32NamedObjectsDirectory()) { - Directory = ObpWin32NamedObjectsDirectoryObject; - } else { - Directory = (POBJECT_DIRECTORY)ObpGetObjectHandleContents(RootDirectoryHandle); - - if (Directory == NULL) { - status = STATUS_INVALID_HANDLE; - goto CleanupAndExit; - } - - if (OBJECT_TO_OBJECT_HEADER(Directory)->Type != &ObDirectoryObjectType) { - status = STATUS_OBJECT_TYPE_MISMATCH; - goto CleanupAndExit; - } - } - } else { - if (RemainingName.Buffer[0] != OBJ_NAME_PATH_SEPARATOR) { - status = STATUS_OBJECT_NAME_INVALID; - goto CleanupAndExit; - } - - Directory = ObpRootDirectoryObject; - } - - for (;;) { - OBJECT_STRING ElementName; - ObDissectName(RemainingName, &ElementName, &RemainingName); - PVOID FoundObject; - if (ObpLookupElementNameInDirectory(Directory, &ElementName, TRUE, &FoundObject)) { - if (RemainingName.Length == 0) { - if (ObpIsFlagSet(ObjectAttributes->Attributes, OBJ_OPENIF)) { - if (OBJECT_TO_OBJECT_HEADER(FoundObject)->Type == OBJECT_TO_OBJECT_HEADER(Object)->Type) { - InsertObject = FoundObject; - Directory = NULL; - break; - } else { - status = STATUS_OBJECT_TYPE_MISMATCH; - goto CleanupAndExit; - } - } else { - status = STATUS_OBJECT_NAME_COLLISION; - goto CleanupAndExit; - } - } - - if (OBJECT_TO_OBJECT_HEADER(FoundObject)->Type != - &ObDirectoryObjectType) { - status = STATUS_OBJECT_PATH_NOT_FOUND; - goto CleanupAndExit; - } - - Directory = (POBJECT_DIRECTORY)FoundObject; - } else { - if (RemainingName.Length != 0) { - status = STATUS_OBJECT_PATH_NOT_FOUND; - goto CleanupAndExit; - } - - break; - } - } - } else { - Directory = NULL; - } - - Handle = ObpCreateObjectHandle(InsertObject); - - if (Handle == NULL) { - status = STATUS_INSUFFICIENT_RESOURCES; - goto CleanupAndExit; - } - - POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject); - ObjectHeader->PointerCount += ObjectPointerBias + 1; - - if (Directory != NULL) { - POBJECT_HEADER_NAME_INFO ObjectHeaderNameInfo = OBJECT_TO_OBJECT_HEADER_NAME_INFO(Object); - ULONG HashIndex = ObpComputeHashIndex(&ObjectHeaderNameInfo->Name); - ObjectHeader->Flags |= OB_FLAG_ATTACHED_OBJECT; - ObjectHeaderNameInfo->Directory = Directory; - ObjectHeaderNameInfo->ChainLink = Directory->HashBuckets[HashIndex]; - Directory->HashBuckets[HashIndex] = ObjectHeaderNameInfo; - - if ((Directory == ObpDosDevicesDirectoryObject) && (ObjectHeaderNameInfo->Name.Length == sizeof(CHAR) * 2) && (ObjectHeaderNameInfo->Name.Buffer[1] == (CHAR)':')) { - PVOID DosDevicesObject = Object; - - if (OBJECT_TO_OBJECT_HEADER(DosDevicesObject)->Type == - &ObSymbolicLinkObjectType) { - DosDevicesObject = ((POBJECT_SYMBOLIC_LINK)DosDevicesObject)->LinkTargetObject; - } - - CHAR DriveLetter = ObjectHeaderNameInfo->Name.Buffer[0]; - if (DriveLetter >= 'a' && DriveLetter <= 'z') { - ObpDosDevicesDriveLetterMap[DriveLetter - 'a'] = DosDevicesObject; - } else if (DriveLetter >= 'A' && DriveLetter <= 'Z') { - ObpDosDevicesDriveLetterMap[DriveLetter - 'A'] = DosDevicesObject; - } - } - - OBJECT_TO_OBJECT_HEADER(Directory)->PointerCount++; - ObjectHeader->PointerCount++; - } - - if ((ObjectAttributes != NULL) && - ObpIsFlagSet(ObjectAttributes->Attributes, OBJ_PERMANENT)) { - ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT; - } - - status = (Object == InsertObject) ? STATUS_SUCCESS : STATUS_OBJECT_NAME_EXISTS; - -CleanupAndExit: - KfLowerIrql(OldIrql); - ObfDereferenceObject(Object); - *ReturnedHandle = Handle; - - RETURN(status); -} - -// ****************************************************************** -// * 0x00F2 - ObMakeTemporaryObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(242) xboxkrnl::VOID NTAPI xboxkrnl::ObMakeTemporaryObject -( - IN PVOID Object -) -{ - LOG_FUNC_ONE_ARG(Object); - - LOG_UNIMPLEMENTED(); - assert(false); -} - -// ****************************************************************** -// * 0x00F3 - ObOpenObjectByName() -// ****************************************************************** -XBSYSAPI EXPORTNUM(243) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObOpenObjectByName -( - IN POBJECT_ATTRIBUTES ObjectAttributes, - IN POBJECT_TYPE ObjectType, - IN OUT PVOID ParseContext OPTIONAL, - OUT PHANDLE Handle -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ObjectAttributes) - LOG_FUNC_ARG(ObjectType) - LOG_FUNC_ARG(ParseContext) - LOG_FUNC_ARG_OUT(Handle) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - assert(false); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00F4 - ObOpenObjectByPointer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(244) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObOpenObjectByPointer -( - IN PVOID Object, - IN POBJECT_TYPE ObjectType, - OUT PHANDLE Handle -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Object) - LOG_FUNC_ARG(ObjectType) - LOG_FUNC_ARG_OUT(Handle) - LOG_FUNC_END; - - LOG_UNIMPLEMENTED(); - assert(false); - - RETURN(STATUS_SUCCESS); -} - -// ****************************************************************** -// * 0x00F6 - ObReferenceObjectByHandle() -// ****************************************************************** -// Turns a handle into a kernel object pointer. The ObjectType parameter -// specifies what type of object it is. This function also increments the -// object's reference count. -// -// Differences from NT: There are no DesiredAccess, AccessMode, or -// HandleInformation parameters. -XBSYSAPI EXPORTNUM(246) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObReferenceObjectByHandle -( - IN HANDLE Handle, - IN POBJECT_TYPE ObjectType OPTIONAL, - OUT PVOID *ReturnedObject -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Handle) - LOG_FUNC_ARG(ObjectType) - LOG_FUNC_ARG_OUT(ReturnedObject) - LOG_FUNC_END; - - NTSTATUS status; - PVOID Object; - POBJECT_HEADER ObjectHeader; - - if (Handle == NtCurrentThread()) { - if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) { - Object = PsGetCurrentThread(); - ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); - InterlockedIncrement(&ObjectHeader->PointerCount); - *ReturnedObject = Object; - return STATUS_SUCCESS; - } else { - status = STATUS_OBJECT_TYPE_MISMATCH; - } - } else { - Object = ObpGetObjectHandleReference(Handle); - - if (Object != NULL) { - ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); - - if ((ObjectType == ObjectHeader->Type) || (ObjectType == NULL)) { - *ReturnedObject = Object; - return STATUS_SUCCESS; - } else { - ObfDereferenceObject(Object); - status = STATUS_OBJECT_TYPE_MISMATCH; - } - } else { - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, we must return the input handle - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation(Handle, &flags)) { - // This was a Windows Handle, so return it. - *ReturnedObject = Handle; - return STATUS_SUCCESS; - } - - status = STATUS_INVALID_HANDLE; - } - } - - *ReturnedObject = NULL; - - RETURN(status); -} - -// ****************************************************************** -// * 0x00F7 - ObReferenceObjectByName() -// ****************************************************************** -XBSYSAPI EXPORTNUM(247) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObReferenceObjectByName -( - IN POBJECT_STRING ObjectName, - IN ULONG Attributes, - IN POBJECT_TYPE ObjectType, - IN OUT PVOID ParseContext OPTIONAL, - OUT PVOID *Object -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(ObjectName) - LOG_FUNC_ARG(Attributes) // TODO : Use, how? - LOG_FUNC_ARG(ObjectType) - LOG_FUNC_ARG_OUT(ParseContext) // TODO : Use and populate, how? - LOG_FUNC_ARG_OUT(Object) - LOG_FUNC_END; - - NTSTATUS result = ObpReferenceObjectByName(NULL, ObjectName, Attributes, ObjectType, ParseContext, Object); - RETURN(result); -} - -// ****************************************************************** -// * 0x00F8 - ObReferenceObjectByPointer() -// ****************************************************************** -XBSYSAPI EXPORTNUM(248) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObReferenceObjectByPointer -( - IN PVOID Object, - IN POBJECT_TYPE ObjectType -) -{ - LOG_FUNC_BEGIN - LOG_FUNC_ARG(Object) - LOG_FUNC_ARG(ObjectType) - LOG_FUNC_END; - - POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); - - if (ObjectType == ObjectHeader->Type) { - InterlockedIncrement(&ObjectHeader->PointerCount); - RETURN(STATUS_SUCCESS); - } - - RETURN(STATUS_OBJECT_TYPE_MISMATCH); -} - -// ****************************************************************** -// * 0x00F9 - ObSymbolicLinkObjectType -// ****************************************************************** -XBSYSAPI EXPORTNUM(249) xboxkrnl::OBJECT_TYPE xboxkrnl::ObSymbolicLinkObjectType = -{ - xboxkrnl::ExAllocatePoolWithTag, - xboxkrnl::ExFreePool, - NULL, - NULL, // TODO : xboxkrnl::ObpDeleteSymbolicLink, - NULL, - NULL, // TODO : &xboxkrnl::ObpDefaultObject, - 'bmyS' // = first four characters of "SymbolicLink" in reverse -}; - -// ****************************************************************** -// * 0x00FA - ObfDereferenceObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(250) xboxkrnl::VOID FASTCALL xboxkrnl::ObfDereferenceObject -( - IN PVOID Object -) -{ - LOG_FUNC_ONE_ARG_OUT(Object); - - // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own - // In this case, we must do nothing, otherwise we'll crash... - // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) - DWORD flags = 0; - if (GetHandleInformation((HANDLE)Object, &flags)) { - return; - } - - POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); - - if (InterlockedDecrement(&ObjectHeader->PointerCount) == 0) { - if (ObjectHeader->Type->DeleteProcedure != NULL) { - ObjectHeader->Type->DeleteProcedure(Object); - } - - PVOID ObjectBase; - if (ObpIsFlagSet(ObjectHeader->Flags, OB_FLAG_NAMED_OBJECT)) { - ObjectBase = OBJECT_HEADER_TO_OBJECT_HEADER_NAME_INFO(ObjectHeader); - } else { - ObjectBase = ObjectHeader; - } - - ObjectHeader->Type->FreeProcedure(ObjectBase); - } -} - -// ****************************************************************** -// * 0x00FB - ObfReferenceObject() -// ****************************************************************** -XBSYSAPI EXPORTNUM(251) xboxkrnl::VOID FASTCALL xboxkrnl::ObfReferenceObject -( - IN PVOID Object -) -{ - LOG_FUNC_ONE_ARG_OUT(Object); - InterlockedIncrement(&OBJECT_TO_OBJECT_HEADER(Object)->PointerCount); -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2016 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::OB + + +#include // For ObDirectoryObjectType, etc. +#include "Logging.h" // For LOG_FUNC() +#include "EmuKrnlLogging.h" +#include "core\kernel\init\CxbxKrnl.h" // For CxbxKrnlCleanup +#include "EmuKrnl.h" // For OBJECT_TO_OBJECT_HEADER() +#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc. +#include + +#pragma warning(disable:4005) // Ignore redefined status values +#include +#pragma warning(default:4005) + +#define FIELD_OFFSET(type,field) ((ULONG)&(((type *)0)->field)) + +#define INITIALIZED_OBJECT_STRING(ObjectString, Value) \ + xboxkrnl::CHAR ObjectString##Buffer[] = Value; \ + xboxkrnl::OBJECT_STRING ObjectString = { \ + sizeof(Value) - sizeof(CHAR), \ + sizeof(Value), \ + ObjectString##Buffer \ +} + +INITIALIZED_OBJECT_STRING(ObpDosDevicesString, "\\??"); +INITIALIZED_OBJECT_STRING(ObpIoDevicesString, "\\Device"); +INITIALIZED_OBJECT_STRING(ObpWin32NamedObjectsString, "\\Win32NamedObjects"); + +xboxkrnl::POBJECT_DIRECTORY ObpDosDevicesDirectoryObject; +xboxkrnl::POBJECT_DIRECTORY ObpWin32NamedObjectsDirectoryObject; +xboxkrnl::POBJECT_DIRECTORY ObpRootDirectoryObject; +xboxkrnl::POBJECT_DIRECTORY ObpIoDevicesDirectoryObject; + +XBSYSAPI EXPORTNUM(245) xboxkrnl::OBJECT_HANDLE_TABLE xboxkrnl::ObpObjectHandleTable = { + +}; + +xboxkrnl::PVOID ObpDosDevicesDriveLetterMap['Z' - 'A' + 1]; + +xboxkrnl::BOOLEAN xboxkrnl::ObpCreatePermanentDirectoryObject( + IN xboxkrnl::POBJECT_STRING DirectoryName OPTIONAL, + OUT xboxkrnl::POBJECT_DIRECTORY *DirectoryObject +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(DirectoryName) + LOG_FUNC_ARG(DirectoryObject) + LOG_FUNC_END; + + OBJECT_ATTRIBUTES ObjectAttributes; + XB_InitializeObjectAttributes(&ObjectAttributes, DirectoryName, OBJ_PERMANENT, NULL, NULL); + + HANDLE Handle; + NTSTATUS status = NtCreateDirectoryObject(&Handle, &ObjectAttributes); + + if (!NT_SUCCESS(status)) { + RETURN(FALSE); + } + + status = ObReferenceObjectByHandle(Handle, &ObDirectoryObjectType, (PVOID *)DirectoryObject); + + if (!NT_SUCCESS(status)) { + RETURN(FALSE); + } + + NtClose(Handle); + + RETURN(TRUE); +} + +xboxkrnl::NTSTATUS xboxkrnl::ObpReferenceObjectByName( + IN HANDLE RootDirectoryHandle, + IN POBJECT_STRING ObjectName, + IN ULONG Attributes, + IN POBJECT_TYPE ObjectType, + IN OUT PVOID ParseContext OPTIONAL, + OUT PVOID *ReturnedObject +) +{ + NTSTATUS status; + *ReturnedObject = NULL; + + KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); + + OBJECT_STRING RemainingName; + if (ObjectName != NULL) { + RemainingName = *ObjectName; + } else { + RtlZeroMemory(&RemainingName, sizeof(OBJECT_STRING)); + } + + BOOLEAN ResolveSymbolicLink = TRUE; + PVOID FoundObject; + + if (RootDirectoryHandle != NULL) { + if (RootDirectoryHandle == ObDosDevicesDirectory()) { + FoundObject = ObpDosDevicesDirectoryObject; + } else if (RootDirectoryHandle == ObWin32NamedObjectsDirectory()) { + FoundObject = ObpWin32NamedObjectsDirectoryObject; + } else { + FoundObject = (POBJECT_DIRECTORY)ObpGetObjectHandleContents(RootDirectoryHandle); + + if (FoundObject == NULL) { + status = STATUS_INVALID_HANDLE; + goto CleanupAndExit; + } + } + + if ((RemainingName.Length != 0) && + (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { + status = STATUS_OBJECT_NAME_INVALID; + goto CleanupAndExit; + } + + goto OpenRootDirectory; + } + + if ((RemainingName.Length == 0) || + (RemainingName.Buffer[0] != OBJ_NAME_PATH_SEPARATOR)) { + status = STATUS_OBJECT_NAME_INVALID; + goto CleanupAndExit; + } + + if (RemainingName.Length == sizeof(CHAR)) { + RemainingName.Buffer++; + RemainingName.Length = 0; + RemainingName.MaximumLength = 0; + goto OpenRootDirectory; + } + + OBJECT_STRING ElementName; + for (;;) { + POBJECT_DIRECTORY Directory = (POBJECT_DIRECTORY)FoundObject; + ObDissectName(RemainingName, &ElementName, &RemainingName); + + if (RemainingName.Length != 0) { + if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { + status = STATUS_OBJECT_NAME_INVALID; + goto CleanupAndExit; + } + } else { + if (ObjectType == &ObSymbolicLinkObjectType) { + ResolveSymbolicLink = FALSE; + } + } + + if (!ObpLookupElementNameInDirectory(Directory, &ElementName, + ResolveSymbolicLink, &FoundObject)) { + status = (RemainingName.Length != 0) ? STATUS_OBJECT_PATH_NOT_FOUND : STATUS_OBJECT_NAME_NOT_FOUND; + goto CleanupAndExit; + } + +OpenRootDirectory: + POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(FoundObject); + + if (RemainingName.Length == 0) { + if (ObjectHeader->Type->ParseProcedure != NULL) { + goto InvokeParseProcedure; + } + + if ((ObjectType != NULL) && (ObjectType != ObjectHeader->Type)) { + status = STATUS_OBJECT_TYPE_MISMATCH; + goto CleanupAndExit; + } + + ObjectHeader->PointerCount++; + *ReturnedObject = FoundObject; + status = STATUS_SUCCESS; + goto CleanupAndExit; + } + + if (ObjectHeader->Type != &ObDirectoryObjectType) { + if (ObjectHeader->Type->ParseProcedure == NULL) { + status = STATUS_OBJECT_PATH_NOT_FOUND; + goto CleanupAndExit; + } + +InvokeParseProcedure: + ObjectHeader->PointerCount++; + KfLowerIrql(OldIrql); + + if (ObjectHeader->Type != &IoFileObjectType && (RemainingName.Buffer > ObjectName->Buffer)) { + RemainingName.Buffer--; + RemainingName.Length += sizeof(CHAR); + RemainingName.MaximumLength = RemainingName.Length; + } + + PVOID ParsedObject = NULL; + status = ObjectHeader->Type->ParseProcedure(FoundObject, ObjectType, Attributes, ObjectName, &RemainingName, ParseContext, &ParsedObject); + ObfDereferenceObject(FoundObject); + + if (NT_SUCCESS(status)) { + if ((ObjectType == NULL) || (ObjectType == OBJECT_TO_OBJECT_HEADER(ParsedObject)->Type)) { + *ReturnedObject = ParsedObject; + status = STATUS_SUCCESS; + } else { + ObfDereferenceObject(ParsedObject); + status = STATUS_OBJECT_TYPE_MISMATCH; + } + } + + return status; + } + } +CleanupAndExit: + KfLowerIrql(OldIrql); + return status; +} + +xboxkrnl::BOOLEAN xboxkrnl::ObInitSystem() +{ + ObpObjectHandleTable.HandleCount = 0; + ObpObjectHandleTable.FirstFreeTableEntry = -1; + ObpObjectHandleTable.NextHandleNeedingPool = 0; + ObpObjectHandleTable.RootTable = NULL; + + RtlZeroMemory(ObpDosDevicesDriveLetterMap, sizeof(ObpDosDevicesDriveLetterMap)); + + if (!ObpCreatePermanentDirectoryObject(NULL, &ObpRootDirectoryObject)) { + return FALSE; + } + + if (!ObpCreatePermanentDirectoryObject(&ObpDosDevicesString, &ObpDosDevicesDirectoryObject)) { + return FALSE; + } + + if (!ObpCreatePermanentDirectoryObject(&ObpIoDevicesString, &ObpIoDevicesDirectoryObject)) { + return FALSE; + } + + if (!ObpCreatePermanentDirectoryObject(&ObpWin32NamedObjectsString, &ObpWin32NamedObjectsDirectoryObject)) { + return FALSE; + } + + return TRUE; +} + +xboxkrnl::BOOLEAN xboxkrnl::ObpExtendObjectHandleTable() +{ + PVOID* NewTable = (PVOID*)ExAllocatePoolWithTag(sizeof(PVOID) * OB_HANDLES_PER_TABLE, 'tHbO'); + if (NewTable == NULL) { + return FALSE; + } + + PVOID **NewRootTable; + SIZE_T NewRootTableSize; + if ((HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool) & (sizeof(PVOID) * OB_HANDLES_PER_SEGMENT - 1)) == 0) { + if (ObpObjectHandleTable.NextHandleNeedingPool == NULL) { + NewRootTable = ObpObjectHandleTable.BuiltinRootTable; + } else { + SIZE_T OldRootTableSize = HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool) / (sizeof(PVOID*) * OB_HANDLES_PER_TABLE); + NewRootTableSize = OldRootTableSize + OB_TABLES_PER_SEGMENT; + + NewRootTable = (PVOID**)ExAllocatePoolWithTag(sizeof(PVOID*) * NewRootTableSize, 'rHbO'); + if (NewRootTable == NULL) { + ExFreePool(NewTable); + return FALSE; + } + + RtlCopyMemory(NewRootTable, ObpObjectHandleTable.RootTable, sizeof(PVOID*) * OldRootTableSize); + + if (ObpObjectHandleTable.RootTable != ObpObjectHandleTable.BuiltinRootTable) { + ExFreePool(ObpObjectHandleTable.RootTable); + } + } + + ObpObjectHandleTable.RootTable = NewRootTable; + } + + ObpGetTableFromHandle(ObpObjectHandleTable.NextHandleNeedingPool) = NewTable; + HANDLE Handle = ObpObjectHandleTable.NextHandleNeedingPool; + PVOID *HandleContents = NewTable; + LONG_PTR FreeHandleLink = ObpEncodeFreeHandleLink(Handle); + ObpObjectHandleTable.FirstFreeTableEntry = FreeHandleLink; + + for (ULONG Index = 0; Index < OB_HANDLES_PER_TABLE - 1; Index++) { + FreeHandleLink += sizeof(PVOID); + *HandleContents++ = (PVOID)FreeHandleLink; + } + + *HandleContents = (PVOID)-1; + + if (Handle == NULL) { + HandleContents = NewTable; + ObpObjectHandleTable.FirstFreeTableEntry = (LONG_PTR)*HandleContents; + *HandleContents = NULL; + } + + ObpObjectHandleTable.NextHandleNeedingPool = (HANDLE)(HandleToLong(Handle) + (sizeof(PVOID) * OB_HANDLES_PER_TABLE)); + + return TRUE; +} + +xboxkrnl::HANDLE xboxkrnl::ObpCreateObjectHandle(xboxkrnl::PVOID Object) +{ + LOG_FUNC_ONE_ARG(Object); + + HANDLE Handle; + PVOID *HandleContents; + + if (ObpObjectHandleTable.FirstFreeTableEntry == -1) { + if (!ObpExtendObjectHandleTable()) { + return NULL; + } + } + + Handle = (HANDLE)ObpDecodeFreeHandleLink(ObpObjectHandleTable.FirstFreeTableEntry); + HandleContents = ObpGetHandleContentsPointer(Handle); + ObpObjectHandleTable.FirstFreeTableEntry = (LONG_PTR)*HandleContents; + ObpObjectHandleTable.HandleCount++; + OBJECT_TO_OBJECT_HEADER(Object)->HandleCount++; + *HandleContents = Object; + return Handle; +} + +xboxkrnl::VOID xboxkrnl::ObDissectName(OBJECT_STRING Path, POBJECT_STRING FirstName, POBJECT_STRING RemainingName) +{ + ULONG i = 0; + + FirstName->Length = 0; + FirstName->MaximumLength = 0; + FirstName->Buffer = NULL; + + RemainingName->Length = 0; + RemainingName->MaximumLength = 0; + RemainingName->Buffer = NULL; + + ULONG PathLength = Path.Length / sizeof(CHAR); + + if (PathLength == 0) { + return; + } + + if (Path.Buffer[0] == '\\') { + i = 1; + } + + ULONG FirstNameStart; + for (FirstNameStart = i; (i < PathLength) && (Path.Buffer[i] != '\\'); i += 1) { + ; + } + + FirstName->Length = (USHORT)((i - FirstNameStart) * sizeof(CHAR)); + FirstName->MaximumLength = FirstName->Length; + FirstName->Buffer = &Path.Buffer[FirstNameStart]; + + if (i < PathLength) { + RemainingName->Length = (USHORT)((PathLength - (i + 1)) * sizeof(CHAR)); + RemainingName->MaximumLength = RemainingName->Length; + RemainingName->Buffer = &Path.Buffer[i + 1]; + } + + return; +} + +// ****************************************************************** +// * 0x00EF - ObCreateObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(239) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObCreateObject +( + IN POBJECT_TYPE ObjectType, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN ULONG ObjectBodySize, + OUT PVOID *Object +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ObjectType) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(ObjectBodySize) + LOG_FUNC_ARG_OUT(Object) + LOG_FUNC_END; + + if (ObjectAttributes == NULL || ObjectAttributes->ObjectName == NULL) { + POBJECT_HEADER ObjectHeader = (POBJECT_HEADER)ObjectType->AllocateProcedure(FIELD_OFFSET(OBJECT_HEADER, Body) + ObjectBodySize, ObjectType->PoolTag); + + if (ObjectHeader == nullptr) { + RETURN(STATUS_INSUFFICIENT_RESOURCES); + } + + ObjectHeader->PointerCount = 1; + ObjectHeader->HandleCount = 0; + ObjectHeader->Type = ObjectType; + ObjectHeader->Flags = 0; + + *Object = &ObjectHeader->Body; + + RETURN(STATUS_SUCCESS); + } + + OBJECT_STRING RemainingName = *ObjectAttributes->ObjectName; + OBJECT_STRING ElementName; + ElementName.Buffer = NULL; + ElementName.Length = 0; + + while (RemainingName.Length != 0) { + ObDissectName(RemainingName, &ElementName, &RemainingName); + if ((RemainingName.Length != 0) && (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { + return STATUS_OBJECT_NAME_INVALID; + } + } + + if (ElementName.Length == 0) { + return STATUS_OBJECT_NAME_INVALID; + } + + ObjectBodySize = ALIGN_UP(ObjectBodySize, ULONG); + + POBJECT_HEADER_NAME_INFO ObjectNameInfo = (POBJECT_HEADER_NAME_INFO)ObjectType->AllocateProcedure( + sizeof(OBJECT_HEADER_NAME_INFO) + FIELD_OFFSET(OBJECT_HEADER, Body) + + ObjectBodySize + ElementName.Length, ObjectType->PoolTag); + + if (ObjectNameInfo == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + POBJECT_HEADER ObjectHeader = (POBJECT_HEADER)(ObjectNameInfo + 1); + ObjectNameInfo->ChainLink = NULL; + ObjectNameInfo->Directory = NULL; + ObjectNameInfo->Name.Buffer = (PSTR)((PUCHAR)&ObjectHeader->Body + ObjectBodySize); + ObjectNameInfo->Name.Length = ElementName.Length; + ObjectNameInfo->Name.MaximumLength = ElementName.Length; + + RtlCopyMemory(ObjectNameInfo->Name.Buffer, ElementName.Buffer, ElementName.Length); + + ObjectHeader->PointerCount = 1; + ObjectHeader->HandleCount = 0; + ObjectHeader->Type = ObjectType; + ObjectHeader->Flags = OB_FLAG_NAMED_OBJECT; + + *Object = &ObjectHeader->Body; + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00F0 - ObDirectoryObjectType +// ****************************************************************** +XBSYSAPI EXPORTNUM(240) xboxkrnl::OBJECT_TYPE xboxkrnl::ObDirectoryObjectType = +{ + xboxkrnl::ExAllocatePoolWithTag, + xboxkrnl::ExFreePool, + NULL, + NULL, + NULL, + NULL, // TODO : &xboxkrnl::ObpDefaultObject, + 'eriD' // = first four characters of "Directory" in reverse +}; + +xboxkrnl::PVOID xboxkrnl::ObpGetObjectHandleContents(HANDLE Handle) +{ + PVOID *HandleContents; + PVOID Object; + Handle = ObpMaskOffApplicationBits(Handle); + + if (HandleToUlong(Handle) < HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool)) { + HandleContents = ObpGetHandleContentsPointer(Handle); + Object = *HandleContents; + + if (Object != NULL && !ObpIsFreeHandleLink(Object)) { + return Object; + } + } + + return NULL; +} + +xboxkrnl::ULONG FASTCALL xboxkrnl::ObpComputeHashIndex( + IN POBJECT_STRING ElementName +) +{ + + ULONG HashIndex = 0; + PUCHAR Buffer = (PUCHAR)ElementName->Buffer; + PUCHAR BufferEnd = Buffer + ElementName->Length; + + // Calculate hash of string data + UCHAR Char; + while (Buffer < BufferEnd) { + Char = *Buffer++; + // Skip special characters + if (Char >= 0x80) { + continue; + } + + // Force all characters to be lowercase + Char |= 0x20; + + HashIndex += (HashIndex << 1) + (HashIndex >> 1) + Char; + } + + return HashIndex % OB_NUMBER_HASH_BUCKETS; +} + +xboxkrnl::PVOID xboxkrnl::ObpGetObjectHandleReference(HANDLE Handle) +{ + PVOID *HandleContents; + Handle = ObpMaskOffApplicationBits(Handle); + KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); + + if (HandleToUlong(Handle) < HandleToUlong(ObpObjectHandleTable.NextHandleNeedingPool)) { + HandleContents = ObpGetHandleContentsPointer(Handle); + PVOID Object = *HandleContents; + + if (Object != NULL && !ObpIsFreeHandleLink(Object)) { + OBJECT_TO_OBJECT_HEADER(Object)->PointerCount++; + KfLowerIrql(OldIrql); + return Object; + } + } + + KfLowerIrql(OldIrql); + return NULL; +} + +xboxkrnl::BOOLEAN xboxkrnl::ObpLookupElementNameInDirectory( + IN POBJECT_DIRECTORY Directory, + IN POBJECT_STRING ElementName, + IN BOOLEAN ResolveSymbolicLink, + OUT PVOID *ReturnedObject +) +{ + PVOID Object = NULL; + + if ((Directory == ObpDosDevicesDirectoryObject) && ResolveSymbolicLink && (ElementName->Length == sizeof(CHAR) * 2) && (ElementName->Buffer[1] == (CHAR)':')) { + CHAR DriveLetter = ElementName->Buffer[0]; + + if (DriveLetter >= 'a' && DriveLetter <= 'z') { + Object = ObpDosDevicesDriveLetterMap[DriveLetter - 'a']; + } else if (DriveLetter >= 'A' && DriveLetter <= 'Z') { + Object = ObpDosDevicesDriveLetterMap[DriveLetter - 'A']; + } + + if (Object != NULL) { + *ReturnedObject = Object; + return TRUE; + } + } + + ULONG HashIndex = ObpComputeHashIndex(ElementName); + POBJECT_HEADER_NAME_INFO ObjectHeaderNameInfo = Directory->HashBuckets[HashIndex]; + + while (ObjectHeaderNameInfo != NULL) { + if (RtlEqualString(&ObjectHeaderNameInfo->Name, ElementName, TRUE)) { + Object = OBJECT_HEADER_NAME_INFO_TO_OBJECT(ObjectHeaderNameInfo); + if (ResolveSymbolicLink && (OBJECT_TO_OBJECT_HEADER(Object)->Type == &ObSymbolicLinkObjectType)) { + Object = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTargetObject; + } + + *ReturnedObject = Object; + return TRUE; + } + + ObjectHeaderNameInfo = ObjectHeaderNameInfo->ChainLink; + } + + *ReturnedObject = NULL; + return FALSE; +} + +// ****************************************************************** +// * 0x00F1 - ObInsertObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(241) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObInsertObject +( + IN PVOID Object, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN ULONG ObjectPointerBias, + OUT PHANDLE ReturnedHandle +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Object) + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(ObjectPointerBias) + LOG_FUNC_ARG_OUT(ReturnedHandle) + LOG_FUNC_END; + + NTSTATUS status; + POBJECT_DIRECTORY Directory; + + KIRQL OldIrql = KeRaiseIrqlToDpcLevel(); + + HANDLE Handle = NULL; + PVOID InsertObject = Object; + if (ObjectAttributes != NULL && ObjectAttributes->ObjectName != NULL) { + OBJECT_STRING RemainingName = *ObjectAttributes->ObjectName; + HANDLE RootDirectoryHandle = ObjectAttributes->RootDirectory; + + if (RootDirectoryHandle != NULL) { + if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { + status = STATUS_OBJECT_NAME_INVALID; + goto CleanupAndExit; + } + + if (RootDirectoryHandle == ObDosDevicesDirectory()) { + Directory = ObpDosDevicesDirectoryObject; + } else if (RootDirectoryHandle == ObWin32NamedObjectsDirectory()) { + Directory = ObpWin32NamedObjectsDirectoryObject; + } else { + Directory = (POBJECT_DIRECTORY)ObpGetObjectHandleContents(RootDirectoryHandle); + + if (Directory == NULL) { + status = STATUS_INVALID_HANDLE; + goto CleanupAndExit; + } + + if (OBJECT_TO_OBJECT_HEADER(Directory)->Type != &ObDirectoryObjectType) { + status = STATUS_OBJECT_TYPE_MISMATCH; + goto CleanupAndExit; + } + } + } else { + if (RemainingName.Buffer[0] != OBJ_NAME_PATH_SEPARATOR) { + status = STATUS_OBJECT_NAME_INVALID; + goto CleanupAndExit; + } + + Directory = ObpRootDirectoryObject; + } + + for (;;) { + OBJECT_STRING ElementName; + ObDissectName(RemainingName, &ElementName, &RemainingName); + PVOID FoundObject; + if (ObpLookupElementNameInDirectory(Directory, &ElementName, TRUE, &FoundObject)) { + if (RemainingName.Length == 0) { + if (ObpIsFlagSet(ObjectAttributes->Attributes, OBJ_OPENIF)) { + if (OBJECT_TO_OBJECT_HEADER(FoundObject)->Type == OBJECT_TO_OBJECT_HEADER(Object)->Type) { + InsertObject = FoundObject; + Directory = NULL; + break; + } else { + status = STATUS_OBJECT_TYPE_MISMATCH; + goto CleanupAndExit; + } + } else { + status = STATUS_OBJECT_NAME_COLLISION; + goto CleanupAndExit; + } + } + + if (OBJECT_TO_OBJECT_HEADER(FoundObject)->Type != + &ObDirectoryObjectType) { + status = STATUS_OBJECT_PATH_NOT_FOUND; + goto CleanupAndExit; + } + + Directory = (POBJECT_DIRECTORY)FoundObject; + } else { + if (RemainingName.Length != 0) { + status = STATUS_OBJECT_PATH_NOT_FOUND; + goto CleanupAndExit; + } + + break; + } + } + } else { + Directory = NULL; + } + + Handle = ObpCreateObjectHandle(InsertObject); + + if (Handle == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto CleanupAndExit; + } + + POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject); + ObjectHeader->PointerCount += ObjectPointerBias + 1; + + if (Directory != NULL) { + POBJECT_HEADER_NAME_INFO ObjectHeaderNameInfo = OBJECT_TO_OBJECT_HEADER_NAME_INFO(Object); + ULONG HashIndex = ObpComputeHashIndex(&ObjectHeaderNameInfo->Name); + ObjectHeader->Flags |= OB_FLAG_ATTACHED_OBJECT; + ObjectHeaderNameInfo->Directory = Directory; + ObjectHeaderNameInfo->ChainLink = Directory->HashBuckets[HashIndex]; + Directory->HashBuckets[HashIndex] = ObjectHeaderNameInfo; + + if ((Directory == ObpDosDevicesDirectoryObject) && (ObjectHeaderNameInfo->Name.Length == sizeof(CHAR) * 2) && (ObjectHeaderNameInfo->Name.Buffer[1] == (CHAR)':')) { + PVOID DosDevicesObject = Object; + + if (OBJECT_TO_OBJECT_HEADER(DosDevicesObject)->Type == + &ObSymbolicLinkObjectType) { + DosDevicesObject = ((POBJECT_SYMBOLIC_LINK)DosDevicesObject)->LinkTargetObject; + } + + CHAR DriveLetter = ObjectHeaderNameInfo->Name.Buffer[0]; + if (DriveLetter >= 'a' && DriveLetter <= 'z') { + ObpDosDevicesDriveLetterMap[DriveLetter - 'a'] = DosDevicesObject; + } else if (DriveLetter >= 'A' && DriveLetter <= 'Z') { + ObpDosDevicesDriveLetterMap[DriveLetter - 'A'] = DosDevicesObject; + } + } + + OBJECT_TO_OBJECT_HEADER(Directory)->PointerCount++; + ObjectHeader->PointerCount++; + } + + if ((ObjectAttributes != NULL) && + ObpIsFlagSet(ObjectAttributes->Attributes, OBJ_PERMANENT)) { + ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT; + } + + status = (Object == InsertObject) ? STATUS_SUCCESS : STATUS_OBJECT_NAME_EXISTS; + +CleanupAndExit: + KfLowerIrql(OldIrql); + ObfDereferenceObject(Object); + *ReturnedHandle = Handle; + + RETURN(status); +} + +// ****************************************************************** +// * 0x00F2 - ObMakeTemporaryObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(242) xboxkrnl::VOID NTAPI xboxkrnl::ObMakeTemporaryObject +( + IN PVOID Object +) +{ + LOG_FUNC_ONE_ARG(Object); + + LOG_UNIMPLEMENTED(); + assert(false); +} + +// ****************************************************************** +// * 0x00F3 - ObOpenObjectByName() +// ****************************************************************** +XBSYSAPI EXPORTNUM(243) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObOpenObjectByName +( + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN POBJECT_TYPE ObjectType, + IN OUT PVOID ParseContext OPTIONAL, + OUT PHANDLE Handle +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ObjectAttributes) + LOG_FUNC_ARG(ObjectType) + LOG_FUNC_ARG(ParseContext) + LOG_FUNC_ARG_OUT(Handle) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + assert(false); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00F4 - ObOpenObjectByPointer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(244) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObOpenObjectByPointer +( + IN PVOID Object, + IN POBJECT_TYPE ObjectType, + OUT PHANDLE Handle +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Object) + LOG_FUNC_ARG(ObjectType) + LOG_FUNC_ARG_OUT(Handle) + LOG_FUNC_END; + + LOG_UNIMPLEMENTED(); + assert(false); + + RETURN(STATUS_SUCCESS); +} + +// ****************************************************************** +// * 0x00F6 - ObReferenceObjectByHandle() +// ****************************************************************** +// Turns a handle into a kernel object pointer. The ObjectType parameter +// specifies what type of object it is. This function also increments the +// object's reference count. +// +// Differences from NT: There are no DesiredAccess, AccessMode, or +// HandleInformation parameters. +XBSYSAPI EXPORTNUM(246) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObReferenceObjectByHandle +( + IN HANDLE Handle, + IN POBJECT_TYPE ObjectType OPTIONAL, + OUT PVOID *ReturnedObject +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Handle) + LOG_FUNC_ARG(ObjectType) + LOG_FUNC_ARG_OUT(ReturnedObject) + LOG_FUNC_END; + + NTSTATUS status; + PVOID Object; + POBJECT_HEADER ObjectHeader; + + if (Handle == NtCurrentThread()) { + if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) { + Object = PsGetCurrentThread(); + ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); + InterlockedIncrement(&ObjectHeader->PointerCount); + *ReturnedObject = Object; + return STATUS_SUCCESS; + } else { + status = STATUS_OBJECT_TYPE_MISMATCH; + } + } else { + Object = ObpGetObjectHandleReference(Handle); + + if (Object != NULL) { + ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); + + if ((ObjectType == ObjectHeader->Type) || (ObjectType == NULL)) { + *ReturnedObject = Object; + return STATUS_SUCCESS; + } else { + ObfDereferenceObject(Object); + status = STATUS_OBJECT_TYPE_MISMATCH; + } + } else { + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, we must return the input handle + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation(Handle, &flags)) { + // This was a Windows Handle, so return it. + *ReturnedObject = Handle; + return STATUS_SUCCESS; + } + + status = STATUS_INVALID_HANDLE; + } + } + + *ReturnedObject = NULL; + + RETURN(status); +} + +// ****************************************************************** +// * 0x00F7 - ObReferenceObjectByName() +// ****************************************************************** +XBSYSAPI EXPORTNUM(247) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObReferenceObjectByName +( + IN POBJECT_STRING ObjectName, + IN ULONG Attributes, + IN POBJECT_TYPE ObjectType, + IN OUT PVOID ParseContext OPTIONAL, + OUT PVOID *Object +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(ObjectName) + LOG_FUNC_ARG(Attributes) // TODO : Use, how? + LOG_FUNC_ARG(ObjectType) + LOG_FUNC_ARG_OUT(ParseContext) // TODO : Use and populate, how? + LOG_FUNC_ARG_OUT(Object) + LOG_FUNC_END; + + NTSTATUS result = ObpReferenceObjectByName(NULL, ObjectName, Attributes, ObjectType, ParseContext, Object); + RETURN(result); +} + +// ****************************************************************** +// * 0x00F8 - ObReferenceObjectByPointer() +// ****************************************************************** +XBSYSAPI EXPORTNUM(248) xboxkrnl::NTSTATUS NTAPI xboxkrnl::ObReferenceObjectByPointer +( + IN PVOID Object, + IN POBJECT_TYPE ObjectType +) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Object) + LOG_FUNC_ARG(ObjectType) + LOG_FUNC_END; + + POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); + + if (ObjectType == ObjectHeader->Type) { + InterlockedIncrement(&ObjectHeader->PointerCount); + RETURN(STATUS_SUCCESS); + } + + RETURN(STATUS_OBJECT_TYPE_MISMATCH); +} + +// ****************************************************************** +// * 0x00F9 - ObSymbolicLinkObjectType +// ****************************************************************** +XBSYSAPI EXPORTNUM(249) xboxkrnl::OBJECT_TYPE xboxkrnl::ObSymbolicLinkObjectType = +{ + xboxkrnl::ExAllocatePoolWithTag, + xboxkrnl::ExFreePool, + NULL, + NULL, // TODO : xboxkrnl::ObpDeleteSymbolicLink, + NULL, + NULL, // TODO : &xboxkrnl::ObpDefaultObject, + 'bmyS' // = first four characters of "SymbolicLink" in reverse +}; + +// ****************************************************************** +// * 0x00FA - ObfDereferenceObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(250) xboxkrnl::VOID FASTCALL xboxkrnl::ObfDereferenceObject +( + IN PVOID Object +) +{ + LOG_FUNC_ONE_ARG_OUT(Object); + + // HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own + // In this case, we must do nothing, otherwise we'll crash... + // Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection) + DWORD flags = 0; + if (GetHandleInformation((HANDLE)Object, &flags)) { + return; + } + + POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object); + + if (InterlockedDecrement(&ObjectHeader->PointerCount) == 0) { + if (ObjectHeader->Type->DeleteProcedure != NULL) { + ObjectHeader->Type->DeleteProcedure(Object); + } + + PVOID ObjectBase; + if (ObpIsFlagSet(ObjectHeader->Flags, OB_FLAG_NAMED_OBJECT)) { + ObjectBase = OBJECT_HEADER_TO_OBJECT_HEADER_NAME_INFO(ObjectHeader); + } else { + ObjectBase = ObjectHeader; + } + + ObjectHeader->Type->FreeProcedure(ObjectBase); + } +} + +// ****************************************************************** +// * 0x00FB - ObfReferenceObject() +// ****************************************************************** +XBSYSAPI EXPORTNUM(251) xboxkrnl::VOID FASTCALL xboxkrnl::ObfReferenceObject +( + IN PVOID Object +) +{ + LOG_FUNC_ONE_ARG_OUT(Object); + InterlockedIncrement(&OBJECT_TO_OBJECT_HEADER(Object)->PointerCount); +} diff --git a/src/core/kernel/exports/EmuKrnlPs.cpp b/src/core/kernel/exports/EmuKrnlPs.cpp index 6366fc484..2f98c1761 100644 --- a/src/core/kernel/exports/EmuKrnlPs.cpp +++ b/src/core/kernel/exports/EmuKrnlPs.cpp @@ -31,7 +31,7 @@ #include // For PsCreateSystemThreadEx, etc. -#include // For __beginthreadex(), etc. +#include // For __beginthreadex(), etc. #include // For _controlfp constants #include "Logging.h" // For LOG_FUNC() @@ -130,7 +130,7 @@ static unsigned int WINAPI PCSTProxy SetEvent(hStartedEvent); if (StartSuspended == TRUE) { - SuspendThread(GetCurrentThread()); + SuspendThread(GetCurrentThread()); } auto routine = (xboxkrnl::PKSYSTEM_ROUTINE)SystemRoutine; @@ -140,7 +140,7 @@ static unsigned int WINAPI PCSTProxy // (To avoid repetitions, uncheck "Break when this exception type is thrown"). routine(xboxkrnl::PKSTART_ROUTINE(StartRoutine), StartContext); - // This will also handle thread notification : + // This will also handle thread notification : LOG_TEST_CASE("Thread returned from SystemRoutine"); xboxkrnl::PsTerminateSystemThread(STATUS_SUCCESS); @@ -235,9 +235,9 @@ XBSYSAPI EXPORTNUM(255) xboxkrnl::NTSTATUS NTAPI xboxkrnl::PsCreateSystemThreadE // use default kernel stack size if lesser specified if (KernelStackSize < KERNEL_STACK_SIZE) - KernelStackSize = KERNEL_STACK_SIZE; - - // Double the stack size, this is to account for the overhead HLE patching adds to the stack + KernelStackSize = KERNEL_STACK_SIZE; + + // Double the stack size, this is to account for the overhead HLE patching adds to the stack KernelStackSize *= 2; // round up to the next page boundary if un-aligned @@ -260,17 +260,17 @@ XBSYSAPI EXPORTNUM(255) xboxkrnl::NTSTATUS NTAPI xboxkrnl::PsCreateSystemThreadE iPCSTProxyParam->StartContext = StartContext; iPCSTProxyParam->SystemRoutine = (PVOID)SystemRoutine; // NULL, XapiThreadStartup or unknown? iPCSTProxyParam->StartSuspended = CreateSuspended; - iPCSTProxyParam->hStartedEvent = hStartedEvent; - - /* + iPCSTProxyParam->hStartedEvent = hStartedEvent; + + /* // call thread notification routine(s) if (g_iThreadNotificationCount != 0) { for (int i = 0; i < 16; i++) - { - // TODO: This is *very* wrong, ps notification routines are NOT the same as XApi notification routines - // TODO: XAPI notification routines are already handeld by XapiThreadStartup and don't need to be called by us - // TODO: This type of notification routine is PCREATE_THREAD_NOTIFY_ROUTINE, which takes an ETHREAD pointer as well as Thread ID as input + { + // TODO: This is *very* wrong, ps notification routines are NOT the same as XApi notification routines + // TODO: XAPI notification routines are already handeld by XapiThreadStartup and don't need to be called by us + // TODO: This type of notification routine is PCREATE_THREAD_NOTIFY_ROUTINE, which takes an ETHREAD pointer as well as Thread ID as input // TODO: This is impossible to support currently, as we do not create or register Xbox ETHREAD objects, so we're better to skip it entirely! XTL::XTHREAD_NOTIFY_PROC pfnNotificationRoutine = (XTL::XTHREAD_NOTIFY_PROC)g_pfnThreadNotification[i]; @@ -282,8 +282,8 @@ XBSYSAPI EXPORTNUM(255) xboxkrnl::NTSTATUS NTAPI xboxkrnl::PsCreateSystemThreadE pfnNotificationRoutine(TRUE); } - }*/ - + }*/ + *ThreadHandle = (HANDLE)_beginthreadex(NULL, KernelStackSize, PCSTProxy, iPCSTProxyParam, NULL, (unsigned int*)&dwThreadId); // Note : DO NOT use iPCSTProxyParam anymore, since ownership is transferred to the proxy (which frees it too) @@ -325,9 +325,9 @@ XBSYSAPI EXPORTNUM(255) xboxkrnl::NTSTATUS NTAPI xboxkrnl::PsCreateSystemThreadE if (ThreadId != NULL) *ThreadId = (xboxkrnl::HANDLE)dwThreadId; - } - - SwitchToThread(); + } + + SwitchToThread(); Sleep(10); RETURN(STATUS_SUCCESS); @@ -403,7 +403,7 @@ XBSYSAPI EXPORTNUM(258) xboxkrnl::VOID NTAPI xboxkrnl::PsTerminateSystemThread ) { LOG_FUNC_ONE_ARG(ExitStatus); - + /* // call thread notification routine(s) if (g_iThreadNotificationCount != 0) diff --git a/src/core/kernel/exports/EmuKrnlRtl.cpp b/src/core/kernel/exports/EmuKrnlRtl.cpp index e6bbbe941..133d3006a 100644 --- a/src/core/kernel/exports/EmuKrnlRtl.cpp +++ b/src/core/kernel/exports/EmuKrnlRtl.cpp @@ -49,20 +49,20 @@ namespace NtDll // Prevent errors compiling #undef RtlFillMemory #undef RtlMoveMemory -#undef RtlZeroMemory -#undef EXCEPTION_NONCONTINUABLE -#undef EXCEPTION_UNWINDING -#undef EXCEPTION_EXIT_UNWIND -#undef EXCEPTION_STACK_INVALID -#undef EXCEPTION_NESTED_CALL -#undef EXCEPTION_TARGET_UNWIND -#undef EXCEPTION_COLLIDED_UNWIND +#undef RtlZeroMemory +#undef EXCEPTION_NONCONTINUABLE +#undef EXCEPTION_UNWINDING +#undef EXCEPTION_EXIT_UNWIND +#undef EXCEPTION_STACK_INVALID +#undef EXCEPTION_NESTED_CALL +#undef EXCEPTION_TARGET_UNWIND +#undef EXCEPTION_COLLIDED_UNWIND #undef EXCEPTION_UNWIND -#endif // _WIN32 - +#endif // _WIN32 + // Exception record flags -// Source: ReactOS +// Source: ReactOS // NOTE: don't put these in xboxkrnl.h, they will conflict with the macros provided by Windows #define EXCEPTION_NONCONTINUABLE 0x01 #define EXCEPTION_UNWINDING 0x02 @@ -229,16 +229,16 @@ XBSYSAPI EXPORTNUM(264) xboxkrnl::VOID NTAPI xboxkrnl::RtlAssert LOG_FUNC_ARG(FileName) LOG_FUNC_ARG(LineNumber) LOG_FUNC_ARG(Message) - LOG_FUNC_END; - - std::stringstream ss; - ss << "RtlAssert() raised by emulated program\n" << FileName << ":" << LineNumber << ":" << FailedAssertion ; - if (Message) { - ss << " " << Message; - } - - ss << ")"; - + LOG_FUNC_END; + + std::stringstream ss; + ss << "RtlAssert() raised by emulated program\n" << FileName << ":" << LineNumber << ":" << FailedAssertion ; + if (Message) { + ss << " " << Message; + } + + ss << ")"; + PopupWarning(nullptr, ss.str().c_str()); } diff --git a/src/core/kernel/exports/EmuKrnlXbox.cpp b/src/core/kernel/exports/EmuKrnlXbox.cpp index 516751522..7717c961b 100644 --- a/src/core/kernel/exports/EmuKrnlXbox.cpp +++ b/src/core/kernel/exports/EmuKrnlXbox.cpp @@ -29,7 +29,7 @@ #define LOG_PREFIX CXBXR_MODULE::XBOX -#include // For XboxEEPROMKey, etc. +#include // For XboxEEPROMKey, etc. #include "Logging.h" // Certificate Key diff --git a/src/core/kernel/exports/EmuKrnlXc.cpp b/src/core/kernel/exports/EmuKrnlXc.cpp index 7d9bd6f49..70c2366b7 100644 --- a/src/core/kernel/exports/EmuKrnlXc.cpp +++ b/src/core/kernel/exports/EmuKrnlXc.cpp @@ -20,8 +20,8 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2002-2003 Aaron Robinson -// * (c) 2016 Patrick van Logchem -// * (c) 2019 Jannik Vogel +// * (c) 2016 Patrick van Logchem +// * (c) 2019 Jannik Vogel // * (c) 2018-2019 ergo720 // * // * All rights reserved @@ -35,7 +35,7 @@ #include "Logging.h" // For LOG_FUNC() #include "EmuKrnlLogging.h" #include "common\crypto\EmuSha.h" // For A_SHAInit, etc. -#include "common\crypto\LibRc4.h" // For RC4 Functions +#include "common\crypto\LibRc4.h" // For RC4 Functions #include "common\crypto\EmuDes.h" // For DES Functions #include "common\crypto\EmuRSA.h" // For RSA Functions @@ -44,7 +44,7 @@ namespace NtDll { #include "core\kernel\support\EmuNtDll.h" }; - + // The following are the default implementations of the crypto functions @@ -209,8 +209,8 @@ xboxkrnl::ULONG NTAPI JumpedModExp xboxkrnl::LPDWORD pD, xboxkrnl::ULONG dwN ) -{ - unsigned int len = dwN * 4; +{ + unsigned int len = dwN * 4; if (xbox_exp_mod((unsigned char*)pA, (const unsigned char*)pB, (const unsigned char*)pC, (const unsigned char*)pD, len, len, len, len)) { return 1; } @@ -233,12 +233,12 @@ xboxkrnl::VOID NTAPI JumpedKeyTable xboxkrnl::PUCHAR pbKeyTable, xboxkrnl::PUCHAR pbKey ) -{ +{ if (dwCipher) { mbedtls_des3_set3key_enc((mbedtls_des3_context*)pbKeyTable, pbKey); - } + } else { - mbedtls_des_setkey_enc((mbedtls_des_context*)pbKeyTable, pbKey); + mbedtls_des_setkey_enc((mbedtls_des_context*)pbKeyTable, pbKey); } } @@ -250,10 +250,10 @@ xboxkrnl::VOID NTAPI JumpedBlockCrypt xboxkrnl::PUCHAR pbKeyTable, xboxkrnl::ULONG dwOp ) -{ - if (dwCipher) { - mbedtls_des3_crypt_ecb((mbedtls_des3_context*)pbKeyTable, pbInput, pbOutput, dwOp); - } +{ + if (dwCipher) { + mbedtls_des3_crypt_ecb((mbedtls_des3_context*)pbKeyTable, pbInput, pbOutput, dwOp); + } else { mbedtls_des_crypt_ecb((mbedtls_des_context*)pbKeyTable, pbInput, pbOutput, dwOp); } @@ -269,16 +269,16 @@ xboxkrnl::VOID NTAPI JumpedBlockCryptCBC xboxkrnl::ULONG dwOp, xboxkrnl::PUCHAR pbFeedback ) -{ - int ret; +{ + int ret; - if (dwCipher) { - ret = mbedtls_des3_crypt_cbc((mbedtls_des3_context*)pbKeyTable, dwOp, dwInputLength, pbFeedback, pbInput, pbOutput); - } + if (dwCipher) { + ret = mbedtls_des3_crypt_cbc((mbedtls_des3_context*)pbKeyTable, dwOp, dwInputLength, pbFeedback, pbInput, pbOutput); + } else { ret = mbedtls_des_crypt_cbc((mbedtls_des_context*)pbKeyTable, dwOp, dwInputLength, pbFeedback, pbInput, pbOutput); - } - + } + if (ret == MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH) { EmuLog(LOG_LEVEL::DEBUG, "%s: dwInputLength was not a multiple of 8 (it was %lu)", __func__, dwInputLength); } diff --git a/src/core/kernel/exports/EmuKrnlXe.cpp b/src/core/kernel/exports/EmuKrnlXe.cpp index 7bade4501..4d1faeec5 100644 --- a/src/core/kernel/exports/EmuKrnlXe.cpp +++ b/src/core/kernel/exports/EmuKrnlXe.cpp @@ -79,11 +79,11 @@ XBSYSAPI EXPORTNUM(327) xboxkrnl::NTSTATUS NTAPI xboxkrnl::XeLoadSection VAddr BaseAddress = (VAddr)Section->VirtualAddress; size_t SectionSize = (VAddr)Section->VirtualSize; - ret = g_VMManager.XbAllocateVirtualMemory(&BaseAddress, 0, &SectionSize, XBOX_MEM_COMMIT, XBOX_PAGE_EXECUTE_READWRITE); + ret = g_VMManager.XbAllocateVirtualMemory(&BaseAddress, 0, &SectionSize, XBOX_MEM_COMMIT, XBOX_PAGE_EXECUTE_READWRITE); if (ret != STATUS_SUCCESS) { RETURN(ret); } - + // Clear the memory the section requires memset(Section->VirtualAddress, 0, Section->VirtualSize); // Copy the section data @@ -162,9 +162,9 @@ XBSYSAPI EXPORTNUM(328) xboxkrnl::NTSTATUS NTAPI xboxkrnl::XeUnloadSection // ****************************************************************** // * 0x0163 - XePublicKeyData -// ****************************************************************** -// Define XePublicKeyData: This will be overwritten by the correct key for the given Xbe at runtime -XBSYSAPI EXPORTNUM(355) xboxkrnl::UCHAR xboxkrnl::XePublicKeyData[284] = { 0 }; +// ****************************************************************** +// Define XePublicKeyData: This will be overwritten by the correct key for the given Xbe at runtime +XBSYSAPI EXPORTNUM(355) xboxkrnl::UCHAR xboxkrnl::XePublicKeyData[284] = { 0 }; // We are allowed to use the real RSA public key since it cannot be used to sign data, only verify it -> it's not secret/private xboxkrnl::UCHAR xboxkrnl::XePublicKeyDataRetail[284] = { @@ -191,58 +191,58 @@ xboxkrnl::UCHAR xboxkrnl::XePublicKeyDataRetail[284] = { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }; -// Chihiro Game Public Key -xboxkrnl::UCHAR xboxkrnl::XePublicKeyDataChihiroGame[284] = { - 0x52, 0x53, 0x41, 0x31, 0x08, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, - 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x9B, 0x83, 0xD4, 0xD5, - 0xDE, 0x16, 0x25, 0x8E, 0xE5, 0x15, 0xF2, 0x18, 0x9D, 0x19, 0x1C, 0xF8, - 0xFE, 0x91, 0xA5, 0x83, 0xAE, 0xA5, 0xA8, 0x95, 0x3F, 0x01, 0xB2, 0xC9, - 0x34, 0xFB, 0xC7, 0x51, 0x2D, 0xAC, 0xFF, 0x38, 0xE6, 0xB6, 0x7B, 0x08, - 0x4A, 0xDF, 0x98, 0xA3, 0xFD, 0x31, 0x81, 0xBF, 0xAA, 0xD1, 0x62, 0x58, - 0xC0, 0x6C, 0x8F, 0x8E, 0xCD, 0x96, 0xCE, 0x6D, 0x03, 0x44, 0x59, 0x93, - 0xCE, 0xEA, 0x8D, 0xF4, 0xD4, 0x6F, 0x6F, 0x34, 0x5D, 0x50, 0xF1, 0xAE, - 0x99, 0x7F, 0x1D, 0x92, 0x15, 0xF3, 0x6B, 0xDB, 0xF9, 0x95, 0x8B, 0x3F, - 0x54, 0xAD, 0x37, 0xB5, 0x4F, 0x0A, 0x58, 0x7B, 0x48, 0xA2, 0x9F, 0x9E, - 0xA3, 0x16, 0xC8, 0xBD, 0x37, 0xDA, 0x9A, 0x37, 0xE6, 0x3F, 0x10, 0x1B, - 0xA8, 0x4F, 0xA3, 0x14, 0xFA, 0xBE, 0x12, 0xFB, 0xD7, 0x19, 0x4C, 0xED, - 0xAD, 0xA2, 0x95, 0x8F, 0x39, 0x8C, 0xC4, 0x69, 0x0F, 0x7D, 0xB8, 0x84, - 0x0A, 0x99, 0x5C, 0x53, 0x2F, 0xDE, 0xF2, 0x1B, 0xC5, 0x1D, 0x4C, 0x43, - 0x3C, 0x97, 0xA7, 0xBA, 0x8F, 0xC3, 0x22, 0x67, 0x39, 0xC2, 0x62, 0x74, - 0x3A, 0x0C, 0xB5, 0x57, 0x01, 0x3A, 0x67, 0xC6, 0xDE, 0x0C, 0x0B, 0xF6, - 0x08, 0x01, 0x64, 0xDB, 0xBD, 0x81, 0xE4, 0xDC, 0x09, 0x2E, 0xD0, 0xF1, - 0xD0, 0xD6, 0x1E, 0xBA, 0x38, 0x36, 0xF4, 0x4A, 0xDD, 0xCA, 0x39, 0xEB, - 0x76, 0xCF, 0x95, 0xDC, 0x48, 0x4C, 0xF2, 0x43, 0x8C, 0xD9, 0x44, 0x26, - 0x7A, 0x9E, 0xEB, 0x99, 0xA3, 0xD8, 0xFB, 0x30, 0xA8, 0x14, 0x42, 0x82, - 0x8D, 0xB4, 0x31, 0xB3, 0x1A, 0xD5, 0x2B, 0xF6, 0x32, 0xBC, 0x62, 0xC0, - 0xFE, 0x81, 0x20, 0x49, 0xE7, 0xF7, 0x58, 0x2F, 0x2D, 0xA6, 0x1B, 0x41, - 0x62, 0xC7, 0xE0, 0x32, 0x02, 0x5D, 0x82, 0xEC, 0xA3, 0xE4, 0x6C, 0x9B, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// Chihiro bootloader public key (Segaboot) -xboxkrnl::UCHAR xboxkrnl::XePublicKeyDataChihiroBoot[284] = { - 0x52, 0x53, 0x41, 0x31, 0x08, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, - 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x6B, 0x7B, 0x38, 0x78, - 0xE3, 0x16, 0x04, 0x88, 0x1D, 0xAF, 0x63, 0x4E, 0x23, 0xDB, 0x10, 0x14, - 0xB4, 0x52, 0x87, 0xEB, 0xE3, 0x37, 0xC2, 0x35, 0x6E, 0x38, 0x08, 0xDC, - 0x07, 0xC5, 0x92, 0x17, 0xC0, 0xBF, 0xDB, 0xF5, 0x9E, 0x72, 0xDB, 0x2F, - 0x98, 0x93, 0x6E, 0x98, 0x5E, 0xD9, 0x69, 0x80, 0x17, 0xFC, 0x0C, 0x72, - 0x2E, 0xE6, 0x75, 0x85, 0x48, 0xEA, 0xBD, 0xDA, 0x6E, 0x82, 0xD9, 0xFB, - 0x10, 0xAA, 0x11, 0xEE, 0xB5, 0xC1, 0xF2, 0x53, 0xD6, 0xF0, 0xD3, 0x4B, - 0xC2, 0x11, 0x4F, 0x8A, 0x18, 0xFB, 0xB7, 0x36, 0xFC, 0xDD, 0xD0, 0xBF, - 0x5C, 0x32, 0x44, 0x40, 0xEB, 0x92, 0x70, 0xA4, 0xEF, 0x3A, 0xAB, 0x78, - 0x66, 0x1A, 0x03, 0x0A, 0x9E, 0xC5, 0x3A, 0xB7, 0x8F, 0xE5, 0xB1, 0x5E, - 0x44, 0x15, 0xBA, 0x42, 0xD9, 0x10, 0x2A, 0x60, 0x93, 0x47, 0x4C, 0x5B, - 0xE1, 0x24, 0x04, 0x1E, 0x5C, 0x95, 0xB2, 0x17, 0x34, 0xD2, 0x37, 0x5F, - 0x85, 0x83, 0x62, 0x8D, 0x6E, 0x90, 0x69, 0x06, 0xB9, 0xFB, 0x7A, 0x24, - 0x8A, 0xE6, 0xCC, 0x77, 0x1E, 0x0A, 0x8C, 0x2B, 0x3B, 0xA2, 0x33, 0x79, - 0x24, 0x8C, 0xD3, 0x88, 0x01, 0x3A, 0x38, 0x7F, 0xF0, 0xAB, 0x9E, 0x2F, - 0x74, 0xCE, 0x50, 0xD1, 0xC2, 0x00, 0x57, 0xD3, 0xA7, 0x09, 0x45, 0x36, - 0xFA, 0xC1, 0xC7, 0x1B, 0x65, 0xAD, 0x98, 0x9C, 0x63, 0xED, 0xBA, 0x99, - 0x9B, 0x07, 0x3E, 0x57, 0xBD, 0xB5, 0x52, 0x44, 0x72, 0x09, 0x43, 0xE0, - 0x5C, 0x35, 0xCC, 0xE4, 0xE0, 0x85, 0x6A, 0x61, 0xAA, 0xF5, 0x0D, 0x1E, - 0xE7, 0x8F, 0xB0, 0xB9, 0xE3, 0xC3, 0x83, 0x10, 0x6C, 0x2F, 0x5D, 0xD4, - 0xAB, 0x2D, 0xAB, 0x4D, 0xCE, 0xC9, 0x7F, 0x52, 0x39, 0x13, 0xED, 0x44, - 0x06, 0x23, 0x2F, 0x62, 0xFF, 0xA1, 0x2B, 0xEE, 0x07, 0x98, 0x7D, 0xBC, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +// Chihiro Game Public Key +xboxkrnl::UCHAR xboxkrnl::XePublicKeyDataChihiroGame[284] = { + 0x52, 0x53, 0x41, 0x31, 0x08, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x9B, 0x83, 0xD4, 0xD5, + 0xDE, 0x16, 0x25, 0x8E, 0xE5, 0x15, 0xF2, 0x18, 0x9D, 0x19, 0x1C, 0xF8, + 0xFE, 0x91, 0xA5, 0x83, 0xAE, 0xA5, 0xA8, 0x95, 0x3F, 0x01, 0xB2, 0xC9, + 0x34, 0xFB, 0xC7, 0x51, 0x2D, 0xAC, 0xFF, 0x38, 0xE6, 0xB6, 0x7B, 0x08, + 0x4A, 0xDF, 0x98, 0xA3, 0xFD, 0x31, 0x81, 0xBF, 0xAA, 0xD1, 0x62, 0x58, + 0xC0, 0x6C, 0x8F, 0x8E, 0xCD, 0x96, 0xCE, 0x6D, 0x03, 0x44, 0x59, 0x93, + 0xCE, 0xEA, 0x8D, 0xF4, 0xD4, 0x6F, 0x6F, 0x34, 0x5D, 0x50, 0xF1, 0xAE, + 0x99, 0x7F, 0x1D, 0x92, 0x15, 0xF3, 0x6B, 0xDB, 0xF9, 0x95, 0x8B, 0x3F, + 0x54, 0xAD, 0x37, 0xB5, 0x4F, 0x0A, 0x58, 0x7B, 0x48, 0xA2, 0x9F, 0x9E, + 0xA3, 0x16, 0xC8, 0xBD, 0x37, 0xDA, 0x9A, 0x37, 0xE6, 0x3F, 0x10, 0x1B, + 0xA8, 0x4F, 0xA3, 0x14, 0xFA, 0xBE, 0x12, 0xFB, 0xD7, 0x19, 0x4C, 0xED, + 0xAD, 0xA2, 0x95, 0x8F, 0x39, 0x8C, 0xC4, 0x69, 0x0F, 0x7D, 0xB8, 0x84, + 0x0A, 0x99, 0x5C, 0x53, 0x2F, 0xDE, 0xF2, 0x1B, 0xC5, 0x1D, 0x4C, 0x43, + 0x3C, 0x97, 0xA7, 0xBA, 0x8F, 0xC3, 0x22, 0x67, 0x39, 0xC2, 0x62, 0x74, + 0x3A, 0x0C, 0xB5, 0x57, 0x01, 0x3A, 0x67, 0xC6, 0xDE, 0x0C, 0x0B, 0xF6, + 0x08, 0x01, 0x64, 0xDB, 0xBD, 0x81, 0xE4, 0xDC, 0x09, 0x2E, 0xD0, 0xF1, + 0xD0, 0xD6, 0x1E, 0xBA, 0x38, 0x36, 0xF4, 0x4A, 0xDD, 0xCA, 0x39, 0xEB, + 0x76, 0xCF, 0x95, 0xDC, 0x48, 0x4C, 0xF2, 0x43, 0x8C, 0xD9, 0x44, 0x26, + 0x7A, 0x9E, 0xEB, 0x99, 0xA3, 0xD8, 0xFB, 0x30, 0xA8, 0x14, 0x42, 0x82, + 0x8D, 0xB4, 0x31, 0xB3, 0x1A, 0xD5, 0x2B, 0xF6, 0x32, 0xBC, 0x62, 0xC0, + 0xFE, 0x81, 0x20, 0x49, 0xE7, 0xF7, 0x58, 0x2F, 0x2D, 0xA6, 0x1B, 0x41, + 0x62, 0xC7, 0xE0, 0x32, 0x02, 0x5D, 0x82, 0xEC, 0xA3, 0xE4, 0x6C, 0x9B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// Chihiro bootloader public key (Segaboot) +xboxkrnl::UCHAR xboxkrnl::XePublicKeyDataChihiroBoot[284] = { + 0x52, 0x53, 0x41, 0x31, 0x08, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x6B, 0x7B, 0x38, 0x78, + 0xE3, 0x16, 0x04, 0x88, 0x1D, 0xAF, 0x63, 0x4E, 0x23, 0xDB, 0x10, 0x14, + 0xB4, 0x52, 0x87, 0xEB, 0xE3, 0x37, 0xC2, 0x35, 0x6E, 0x38, 0x08, 0xDC, + 0x07, 0xC5, 0x92, 0x17, 0xC0, 0xBF, 0xDB, 0xF5, 0x9E, 0x72, 0xDB, 0x2F, + 0x98, 0x93, 0x6E, 0x98, 0x5E, 0xD9, 0x69, 0x80, 0x17, 0xFC, 0x0C, 0x72, + 0x2E, 0xE6, 0x75, 0x85, 0x48, 0xEA, 0xBD, 0xDA, 0x6E, 0x82, 0xD9, 0xFB, + 0x10, 0xAA, 0x11, 0xEE, 0xB5, 0xC1, 0xF2, 0x53, 0xD6, 0xF0, 0xD3, 0x4B, + 0xC2, 0x11, 0x4F, 0x8A, 0x18, 0xFB, 0xB7, 0x36, 0xFC, 0xDD, 0xD0, 0xBF, + 0x5C, 0x32, 0x44, 0x40, 0xEB, 0x92, 0x70, 0xA4, 0xEF, 0x3A, 0xAB, 0x78, + 0x66, 0x1A, 0x03, 0x0A, 0x9E, 0xC5, 0x3A, 0xB7, 0x8F, 0xE5, 0xB1, 0x5E, + 0x44, 0x15, 0xBA, 0x42, 0xD9, 0x10, 0x2A, 0x60, 0x93, 0x47, 0x4C, 0x5B, + 0xE1, 0x24, 0x04, 0x1E, 0x5C, 0x95, 0xB2, 0x17, 0x34, 0xD2, 0x37, 0x5F, + 0x85, 0x83, 0x62, 0x8D, 0x6E, 0x90, 0x69, 0x06, 0xB9, 0xFB, 0x7A, 0x24, + 0x8A, 0xE6, 0xCC, 0x77, 0x1E, 0x0A, 0x8C, 0x2B, 0x3B, 0xA2, 0x33, 0x79, + 0x24, 0x8C, 0xD3, 0x88, 0x01, 0x3A, 0x38, 0x7F, 0xF0, 0xAB, 0x9E, 0x2F, + 0x74, 0xCE, 0x50, 0xD1, 0xC2, 0x00, 0x57, 0xD3, 0xA7, 0x09, 0x45, 0x36, + 0xFA, 0xC1, 0xC7, 0x1B, 0x65, 0xAD, 0x98, 0x9C, 0x63, 0xED, 0xBA, 0x99, + 0x9B, 0x07, 0x3E, 0x57, 0xBD, 0xB5, 0x52, 0x44, 0x72, 0x09, 0x43, 0xE0, + 0x5C, 0x35, 0xCC, 0xE4, 0xE0, 0x85, 0x6A, 0x61, 0xAA, 0xF5, 0x0D, 0x1E, + 0xE7, 0x8F, 0xB0, 0xB9, 0xE3, 0xC3, 0x83, 0x10, 0x6C, 0x2F, 0x5D, 0xD4, + 0xAB, 0x2D, 0xAB, 0x4D, 0xCE, 0xC9, 0x7F, 0x52, 0x39, 0x13, 0xED, 0x44, + 0x06, 0x23, 0x2F, 0x62, 0xFF, 0xA1, 0x2B, 0xEE, 0x07, 0x98, 0x7D, 0xBC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index d72d3fca1..77d522adc 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -1,1895 +1,1895 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::CXBXR -#define LOG_PREFIX_INIT CXBXR_MODULE::INIT - - -#include -#include "gui/resource/ResCxbx.h" -#include "core\kernel\init\CxbxKrnl.h" -#include "common\xbdm\CxbxXbdm.h" // For Cxbx_LibXbdmThunkTable -#include "CxbxVersion.h" -#include "core\kernel\support\Emu.h" -#include "devices\x86\EmuX86.h" -#include "core\kernel\support\EmuFile.h" -#include "core\kernel\support\EmuFS.h" // EmuInitFS -#include "EmuEEPROM.h" // For CxbxRestoreEEPROM, EEPROM, XboxFactoryGameRegion -#include "core\kernel\exports\EmuKrnl.h" -#include "core\kernel\exports\EmuKrnlKi.h" -#include "EmuShared.h" -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow, EmuD3DInit -#include "core\hle\DSOUND\DirectSound\DirectSound.hpp" // For CxbxInitAudio -#include "core\hle\Intercept.hpp" -#include "ReservedMemory.h" // For virtual_memory_placeholder -#include "core\kernel\memory-manager\VMManager.h" -#include "CxbxDebugger.h" -#include "common/util/cliConfig.hpp" -#include "common/util/xxhash.h" -#include "common/ReserveAddressRanges.h" -#include "common/xbox/Types.hpp" - -#include -#include -#include // For time() -#include // For std::ostringstream - -#include "devices\EEPROMDevice.h" // For g_EEPROM -#include "devices\Xbox.h" // For InitXboxHardware() -#include "devices\LED.h" // For LED::Sequence -#include "devices\SMCDevice.h" // For SMC Access -#include "common\crypto\EmuSha.h" // For the SHA1 functions -#include "Timer.h" // For Timer_Init -#include "common\input\InputManager.h" // For the InputDeviceManager - -/*! thread local storage */ -Xbe::TLS *CxbxKrnl_TLS = NULL; -/*! thread local storage data */ -void *CxbxKrnl_TLSData = NULL; -/*! xbe header structure */ -Xbe::Header *CxbxKrnl_XbeHeader = NULL; -/*! parent window handle */ - -/*! indicates a debug kernel */ -bool g_bIsDebugKernel = false; - -HWND CxbxKrnl_hEmuParent = NULL; -DebugMode CxbxKrnl_DebugMode = DebugMode::DM_NONE; -std::string CxbxKrnl_DebugFileName = ""; -Xbe::Certificate *g_pCertificate = NULL; - -/*! thread handles */ -static std::vector g_hThreads; - -char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 }; -char szFolder_CxbxReloadedData[MAX_PATH] = { 0 }; -char szFilePath_EEPROM_bin[MAX_PATH] = { 0 }; -char szFilePath_Xbe[MAX_PATH*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is MAX_PATH*2 = 520 - -std::string CxbxBasePath; -HANDLE CxbxBasePathHandle; -Xbe* CxbxKrnl_Xbe = NULL; -bool g_bIsChihiro = false; -bool g_bIsDebug = false; -bool g_bIsRetail = false; -DWORD_PTR g_CPUXbox = 0; -DWORD_PTR g_CPUOthers = 0; - -// Indicates to disable/enable all interrupts when cli and sti instructions are executed -std::atomic_bool g_bEnableAllInterrupts = true; - -// Set by the VMManager during initialization. Exported because it's needed in other parts of the emu -size_t g_SystemMaxMemory = 0; - -HANDLE g_CurrentProcessHandle = 0; // Set in CxbxKrnlMain -bool g_bIsWine = false; - -bool g_CxbxPrintUEM = false; -ULONG g_CxbxFatalErrorCode = FATAL_ERROR_NONE; - -// Define function located in EmuXApi so we can call it from here -void SetupXboxDeviceTypes(); - -void ApplyMediaPatches() -{ - // Patch the XBE Header to allow running from all media types - g_pCertificate->dwAllowedMedia |= 0 - | XBEIMAGE_MEDIA_TYPE_HARD_DISK - | XBEIMAGE_MEDIA_TYPE_DVD_X2 - | XBEIMAGE_MEDIA_TYPE_DVD_CD - | XBEIMAGE_MEDIA_TYPE_CD - | XBEIMAGE_MEDIA_TYPE_DVD_5_RO - | XBEIMAGE_MEDIA_TYPE_DVD_9_RO - | XBEIMAGE_MEDIA_TYPE_DVD_5_RW - | XBEIMAGE_MEDIA_TYPE_DVD_9_RW - ; - // Patch the XBE Header to allow running on all regions - g_pCertificate->dwGameRegion = 0 - | XBEIMAGE_GAME_REGION_MANUFACTURING - | XBEIMAGE_GAME_REGION_NA - | XBEIMAGE_GAME_REGION_JAPAN - | XBEIMAGE_GAME_REGION_RESTOFWORLD - ; - // Patch the XBE Security Flag - // This field is only present if the Xbe Size is >= than our Certificate Structure - // This works as our structure is large enough to fit the newer certificate size, - // while dwSize is the actual size of the certificate in the Xbe. - // Source: Various Hacked Kernels - if (g_pCertificate->dwSize >= sizeof(Xbe::Certificate)) { - g_pCertificate->dwSecurityFlags &= ~1; - } -} - -void SetupPerTitleKeys() -{ - // Generate per-title keys from the XBE Certificate - UCHAR Digest[20] = {}; - - // Set the LAN Key - xboxkrnl::XcHMAC(xboxkrnl::XboxCertificateKey, xboxkrnl::XBOX_KEY_LENGTH, g_pCertificate->bzLanKey, xboxkrnl::XBOX_KEY_LENGTH, NULL, 0, Digest); - memcpy(xboxkrnl::XboxLANKey, Digest, xboxkrnl::XBOX_KEY_LENGTH); - - // Signature Key - xboxkrnl::XcHMAC(xboxkrnl::XboxCertificateKey, xboxkrnl::XBOX_KEY_LENGTH, g_pCertificate->bzSignatureKey, xboxkrnl::XBOX_KEY_LENGTH, NULL, 0, Digest); - memcpy(xboxkrnl::XboxSignatureKey, Digest, xboxkrnl::XBOX_KEY_LENGTH); - - // Alternate Signature Keys - for (int i = 0; i < xboxkrnl::ALTERNATE_SIGNATURE_COUNT; i++) { - xboxkrnl::XcHMAC(xboxkrnl::XboxCertificateKey, xboxkrnl::XBOX_KEY_LENGTH, g_pCertificate->bzTitleAlternateSignatureKey[i], xboxkrnl::XBOX_KEY_LENGTH, NULL, 0, Digest); - memcpy(xboxkrnl::XboxAlternateSignatureKeys[i], Digest, xboxkrnl::XBOX_KEY_LENGTH); - } - -} - -void CxbxLaunchXbe(void(*Entry)()) -{ - Entry(); -} - -// Entry point address XOR keys per Xbe type (Retail, Debug or Chihiro) : -const DWORD XOR_EP_KEY[3] = { XOR_EP_RETAIL, XOR_EP_DEBUG, XOR_EP_CHIHIRO }; -// Kernel thunk address XOR keys per Xbe type (Retail, Debug or Chihiro) : -const DWORD XOR_KT_KEY[3] = { XOR_KT_RETAIL, XOR_KT_DEBUG, XOR_KT_CHIHIRO }; - -// Executable image header pointers (it's contents can be switched between -// Exe-compatibility and Xbe-identical mode, using RestoreExeImageHeader -// vs RestoreXbeImageHeader) : -const PIMAGE_DOS_HEADER ExeDosHeader = (PIMAGE_DOS_HEADER)XBE_IMAGE_BASE; -PIMAGE_NT_HEADERS ExeNtHeader = nullptr; -PIMAGE_OPTIONAL_HEADER ExeOptionalHeader = nullptr; - -// Copy of original executable image headers, used both as backup and valid replacement structure : -PIMAGE_DOS_HEADER NewDosHeader = nullptr; -PIMAGE_NT_HEADERS NewNtHeader = nullptr; -PIMAGE_OPTIONAL_HEADER NewOptionalHeader = nullptr; - -// Xbe backup values. RestoreXbeImageHeader place these into ExeHeader to restore loaded Xbe contents. -WORD Xbe_magic = 0; -LONG Xbe_lfanew = 0; -IMAGE_DATA_DIRECTORY Xbe_TLS = { }; - -// Remember the current XBE contents of the executable image -// header fields that RestoreExeImageHeader needs to restore. -void StoreXbeImageHeader() -{ - Xbe_magic = ExeDosHeader->e_magic; // Normally 0x4258 = 'XB'; (...'EH') - Xbe_lfanew = ExeDosHeader->e_lfanew; - Xbe_TLS = ExeOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; -} - -// Restore memory to the exact contents as loaded from the current XBE. -// Avoid threadswitches and calling Windows API's while this in effect -// because those can fail. Hence, RestoreExeImageHeader quickly again! -void RestoreXbeImageHeader() -{ - ExeDosHeader->e_magic = Xbe_magic; // Sets XbeHeader.dwMagic - ExeDosHeader->e_lfanew = Xbe_lfanew; // Sets part of XbeHeader.pbDigitalSignature - ExeOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS] = Xbe_TLS; -} - -// Restore memory to the exact contents loaded from the running EXE. -// This is required to keep thread-switching and Windows API's working. -void RestoreExeImageHeader() -{ - ExeDosHeader->e_magic = NewDosHeader->e_magic; // = 0x5A4D = 'MZ'; Overwrites XbeHeader.dwMagic - ExeDosHeader->e_lfanew = NewDosHeader->e_lfanew; // Overwrites part of XbeHeader.pbDigitalSignature - ExeOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS] = NewOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; -} - -typedef const char* (CDECL *LPFN_WINEGETVERSION)(void); -LPFN_WINEGETVERSION wine_get_version; - -// Forward declaration to avoid moving the definition of LoadXboxKeys -void LoadXboxKeys(std::string path); - -// Returns the Win32 error in string format. Returns an empty string if there is no error. -std::string CxbxGetErrorCodeAsString(DWORD errorCode) -{ - std::string result; - LPSTR lpMessageBuffer = nullptr; - DWORD dwLength = FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, // lpSource - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&lpMessageBuffer, - 0, // nSize - NULL); // Arguments - if (dwLength > 0) { - result = std::string(lpMessageBuffer, dwLength); - } - - LocalFree(lpMessageBuffer); - return result; -} - -// Returns the last Win32 error, in string format. Returns an empty string if there is no error. -std::string CxbxGetLastErrorString(char * lpszFunction) -{ - DWORD errorCode = ::GetLastError(); // Do this first, before any following code changes it - std::string result = "No error"; - if (errorCode > 0) { - std::ostringstream stringStream; - stringStream << lpszFunction << " failed with error " << errorCode << ": " << CxbxGetErrorCodeAsString(errorCode); - result = stringStream.str(); - } - - return result; -} - -#pragma optimize("", off) - -void PrintCurrentConfigurationLog() -{ - if (g_bIsWine) { - EmuLogInit(LOG_LEVEL::INFO, "Running under Wine Version %s", wine_get_version()); - } - - // HACK: For API TRace.. - // bLLE_GPU = true; - - // Print current LLE configuration - { - EmuLogInit(LOG_LEVEL::INFO, "---------------------------- LLE CONFIG ----------------------------"); - EmuLogInit(LOG_LEVEL::INFO, "LLE for APU is %s", bLLE_APU ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "LLE for GPU is %s", bLLE_GPU ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "LLE for USB is %s", bLLE_USB ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "LLE for JIT is %s", bLLE_JIT ? "enabled" : "disabled"); - } - - // Print current video configuration (DirectX/HLE) - if (!bLLE_GPU) { - Settings::s_video XBVideoConf; - g_EmuShared->GetVideoSettings(&XBVideoConf); - - EmuLogInit(LOG_LEVEL::INFO, "--------------------------- VIDEO CONFIG ---------------------------"); - EmuLogInit(LOG_LEVEL::INFO, "Direct3D Device: %s", XBVideoConf.direct3DDevice == 0 ? "Direct3D HAL (Hardware Accelerated)" : "Direct3D REF (Software)"); - EmuLogInit(LOG_LEVEL::INFO, "Video Resolution: %s", XBVideoConf.szVideoResolution); - EmuLogInit(LOG_LEVEL::INFO, "Force VSync is %s", XBVideoConf.bVSync ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "Fullscreen is %s", XBVideoConf.bFullScreen ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "Hardware YUV is %s", XBVideoConf.bHardwareYUV ? "enabled" : "disabled"); - } - - // Print current audio configuration - { - Settings::s_audio XBAudioConf; - g_EmuShared->GetAudioSettings(&XBAudioConf); - - EmuLogInit(LOG_LEVEL::INFO, "--------------------------- AUDIO CONFIG ---------------------------"); - EmuLogInit(LOG_LEVEL::INFO, "Audio Adapter: %s", XBAudioConf.adapterGUID.Data1 == 0 ? "Primary Audio Device" : "Secondary Audio Device"); - EmuLogInit(LOG_LEVEL::INFO, "PCM is %s", XBAudioConf.codec_pcm ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "XADPCM is %s", XBAudioConf.codec_xadpcm ? "enabled" : "disabled"); - EmuLogInit(LOG_LEVEL::INFO, "Unknown Codec is %s", XBAudioConf.codec_unknown ? "enabled" : "disabled"); - } - - // Print current network configuration - { - Settings::s_network XBNetworkConf; - g_EmuShared->GetNetworkSettings(&XBNetworkConf); - - EmuLogInit(LOG_LEVEL::INFO, "--------------------------- NETWORK CONFIG -------------------------"); - EmuLogInit(LOG_LEVEL::INFO, "Network Adapter Name: %s", strlen(XBNetworkConf.adapter_name) == 0 ? "Not Configured" : XBNetworkConf.adapter_name); - } - - // Print Enabled Hacks - { - EmuLogInit(LOG_LEVEL::INFO, "--------------------------- HACKS CONFIG ---------------------------"); - EmuLogInit(LOG_LEVEL::INFO, "Disable Pixel Shaders: %s", g_DisablePixelShaders == 1 ? "On" : "Off (Default)"); - EmuLogInit(LOG_LEVEL::INFO, "Run Xbox threads on all cores: %s", g_UseAllCores == 1 ? "On" : "Off (Default)"); - EmuLogInit(LOG_LEVEL::INFO, "Skip RDTSC Patching: %s", g_SkipRdtscPatching == 1 ? "On" : "Off (Default)"); - } - - EmuLogInit(LOG_LEVEL::INFO, "------------------------- END OF CONFIG LOG ------------------------"); - -} - -#if 0 -BOOLEAN ApcInterrupt -( - IN struct _KINTERRUPT *Interrupt, - IN PVOID ServiceContext -) -{ - -} - -BOOLEAN DispatchInterrupt -( - IN struct _KINTERRUPT *Interrupt, - IN PVOID ServiceContext -) -{ - ExecuteDpcQueue(); -} - -void InitSoftwareInterrupts() -{ - // Init software interrupt 1 (for APC dispatching) - xboxkrnl::KINTERRUPT SoftwareInterrupt_1; - SoftwareInterrupt_1.BusInterruptLevel = 1; - SoftwareInterrupt_1.ServiceRoutine = ApcInterrupt; - xboxkrnl::KeConnectInterrupt(&SoftwareInterrupt_1); - - // Init software interrupt 2 (for DPC dispatching) - xboxkrnl::KINTERRUPT SoftwareInterrupt_2; - SoftwareInterrupt_2.BusInterruptLevel = 2; - SoftwareInterrupt_2.ServiceRoutine = DispatchInterrupt; - xboxkrnl::KeConnectInterrupt(&SoftwareInterrupt_2); -} -#endif - -void TriggerPendingConnectedInterrupts() -{ - for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) { - // If the interrupt is pending and connected, process it - if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) { - HalSystemInterrupts[i].Trigger(EmuInterruptList[i]); - } - SwitchToThread(); - } -} - -static unsigned int WINAPI CxbxKrnlInterruptThread(PVOID param) -{ - CxbxSetThreadName("CxbxKrnl Interrupts"); - - // Make sure Xbox1 code runs on one core : - InitXboxThread(g_CPUXbox); - -#if 0 - InitSoftwareInterrupts(); -#endif - - while (true) { - if (g_bEnableAllInterrupts) { - TriggerPendingConnectedInterrupts(); - } - Sleep(1); - } - - return 0; -} - -static void CxbxKrnlClockThread(void* pVoid) -{ - LARGE_INTEGER CurrentTicks; - uint64_t Delta; - uint64_t Microseconds; - unsigned int IncrementScaling; - static uint64_t LastTicks = 0; - static uint64_t Error = 0; - static uint64_t UnaccountedMicroseconds = 0; - - // This keeps track of how many us have elapsed between two cycles, so that the xbox clocks are updated - // with the proper increment (instead of blindly adding a single increment at every step) - - if (LastTicks == 0) { - QueryPerformanceCounter(&CurrentTicks); - LastTicks = CurrentTicks.QuadPart; - CurrentTicks.QuadPart = 0; - } - - QueryPerformanceCounter(&CurrentTicks); - Delta = CurrentTicks.QuadPart - LastTicks; - LastTicks = CurrentTicks.QuadPart; - - Error += (Delta * SCALE_S_IN_US); - Microseconds = Error / HostClockFrequency; - Error -= (Microseconds * HostClockFrequency); - - UnaccountedMicroseconds += Microseconds; - IncrementScaling = (unsigned int)(UnaccountedMicroseconds / 1000); // -> 1 ms = 1000us -> time between two xbox clock interrupts - UnaccountedMicroseconds -= (IncrementScaling * 1000); - - xboxkrnl::KiClockIsr(IncrementScaling); -} - -std::vector g_RdtscPatches; - -#define OPCODE_PATCH_RDTSC 0x90EF // OUT DX, EAX; NOP - -bool IsRdtscInstruction(xbaddr addr) -{ - // First the fastest check - does addr contain exact patch from PatchRdtsc? - // Second check - is addr on the rdtsc patch list? - return (*(uint16_t*)addr == OPCODE_PATCH_RDTSC) - // Note : It's not needed to check for g_SkipRdtscPatching, - // as when that's set, the g_RdtscPatches vector will be empty - // anyway, failing this lookup : - && (std::find(g_RdtscPatches.begin(), g_RdtscPatches.end(), addr) != g_RdtscPatches.end()); -} - -void PatchRdtsc(xbaddr addr) -{ - // Patch away rdtsc with an opcode we can intercept - // We use a privilaged instruction rather than int 3 for debugging - // When using int 3, attached debuggers trap and rdtsc is used often enough - // that it makes Cxbx-Reloaded unusable - // A privilaged instruction (like OUT) does not suffer from this - EmuLogInit(LOG_LEVEL::DEBUG, "Patching rdtsc opcode at 0x%.8X", (DWORD)addr); - *(uint16_t*)addr = OPCODE_PATCH_RDTSC; - g_RdtscPatches.push_back(addr); -} - -const uint8_t rdtsc_pattern[] = { - 0x89,//{ 0x0F,0x31,0x89 }, - 0xC3,//{ 0x0F,0x31,0xC3 }, - 0x8B,//{ 0x0F,0x31,0x8B }, //one false positive in Sonic Rider .text 88 5C 0F 31 - 0xB9,//{ 0x0F,0x31,0xB9 }, - 0xC7,//{ 0x0F,0x31,0xC7 }, - 0x8D,//{ 0x0F,0x31,0x8D }, - 0x68,//{ 0x0F,0x31,0x68 }, - 0x5A,//{ 0x0F,0x31,0x5A }, - 0x29,//{ 0x0F,0x31,0x29 }, - 0xF3,//{ 0x0F,0x31,0xF3 }, - 0xE9,//{ 0x0F,0x31,0xE9 }, - 0x2B,//{ 0x0F,0x31,0x2B }, - 0x50,//{ 0x0F,0x31,0x50 }, // 0x50 only used in ExaSkeleton .text , but encounter false positive in RalliSport .text 83 E2 0F 31 - 0x0F,//{ 0x0F,0x31,0x0F }, - 0x3B,//{ 0x0F,0x31,0x3B }, - 0xD9,//{ 0x0F,0x31,0xD9 }, - 0x57,//{ 0x0F,0x31,0x57 }, - 0xB9,//{ 0x0F,0x31,0xB9 }, - 0x85,//{ 0x0F,0x31,0x85 }, - 0x83,//{ 0x0F,0x31,0x83 }, - 0x33,//{ 0x0F,0x31,0x33 }, - 0xF7,//{ 0x0F,0x31,0xF7 }, - 0x8A,//{ 0x0F,0x31,0x8A }, // 8A and 56 only apears in RalliSport 2 .text , need to watch whether any future false positive. - 0x56,//{ 0x0F,0x31,0x56 } - 0x6A, // 6A, 39, EB, F6, A1, 01 only appear in Unreal Championship, 01 is at WMVDEC section - 0x39, - 0xEB, - 0xF6, - 0xA1, - 0x01 -}; -const int sizeof_rdtsc_pattern = sizeof(rdtsc_pattern); - -void PatchRdtscInstructions() -{ - uint8_t rdtsc[2] = { 0x0F, 0x31 }; - DWORD sizeOfImage = CxbxKrnl_XbeHeader->dwSizeofImage; - - // Iterate through each CODE section - for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) { - if (!CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwFlags.bExecutable) { - continue; - } - - // Skip some segments known to never contain rdtsc (to avoid false positives) - if (std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "DSOUND" - || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "XGRPH" - || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == ".data" - || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == ".rdata" - || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "XMV" - || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "XONLINE" - || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "MDLPL") { - continue; - } - - EmuLogInit(LOG_LEVEL::INFO, "Searching for rdtsc in section %s", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); - xbaddr startAddr = CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwVirtualAddr; - //rdtsc is two bytes instruction, it needs at least one opcode byte after it to finish a function, so the endAddr need to substract 3 bytes. - xbaddr endAddr = startAddr + CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwSizeofRaw-3; - for (xbaddr addr = startAddr; addr <= endAddr; addr++) - { - if (memcmp((void*)addr, rdtsc, 2) == 0) - { - uint8_t next_byte = *(uint8_t*)(addr + 2); - // If the following byte matches the known pattern. - int i = 0; - for (i = 0; i= sizeof_rdtsc_pattern) - { - //no pattern matched, keep record for detections we treat as non-rdtsc for future debugging. - EmuLogInit(LOG_LEVEL::INFO, "Skipped potential rdtsc: Unknown opcode pattern 0x%.2X, @ 0x%.8X", next_byte, (DWORD)addr); - } - } - } - } - - EmuLogInit(LOG_LEVEL::INFO, "Done patching rdtsc, total %d rdtsc instructions patched", g_RdtscPatches.size()); -} - -void MapThunkTable(uint32_t* kt, uint32_t* pThunkTable) -{ - const bool SendDebugReports = (pThunkTable == CxbxKrnl_KernelThunkTable) && CxbxDebugger::CanReport(); - - uint32_t* kt_tbl = (uint32_t*)kt; - int i = 0; - while (kt_tbl[i] != 0) { - int t = kt_tbl[i] & 0x7FFFFFFF; - kt_tbl[i] = pThunkTable[t]; - if (SendDebugReports) { - // TODO: Update CxbxKrnl_KernelThunkTable to include symbol names - std::string importName = "KernelImport_" + std::to_string(t); - CxbxDebugger::ReportKernelPatch(importName.c_str(), kt_tbl[i]); - } - i++; - } -} - -typedef struct { - xbaddr ThunkAddr; - xbaddr LibNameAddr; -} XbeImportEntry; - -void ImportLibraries(XbeImportEntry *pImportDirectory) -{ - // assert(pImportDirectory); - - while (pImportDirectory->LibNameAddr && pImportDirectory->ThunkAddr) { - std::wstring LibName = std::wstring((wchar_t*)pImportDirectory->LibNameAddr); - - if (LibName == L"xbdm.dll") { - MapThunkTable((uint32_t *)pImportDirectory->ThunkAddr, Cxbx_LibXbdmThunkTable); - } - else { - // TODO: replace wprintf to EmuLogInit, how? - wprintf(L"LOAD : Skipping unrecognized import library : %s\n", LibName.c_str()); - } - - pImportDirectory++; - } -} - -bool CreateSettings() -{ - g_Settings = new Settings(); - if (g_Settings == nullptr) { - PopupError(nullptr, szSettings_alloc_error); - return false; - } - - if (!g_Settings->Init()) { - return false; - } - - log_get_settings(); - return true; -} - -bool HandleFirstLaunch() -{ - bool bFirstLaunch; - g_EmuShared->GetIsFirstLaunch(&bFirstLaunch); - - /* check if process is launch with elevated access then prompt for continue on or not. */ - if (!bFirstLaunch) { - if (!CreateSettings()) { - return false; - } - - bool bElevated = CxbxIsElevated(); - if (bElevated && !g_Settings->m_core.allowAdminPrivilege) { - PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No, - "Cxbx-Reloaded has detected that it has been launched with Administrator rights.\n" - "\nThis is dangerous, as a maliciously modified Xbox titles could take control of your system.\n" - "\nAre you sure you want to continue?"); - if (ret != PopupReturn::Yes) { - return false; - } - } - - g_EmuShared->SetIsFirstLaunch(true); - } - - return true; -} - -void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved) -{ - std::string tempStr; - - // NOTE: This is designated for standalone kernel mode launch without GUI - if (g_Settings != nullptr) { - - // Reset to default - g_EmuShared->Reset(); - - g_Settings->Verify(); - g_Settings->SyncToEmulator(); - - // We don't need to keep Settings open plus allow emulator to use unused memory. - delete g_Settings; - g_Settings = nullptr; - - // Perform identical to what GUI will do to certain EmuShared's variable before launch. - g_EmuShared->SetIsEmulating(true); - - // NOTE: This setting the ready status is optional. Internal kernel process is checking if GUI is running. - // Except if enforce check, then we need to re-set ready status every time for non-GUI. - //g_EmuShared->SetIsReady(true); - } - - /* Initialize popup message management from kernel side. */ - log_init_popup_msg(); - - /* Initialize Cxbx File Paths */ - CxbxInitFilePaths(); - - // Skip '/load' switch - // Get XBE Name : - std::string xbePath; - cli_config::GetValue(cli_config::load, &xbePath); - xbePath = std::filesystem::absolute(std::filesystem::path(xbePath)).string(); - - // Get DCHandle : - // We must save this handle now to keep the child window working in the case we need to display the UEM - HWND hWnd = nullptr; - if (cli_config::GetValue(cli_config::hwnd, &tempStr)) { - hWnd = (HWND)std::atoi(tempStr.c_str()); - } - CxbxKrnl_hEmuParent = IsWindow(hWnd) ? hWnd : nullptr; - - // Get KernelDebugMode : - DebugMode DbgMode = DebugMode::DM_NONE; - if (cli_config::GetValue(cli_config::debug_mode, &tempStr)) { - DbgMode = (DebugMode)std::atoi(tempStr.c_str()); - } - - // Get KernelDebugFileName : - std::string DebugFileName = ""; - if (cli_config::GetValue(cli_config::debug_file, &tempStr)) { - DebugFileName = tempStr; - } - - int BootFlags; - g_EmuShared->GetBootFlags(&BootFlags); - - g_CurrentProcessHandle = GetCurrentProcess(); // OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); - - // Set up the logging variables for the kernel process during initialization. - log_sync_config(); - - // When a reboot occur, we need to keep persistent memory buffer open before emulation process shutdown. - if ((BootFlags & BOOT_QUICK_REBOOT) != 0) { - g_VMManager.GetPersistentMemory(); - } - - if (CxbxKrnl_hEmuParent != NULL) { - ipc_send_gui_update(IPC_UPDATE_GUI::KRNL_IS_READY, static_cast(GetCurrentProcessId())); - - // Force wait until GUI process is ready - do { - int waitCounter = 10; - bool isReady = false; - - while (waitCounter > 0) { - g_EmuShared->GetIsReady(&isReady); - if (isReady) { - break; - } - waitCounter--; - Sleep(100); - } - if (!isReady) { - EmuLog(LOG_LEVEL::WARNING, "GUI process is not ready!"); - PopupReturn mbRet = PopupWarningEx(nullptr, PopupButtons::RetryCancel, PopupReturn::Cancel, - "GUI process is not ready, do you wish to retry?"); - if (mbRet == PopupReturn::Retry) { - continue; - } - CxbxKrnlShutDown(); - } - break; - } while (true); - } - - g_EmuShared->SetIsReady(false); - - UINT prevKrnlProcID = 0; - DWORD dwExitCode = EXIT_SUCCESS; - g_EmuShared->GetKrnlProcID(&prevKrnlProcID); - - // Save current kernel proccess id for next reboot if will occur in the future. - // And to tell previous kernel process we had take over. This allow reboot's shared memory buffer to survive. - g_EmuShared->SetKrnlProcID(GetCurrentProcessId()); - - // Force wait until previous kernel process is closed. - if (prevKrnlProcID != 0) { - HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, prevKrnlProcID); - // If we do receive valid handle, let's do the next step. - if (hProcess != NULL) { - - WaitForSingleObject(hProcess, INFINITE); - - GetExitCodeProcess(hProcess, &dwExitCode); - CloseHandle(hProcess); - } - } - - if (dwExitCode != EXIT_SUCCESS) {// Stop emulation - CxbxKrnlShutDown(); - } - - /* Must be called after CxbxInitFilePaths and previous kernel process shutdown. */ - if (!CxbxLockFilePath()) { - return; - } - - FILE* krnlLog = nullptr; - // debug console allocation (if configured) - if (DbgMode == DM_CONSOLE) - { - if (AllocConsole()) - { - HANDLE StdHandle = GetStdHandle(STD_OUTPUT_HANDLE); - // Maximise the console scroll buffer height : - CONSOLE_SCREEN_BUFFER_INFO coninfo; - GetConsoleScreenBufferInfo(StdHandle, &coninfo); - coninfo.dwSize.Y = SHRT_MAX - 1; // = 32767-1 = 32766 = maximum value that works - SetConsoleScreenBufferSize(StdHandle, coninfo.dwSize); - (void)freopen("CONOUT$", "wt", stdout); - (void)freopen("CONIN$", "rt", stdin); - SetConsoleTitle("Cxbx-Reloaded : Kernel Debug Console"); - SetConsoleTextAttribute(StdHandle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); - } - } - else - { - FreeConsole(); - if (DbgMode == DM_FILE) { - // Peform clean write to kernel log for first boot. Unless multi-xbe boot occur then perform append to existing log. - krnlLog = freopen(DebugFileName.c_str(), ((BootFlags == DebugMode::DM_NONE) ? "wt" : "at"), stdout); - // Append separator for better readability after reboot. - if (BootFlags != DebugMode::DM_NONE) { - std::cout << "\n------REBOOT------REBOOT------REBOOT------REBOOT------REBOOT------\n" << std::endl; - } - } - else { - char buffer[16]; - if (GetConsoleTitle(buffer, 16) != NULL) - (void)freopen("nul", "w", stdout); - } - } - - bool isLogEnabled; - g_EmuShared->GetIsKrnlLogEnabled(&isLogEnabled); - g_bPrintfOn = isLogEnabled; - - g_EmuShared->ResetKrnl(); - - // Write a header to the log - { - EmuLogInit(LOG_LEVEL::INFO, "Cxbx-Reloaded Version %s", CxbxVersionStr); - - time_t startTime = time(nullptr); - struct tm* tm_info = localtime(&startTime); - char timeString[26]; - strftime(timeString, 26, "%F %T", tm_info); - EmuLogInit(LOG_LEVEL::INFO, "Log started at %s", timeString); - -#ifdef _DEBUG_TRACE - EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Enabled."); -#else - EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Disabled."); -#endif - } - - // Log once, since multi-xbe boot is appending to file instead of overwrite. - if (BootFlags == BOOT_NONE) { - log_generate_active_filter_output(CXBXR_MODULE::INIT); - } - - // Detect Wine - g_bIsWine = false; - HMODULE hNtDll = GetModuleHandle("ntdll.dll"); - - if (hNtDll != nullptr) { - wine_get_version = (LPFN_WINEGETVERSION)GetProcAddress(hNtDll, "wine_get_version"); - if (wine_get_version) { - g_bIsWine = true; - } - } - - // Now we got the arguments, start by initializing the Xbox memory map : - // PrepareXBoxMemoryMap() - { - // Our executable DOS image header must be loaded at 0x00010000 - // Assert(ExeDosHeader == XBE_IMAGE_BASE); - - // Determine EXE's header locations & size : - ExeNtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)ExeDosHeader + ExeDosHeader->e_lfanew); // = + 0x138 - ExeOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(ExeNtHeader->OptionalHeader); - - // verify base of code of our executable is 0x00001000 - if (ExeNtHeader->OptionalHeader.BaseOfCode != CXBX_BASE_OF_CODE) - { - PopupFatal(nullptr, "Cxbx-Reloaded executuable requires it's base of code to be 0x00001000"); - return; // TODO : Halt(0); - } - -#ifndef CXBXR_EMU - // verify virtual_memory_placeholder is located at 0x00011000 - if ((UINT_PTR)(&(virtual_memory_placeholder[0])) != (XBE_IMAGE_BASE + CXBX_BASE_OF_CODE)) - { - PopupFatal(nullptr, "virtual_memory_placeholder is not loaded to base address 0x00011000 (which is a requirement for Xbox emulation)"); - return; // TODO : Halt(0); - } -#endif - - // Create a safe copy of the complete EXE header: - DWORD ExeHeaderSize = ExeOptionalHeader->SizeOfHeaders; // Should end up as 0x400 - NewDosHeader = (PIMAGE_DOS_HEADER)VirtualAlloc(nullptr, ExeHeaderSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - memcpy(NewDosHeader, ExeDosHeader, ExeHeaderSize); - - // Determine NewOptionalHeader, required by RestoreExeImageHeader - NewNtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)NewDosHeader + ExeDosHeader->e_lfanew); - NewOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(NewNtHeader->OptionalHeader); - - // Make sure the new DOS header points to the new relative NtHeader location: - NewDosHeader->e_lfanew = (ULONG_PTR)NewNtHeader - XBE_IMAGE_BASE; - - // Note : NewOptionalHeader->ImageBase can stay at ExeOptionalHeader->ImageBase = 0x00010000 - - // Note : Since virtual_memory_placeholder prevents overlap between reserved xbox memory - // and Cxbx.exe sections, section headers don't have to be patched up. - - // Mark the virtual memory range completely accessible - DWORD OldProtection; - if (0 == VirtualProtect((void*)XBE_IMAGE_BASE, XBE_MAX_VA - XBE_IMAGE_BASE, PAGE_EXECUTE_READWRITE, &OldProtection)) { - DWORD err = GetLastError(); - - // Translate ErrorCode to String. - LPTSTR Error = 0; - if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - err, - 0, - (LPTSTR)&Error, - 0, - NULL) == 0) { - // Failed in translating. - } - - // Free the buffer. - if (Error) { - ::LocalFree(Error); - Error = 0; - } - } - - // Clear out the virtual memory range - memset((void*)XBE_IMAGE_BASE, 0, XBE_MAX_VA - XBE_IMAGE_BASE); - - // Restore enough of the executable image headers to keep WinAPI's working : - RestoreExeImageHeader(); - } - - // Load Per-Xbe Keys from the Cxbx-Reloaded AppData directory - LoadXboxKeys(szFolder_CxbxReloadedData); - - EEPROM = CxbxRestoreEEPROM(szFilePath_EEPROM_bin); - if (EEPROM == nullptr) - { - PopupFatal(nullptr, "Couldn't init EEPROM!"); - return; // TODO : Halt(0); - } - - // TODO : Instead of loading an Xbe here, initialize the kernel so that it will launch the Xbe on itself. - // using XeLoadImage from LaunchDataPage->Header.szLaunchPath - - // Now we can load and run the XBE : - // MapAndRunXBE(XbePath, DCHandle); - XbeType xbeType = XbeType::xtRetail; - { - // NOTE: This is a safety to clean the file path for any malicious file path attempt. - // Might want to move this into a utility function. - size_t n, i; - // Remove useless slashes before and after semicolon. - std::string semicolon_search[] = { "\\;", ";\\", "/;", ";/" }; - std::string semicolon_str = ";"; - for (n = 0, i = 0; i < semicolon_search->size(); i++, n = 0) { - while ((n = xbePath.find(semicolon_search[i], n)) != std::string::npos) { - xbePath.replace(n, semicolon_search[i].size(), semicolon_str); - n += semicolon_str.size(); - } - } - // Remove extra slashes. - std::string slash_search[] = { "\\\\", "//" }; - std::string slash_str = "/"; - for (n = 0, i = 0; i < slash_search->size(); i++, n = 0) { - while ((n = xbePath.find(slash_search[i], n)) != std::string::npos) { - xbePath.replace(n, slash_search[i].size(), slash_str); - n += slash_str.size(); - } - } - - // Once clean up process is done, proceed set to global variable string. - strncpy(szFilePath_Xbe, xbePath.c_str(), MAX_PATH - 1); - std::replace(xbePath.begin(), xbePath.end(), ';', '/'); - // Load Xbe (this one will reside above WinMain's virtual_memory_placeholder) - CxbxKrnl_Xbe = new Xbe(xbePath.c_str(), false); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock() - - if (CxbxKrnl_Xbe->HasFatalError()) { - CxbxKrnlCleanup(CxbxKrnl_Xbe->GetError().c_str()); - return; - } - - // Check the signature of the xbe - if (CxbxKrnl_Xbe->CheckSignature()) { - EmuLogInit(LOG_LEVEL::INFO, "Valid xbe signature. Xbe is legit"); - } - else { - EmuLogInit(LOG_LEVEL::WARNING, "Invalid xbe signature. Homebrew, tampered or pirated xbe?"); - } - - // Check the integrity of the xbe sections - for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) { - if (CxbxKrnl_Xbe->CheckSectionIntegrity(sectionIndex)) { - EmuLogInit(LOG_LEVEL::INFO, "SHA hash check of section %s successful", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); - } - else { - EmuLogInit(LOG_LEVEL::WARNING, "SHA hash of section %s doesn't match, section is corrupted", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); - } - } - - // If CLI has given console type, then enforce it. - if (cli_config::hasKey(cli_config::system_chihiro)) { - EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as chihiro."); - xbeType = XbeType::xtChihiro; - } - else if (cli_config::hasKey(cli_config::system_devkit)) { - EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as devkit."); - xbeType = XbeType::xtDebug; - } - else if (cli_config::hasKey(cli_config::system_retail)) { - EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as retail."); - xbeType = XbeType::xtRetail; - } - // Otherwise, use auto detect method. - else { - // Detect XBE type : - xbeType = CxbxKrnl_Xbe->GetXbeType(); - EmuLogInit(LOG_LEVEL::INFO, "Auto detect: XbeType = %s", GetXbeTypeToStr(xbeType)); - } - - EmuLogInit(LOG_LEVEL::INFO, "Host's compatible system types: %2X", reserved_systems); - unsigned int emulate_system = 0; - // Set reserved_systems which system we will about to emulate. - if (isSystemFlagSupport(reserved_systems, SYSTEM_CHIHIRO) && xbeType == XbeType::xtChihiro) { - emulate_system = SYSTEM_CHIHIRO; - } - else if (isSystemFlagSupport(reserved_systems, SYSTEM_DEVKIT) && xbeType == XbeType::xtDebug) { - emulate_system = SYSTEM_DEVKIT; - } - else if (isSystemFlagSupport(reserved_systems, SYSTEM_XBOX) && xbeType == XbeType::xtRetail) { - emulate_system = SYSTEM_XBOX; - } - // If none of system type requested to emulate isn't supported on host's end. Then enforce failure. - else { - CxbxKrnlCleanup("Unable to emulate system type due to host is not able to reserve required memory ranges."); - return; - } - // Clear emulation system from reserved systems to be free. - reserved_systems &= ~emulate_system; - - // Once we have determine which system type to run as, enforce it in future reboots. - if ((BootFlags & BOOT_QUICK_REBOOT) == 0) { - const char* system_str = GetSystemTypeToStr(emulate_system); - cli_config::SetSystemType(system_str); - } - - // Register if we're running an Chihiro executable or a debug xbe, otherwise it's an Xbox retail executable - g_bIsChihiro = (xbeType == XbeType::xtChihiro); - g_bIsDebug = (xbeType == XbeType::xtDebug); - g_bIsRetail = (xbeType == XbeType::xtRetail); - - // Disabled: The media board rom fails to run because it REQUIRES LLE USB, which is not yet enabled. - // Chihiro games can be ran directly for now. - // This just means that you cannot access the Chihiro test menus and related stuff, games should still be okay -#if 0 - // If the Xbe is Chihiro, and we were not launched by SEGABOOT, we need to load SEGABOOT from the Chihiro Media Board rom instead! - // TODO: We also need to store the path of the loaded game, and mount it as the mediaboard filesystem - // TODO: How to we detect who launched us, to prevent a reboot-loop - if (g_bIsChihiro) { - std::string chihiroMediaBoardRom = std::string(szFolder_CxbxReloadedData) + std::string("/EmuDisk/") + MediaBoardRomFile; - if (!std::filesystem::exists(chihiroMediaBoardRom)) { - CxbxKrnlCleanup("Chihiro Media Board ROM (fpr21042_m29w160et.bin) could not be found"); - } - - delete CxbxKrnl_Xbe; - CxbxKrnl_Xbe = new Xbe(chihiroMediaBoardRom.c_str(), false); - } -#endif - -#ifndef CXBXR_EMU - // Only for GUI executable with emulation code. - blocks_reserved_t blocks_reserved_gui = { 0 }; - // Reserve console system's memory ranges before start initialize. - if (!ReserveAddressRanges(emulate_system, blocks_reserved_gui)) { - CxbxKrnlCleanup("Failed to reserve required memory ranges!", GetLastError()); - } - // Initialize the memory manager - g_VMManager.Initialize(emulate_system, BootFlags, blocks_reserved_gui); -#else - // Release unnecessary memory ranges to allow console/host to use those memory ranges. - FreeAddressRanges(emulate_system, reserved_systems, blocks_reserved); - // Initialize the memory manager - g_VMManager.Initialize(emulate_system, BootFlags, blocks_reserved); -#endif - - // Commit the memory used by the xbe header - size_t HeaderSize = CxbxKrnl_Xbe->m_Header.dwSizeofHeaders; - VAddr XbeBase = XBE_IMAGE_BASE; - g_VMManager.XbAllocateVirtualMemory(&XbeBase, 0, &HeaderSize, XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE); - - - // Copy over loaded Xbe Headers to specified base address - memcpy((void*)CxbxKrnl_Xbe->m_Header.dwBaseAddr, &CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header)); - memcpy((void*)(CxbxKrnl_Xbe->m_Header.dwBaseAddr + sizeof(Xbe::Header)), CxbxKrnl_Xbe->m_HeaderEx, CxbxKrnl_Xbe->m_ExSize); - - // Load all sections marked as preload using the in-memory copy of the xbe header - xboxkrnl::PXBEIMAGE_SECTION sectionHeaders = (xboxkrnl::PXBEIMAGE_SECTION)CxbxKrnl_Xbe->m_Header.dwSectionHeadersAddr; - for (uint32_t i = 0; i < CxbxKrnl_Xbe->m_Header.dwSections; i++) { - if ((sectionHeaders[i].Flags & XBEIMAGE_SECTION_PRELOAD) != 0) { - NTSTATUS result = xboxkrnl::XeLoadSection(§ionHeaders[i]); - if (FAILED(result)) { - EmuLogInit(LOG_LEVEL::WARNING, "Failed to preload XBE section: %s", CxbxKrnl_Xbe->m_szSectionName[i]); - } - } - } - - // We need to remember a few XbeHeader fields, so we can switch between a valid ExeHeader and XbeHeader : - StoreXbeImageHeader(); - - // Restore enough of the executable image headers to keep WinAPI's working : - RestoreExeImageHeader(); - - // HACK: Attempt to patch out XBE header reads - // This works by searching for the XBEH signature and replacing it with what appears in host address space instead - // Test case: Half Life 2 - // Iterate through each CODE section - for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) { - if (!CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwFlags.bExecutable) { - continue; - } - - EmuLogInit(LOG_LEVEL::INFO, "Searching for XBEH in section %s", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); - xbaddr startAddr = CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwVirtualAddr; - xbaddr endAddr = startAddr + CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwSizeofRaw; - for (xbaddr addr = startAddr; addr < endAddr; addr++) { - if (*(uint32_t*)addr == 0x48454258) { - EmuLogInit(LOG_LEVEL::INFO, "Patching XBEH at 0x%08X", addr); - *((uint32_t*)addr) = *(uint32_t*)XBE_IMAGE_BASE; - } - } - } - } - - // Decode kernel thunk table address : - uint32_t kt = CxbxKrnl_Xbe->m_Header.dwKernelImageThunkAddr; - kt ^= XOR_KT_KEY[to_underlying(xbeType)]; - - // Process the Kernel thunk table to map Kernel function calls to their actual address : - MapThunkTable((uint32_t*)kt, CxbxKrnl_KernelThunkTable); - - // Does this xbe import any other libraries? - if (CxbxKrnl_Xbe->m_Header.dwNonKernelImportDirAddr) { - ImportLibraries((XbeImportEntry*)CxbxKrnl_Xbe->m_Header.dwNonKernelImportDirAddr); - } - - g_ExceptionManager = new ExceptionManager(); // If in need to add VEHs, move this line earlier. (just in case) - - // Launch the XBE : - { - // Load TLS - Xbe::TLS* XbeTls = (Xbe::TLS*)CxbxKrnl_Xbe->m_Header.dwTLSAddr; - void* XbeTlsData = (XbeTls != nullptr) ? (void*)CxbxKrnl_Xbe->m_TLS->dwDataStartAddr : nullptr; - // Decode Entry Point - xbaddr EntryPoint = CxbxKrnl_Xbe->m_Header.dwEntryAddr; - EntryPoint ^= XOR_EP_KEY[to_underlying(xbeType)]; - // Launch XBE - CxbxKrnlInit( - XbeTlsData, - XbeTls, - CxbxKrnl_Xbe->m_LibraryVersion, - DbgMode, - DebugFileName.c_str(), - (Xbe::Header*)CxbxKrnl_Xbe->m_Header.dwBaseAddr, - CxbxKrnl_Xbe->m_Header.dwSizeofHeaders, - (void(*)())EntryPoint, - BootFlags - ); - } - - if (!krnlLog) { - (void)fclose(krnlLog); - } -} -#pragma optimize("", on) - -// Loads a keys.bin file as generated by dump-xbox -// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c -void LoadXboxKeys(std::string path) -{ - std::string keys_path = path + "\\keys.bin"; - - // Attempt to open Keys.bin - FILE* fp = fopen(keys_path.c_str(), "rb"); - - if (fp != nullptr) { - // Determine size of Keys.bin - xboxkrnl::XBOX_KEY_DATA keys[2]; - fseek(fp, 0, SEEK_END); - long size = ftell(fp); - rewind(fp); - - // If the size of Keys.bin is correct (two keys), read it - if (size == xboxkrnl::XBOX_KEY_LENGTH * 2) { - fread(keys, xboxkrnl::XBOX_KEY_LENGTH, 2, fp); - - memcpy(xboxkrnl::XboxEEPROMKey, &keys[0], xboxkrnl::XBOX_KEY_LENGTH); - memcpy(xboxkrnl::XboxCertificateKey, &keys[1], xboxkrnl::XBOX_KEY_LENGTH); - } - else { - EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xboxkrnl::XBOX_KEY_LENGTH * 2); - } - - fclose(fp); - return; - } - - // If we didn't already exit the function, keys.bin could not be loaded - EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox"); -} - -__declspec(noreturn) void CxbxKrnlInit -( - void *pTLSData, - Xbe::TLS *pTLS, - Xbe::LibraryVersion *pLibraryVersion, - DebugMode DbgMode, - const char *szDebugFilename, - Xbe::Header *pXbeHeader, - uint32_t dwXbeHeaderSize, - void(*Entry)(), - int BootFlags) -{ - // Set windows timer period to 1ms - // Windows will automatically restore this value back to original on program exit - // But with this, we can replace some busy loops with sleeps. - timeBeginPeriod(1); - - xboxkrnl::InitializeFscCacheEvent(); - - // update caches - CxbxKrnl_TLS = pTLS; - CxbxKrnl_TLSData = pTLSData; - CxbxKrnl_XbeHeader = pXbeHeader; - CxbxKrnl_DebugMode = DbgMode; - CxbxKrnl_DebugFileName = (char*)szDebugFilename; - - // A patch to dwCertificateAddr is a requirement due to Windows TLS is overwriting dwGameRegion data address. - // By using unalternated certificate data, it should no longer cause any problem with titles running and Cxbx's log as well. - CxbxKrnl_XbeHeader->dwCertificateAddr = (uint32_t)&CxbxKrnl_Xbe->m_Certificate; - g_pCertificate = &CxbxKrnl_Xbe->m_Certificate; - - // Initialize timer subsystem - Timer_Init(); - // for unicode conversions - setlocale(LC_ALL, "English"); - // Initialize time-related variables for the kernel and the timers - CxbxInitPerformanceCounters(); -#ifdef _DEBUG -// PopupCustom(LOG_LEVEL::INFO, "Attach a Debugger"); -// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool -#endif - - // debug trace - { -#ifdef _DEBUG_TRACE - EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Enabled."); - EmuLogInit(LOG_LEVEL::INFO, "CxbxKrnlInit\n" - "(\n" - " hwndParent : 0x%.08p\n" - " pTLSData : 0x%.08p\n" - " pTLS : 0x%.08p\n" - " pLibraryVersion : 0x%.08p\n" - " DebugConsole : 0x%.08X\n" - " DebugFilename : \"%s\"\n" - " pXBEHeader : 0x%.08p\n" - " dwXBEHeaderSize : 0x%.08X\n" - " Entry : 0x%.08p\n" - ");", - CxbxKrnl_hEmuParent, pTLSData, pTLS, pLibraryVersion, DbgMode, szDebugFilename, pXbeHeader, dwXbeHeaderSize, Entry); -#else - EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Disabled."); -#endif - } - -#ifdef _DEBUG_TRACE - // VerifyHLEDataBase(); -#endif - // TODO : The following seems to cause a crash when booting the game "Forza Motorsport", - // according to https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/101#issuecomment-277230140 - { - // Create a fake kernel header for XapiRestrictCodeSelectorLimit - // Thanks advancingdragon / DirtBox - PDUMMY_KERNEL DummyKernel = (PDUMMY_KERNEL)XBOX_KERNEL_BASE; - memset(DummyKernel, 0, sizeof(DUMMY_KERNEL)); - - // XapiRestrictCodeSelectorLimit only checks these fields. - DummyKernel->DosHeader.e_lfanew = sizeof(IMAGE_DOS_HEADER); // RVA of NtHeaders - DummyKernel->FileHeader.SizeOfOptionalHeader = 0; - DummyKernel->FileHeader.NumberOfSections = 1; - // as long as this doesn't start with "INIT" - strncpy_s((PSTR)DummyKernel->SectionHeader.Name, 8, "DONGS", 8); - EmuLogInit(LOG_LEVEL::INFO, "Initialized dummy kernel image header."); - } - - // Read which components need to be LLE'ed per user request - { - unsigned int CxbxLLE_Flags; - g_EmuShared->GetFlagsLLE(&CxbxLLE_Flags); - bLLE_APU = (CxbxLLE_Flags & LLE_APU) > 0; - bLLE_GPU = (CxbxLLE_Flags & LLE_GPU) > 0; - //bLLE_USB = (CxbxLLE_Flags & LLE_USB) > 0; // Reenable this when LLE USB actually works - bLLE_JIT = (CxbxLLE_Flags & LLE_JIT) > 0; - } - - // Process Hacks - { - int HackEnabled = 0; - g_EmuShared->GetDisablePixelShaders(&HackEnabled); - g_DisablePixelShaders = !!HackEnabled; - g_EmuShared->GetUseAllCores(&HackEnabled); - g_UseAllCores = !!HackEnabled; - g_EmuShared->GetSkipRdtscPatching(&HackEnabled); - g_SkipRdtscPatching = !!HackEnabled; - } - -#ifdef _DEBUG_PRINT_CURRENT_CONF - PrintCurrentConfigurationLog(); -#endif - - // Initialize devices : - char szBuffer[sizeof(szFilePath_Xbe)]; - g_EmuShared->GetStorageLocation(szBuffer); - - CxbxBasePath = std::string(szBuffer) + "\\EmuDisk\\"; - - // Determine XBE Path - strncpy(szBuffer, szFilePath_Xbe, sizeof(szBuffer)-1); - szBuffer[sizeof(szBuffer) - 1] = '\0'; // Safely null terminate at the end. - - std::string xbePath(szBuffer); - std::replace(xbePath.begin(), xbePath.end(), ';', '/'); - std::string xbeDirectory(szBuffer); - size_t lastFind = xbeDirectory.find(';'); - // First find if there is a semicolon when dashboard or title disc (such as demo disc) has it. - // Then we must obey the current directory it asked for. - if (lastFind != std::string::npos) { - if (xbeDirectory.find(';', lastFind + 1) != std::string::npos) { - CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "Cannot contain multiple of ; symbol."); - } - xbeDirectory = xbeDirectory.substr(0, lastFind); - } - else { - xbeDirectory = xbeDirectory.substr(0, xbeDirectory.find_last_of("\\/")); - } - CxbxBasePathHandle = CreateFile(CxbxBasePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - memset(szBuffer, 0, sizeof(szBuffer)); - // Games may assume they are running from CdRom : - CxbxDefaultXbeDriveIndex = CxbxRegisterDeviceHostPath(DeviceCdrom0, xbeDirectory); - // Partition 0 contains configuration data, and is accessed as a native file, instead as a folder : - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition0, CxbxBasePath + "Partition0", /*IsFile=*/true); - // The first two partitions are for Data and Shell files, respectively : - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition1, CxbxBasePath + "Partition1"); - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition2, CxbxBasePath + "Partition2"); - // The following partitions are for caching purposes - for now we allocate up to 7 (as xbmp needs that many) : - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition3, CxbxBasePath + "Partition3"); - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition4, CxbxBasePath + "Partition4"); - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition5, CxbxBasePath + "Partition5"); - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition6, CxbxBasePath + "Partition6"); - CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition7, CxbxBasePath + "Partition7"); - - // Create default symbolic links : - EmuLogInit(LOG_LEVEL::DEBUG, "Creating default symbolic links."); - { - // TODO: DriveD should always point to the Xbe Path - // This is the only symbolic link the Xbox Kernel sets, the rest are set by the application, usually via XAPI. - // If the Xbe is located outside of the emulated HDD, mounting it as DeviceCdrom0 is correct - // If the Xbe is located inside the emulated HDD, the full path should be used, eg: "\\Harddisk0\\partition2\\xboxdash.xbe" - CxbxCreateSymbolicLink(DriveD, DeviceCdrom0); - // Arrange that the Xbe path can reside outside the partitions, and put it to g_hCurDir : - EmuNtSymbolicLinkObject* xbePathSymbolicLinkObject = FindNtSymbolicLinkObjectByDriveLetter(CxbxDefaultXbeDriveLetter); - g_hCurDir = xbePathSymbolicLinkObject->RootDirectoryHandle; - } - - // Determine Xbox path to XBE and place it in XeImageFileName - { - std::string fileName(xbePath); - // Strip out the path, leaving only the XBE file name - // NOTE: we assume that the XBE is always on the root of the D: drive - // This is a safe assumption as the Xbox kernel ALWAYS mounts D: as the Xbe Path - if (fileName.rfind('\\') != std::string::npos) - fileName = fileName.substr(fileName.rfind('\\') + 1); - - if (xboxkrnl::XeImageFileName.Buffer != NULL) - free(xboxkrnl::XeImageFileName.Buffer); - - // Assign the running Xbe path, so it can be accessed via the kernel thunk 'XeImageFileName' : - xboxkrnl::XeImageFileName.MaximumLength = MAX_PATH; - xboxkrnl::XeImageFileName.Buffer = (PCHAR)g_VMManager.Allocate(MAX_PATH); - sprintf(xboxkrnl::XeImageFileName.Buffer, "%c:\\%s", CxbxDefaultXbeDriveLetter, fileName.c_str()); - xboxkrnl::XeImageFileName.Length = (USHORT)strlen(xboxkrnl::XeImageFileName.Buffer); - EmuLogInit(LOG_LEVEL::INFO, "XeImageFileName = %s", xboxkrnl::XeImageFileName.Buffer); - } - - // Dump Xbe information - { - if (CxbxKrnl_Xbe != nullptr) { - EmuLogInit(LOG_LEVEL::INFO, "Title : %s", CxbxKrnl_Xbe->m_szAsciiTitle); - } - - // Dump Xbe certificate - if (g_pCertificate != NULL) { - std::stringstream titleIdHex; - titleIdHex << std::hex << g_pCertificate->dwTitleId; - - EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID : %s", FormatTitleId(g_pCertificate->dwTitleId).c_str()); - EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID (Hex) : 0x%s", titleIdHex.str().c_str()); - EmuLogInit(LOG_LEVEL::INFO, "XBE Version : 1.%02d", g_pCertificate->dwVersion); - EmuLogInit(LOG_LEVEL::INFO, "XBE TitleName : %ls", g_pCertificate->wszTitleName); - EmuLogInit(LOG_LEVEL::INFO, "XBE Region : %s", CxbxKrnl_Xbe->GameRegionToString()); - } - - // Dump Xbe library build numbers - Xbe::LibraryVersion* libVersionInfo = pLibraryVersion;// (LibraryVersion *)(CxbxKrnl_XbeHeader->dwLibraryVersionsAddr); - if (libVersionInfo != NULL) { - for (uint32_t v = 0; v < CxbxKrnl_XbeHeader->dwLibraryVersions; v++) { - EmuLogInit(LOG_LEVEL::INFO, "XBE Library %u : %.8s (version %d)", v, libVersionInfo->szName, libVersionInfo->wBuildVersion); - libVersionInfo++; - } - } - } - - CxbxKrnlRegisterThread(GetCurrentThread()); - - // Make sure the Xbox1 code runs on one core (as the box itself has only 1 CPU, - // this will better aproximate the environment with regard to multi-threading) : - EmuLogInit(LOG_LEVEL::DEBUG, "Determining CPU affinity."); - { - if (!GetProcessAffinityMask(g_CurrentProcessHandle, &g_CPUXbox, &g_CPUOthers)) - CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "GetProcessAffinityMask failed."); - - // For the other threads, remove one bit from the processor mask: - g_CPUOthers = ((g_CPUXbox - 1) & g_CPUXbox); - - // Test if there are any other cores available : - if (g_CPUOthers > 0) { - // If so, make sure the Xbox threads run on the core NOT running Xbox code : - g_CPUXbox = g_CPUXbox & (~g_CPUOthers); - } else { - // Else the other threads must run on the same core as the Xbox code : - g_CPUOthers = g_CPUXbox; - } - } - - // initialize graphics - EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window."); - CxbxInitWindow(true); - - // Now process the boot flags to see if there are any special conditions to handle - if (BootFlags & BOOT_EJECT_PENDING) {} // TODO - if (BootFlags & BOOT_FATAL_ERROR) - { - // If we are here it means we have been rebooted to display the fatal error screen. The error code is set - // to 0x15 and the led flashes with the sequence green, red, red, red - - SetLEDSequence(0xE1); - CxbxKrnlPrintUEM(FATAL_ERROR_REBOOT_ROUTINE); // won't return - } - if (BootFlags & BOOT_SKIP_ANIMATION) {} // TODO - if (BootFlags & BOOT_RUN_DASHBOARD) {} // TODO - - CxbxInitAudio(); - - EmuHLEIntercept(pXbeHeader); - - if (!bLLE_USB) { - SetupXboxDeviceTypes(); - } - - InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable - - // Read Xbox video mode from the SMC, store it in HalBootSMCVideoMode - xboxkrnl::HalReadSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_AV_PACK, FALSE, &xboxkrnl::HalBootSMCVideoMode); - - g_InputDeviceManager.Initialize(false); - - // Now the hardware devices exist, couple the EEPROM buffer to it's device - g_EEPROM->SetEEPROM((uint8_t*)EEPROM); - - if (!bLLE_GPU) - { - EmuLogInit(LOG_LEVEL::DEBUG, "Initializing Direct3D."); - EmuD3DInit(); - } - - if (CxbxDebugger::CanReport()) - { - CxbxDebugger::ReportDebuggerInit(CxbxKrnl_Xbe->m_szAsciiTitle); - } - - // Apply Media Patches to bypass Anti-Piracy checks - // Required until we perfect emulation of X2 DVD Authentication - // See: https://multimedia.cx/eggs/xbox-sphinx-protocol/ - ApplyMediaPatches(); - - // Chihiro games require more patches - // The chihiro BIOS does this to bypass XAPI cache init - if (g_bIsChihiro) { - CxbxKrnl_XbeHeader->dwInitFlags.bDontSetupHarddisk = true; - } - - if(!g_SkipRdtscPatching) - { - PatchRdtscInstructions(); - } - - // Setup per-title encryption keys - SetupPerTitleKeys(); - - EmuInitFS(); - - InitXboxThread(g_CPUXbox); - xboxkrnl::ObInitSystem(); - xboxkrnl::KiInitSystem(); - - EmuX86_Init(); - // Create the interrupt processing thread - DWORD dwThreadId; - HANDLE hThread = (HANDLE)_beginthreadex(NULL, NULL, CxbxKrnlInterruptThread, NULL, NULL, (unsigned int*)&dwThreadId); - // Start the kernel clock thread - TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", &g_CPUOthers); - Timer_Start(KernelClockThr, SCALE_MS_IN_NS); - - EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point..."); - CxbxLaunchXbe(Entry); - - // FIXME: Wait for Cxbx to exit or error fatally - Sleep(INFINITE); - - EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned"); - fflush(stdout); - - CxbxUnlockFilePath(); - - // EmuShared::Cleanup(); FIXME: commenting this line is a bad workaround for issue #617 (https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/617) - CxbxKrnlTerminateThread(); -} - -void CxbxInitFilePaths() -{ - if (g_Settings) { - std::string dataLoc = g_Settings->GetDataLocation(); - std::strncpy(szFolder_CxbxReloadedData, dataLoc.c_str(), dataLoc.length() + 1); - } - else { - g_EmuShared->GetStorageLocation(szFolder_CxbxReloadedData); - } - - // Make sure our data folder exists : - bool result = std::filesystem::exists(szFolder_CxbxReloadedData); - if (!result && !std::filesystem::create_directory(szFolder_CxbxReloadedData)) { - CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__); - } - - // Make sure the EmuDisk folder exists - std::string emuDisk = std::string(szFolder_CxbxReloadedData) + std::string("\\EmuDisk"); - result = std::filesystem::exists(emuDisk); - if (!result && !std::filesystem::create_directory(emuDisk)) { - CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__); - } - - snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", szFolder_CxbxReloadedData); - - GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH); -} - -HANDLE hMapDataHash = nullptr; - -bool CxbxLockFilePath() -{ - std::stringstream filePathHash("Local\\"); - uint64_t hashValue = XXH3_64bits(szFolder_CxbxReloadedData, strlen(szFolder_CxbxReloadedData) + 1); - if (!hashValue) { - CxbxKrnlCleanup("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__); - } - - filePathHash << std::hex << hashValue; - - hMapDataHash = CreateFileMapping - ( - INVALID_HANDLE_VALUE, // Paging file - nullptr, // default security attributes - PAGE_READONLY, // readonly access - 0, // size: high 32 bits - /*Dummy size*/4, // size: low 32 bits - filePathHash.str().c_str() // name of map object - ); - - if (hMapDataHash == nullptr) { - return false; - } - - if (GetLastError() == ERROR_ALREADY_EXISTS) { - PopupError(nullptr, "Data path directory is currently in used.\nUse different data path directory or stop emulation from another process."); - CloseHandle(hMapDataHash); - return false; - } - - return true; -} - -void CxbxUnlockFilePath() -{ - // Close opened file path lockdown shared memory. - if (hMapDataHash) { - CloseHandle(hMapDataHash); - hMapDataHash = nullptr; - } -} - -// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes -/*xboxkrnl::LAUNCH_DATA_PAGE DefaultLaunchDataPage = -{ - { // header - 2, // 2: dashboard, 0: title - 0, - "D:\\default.xbe", - 0 - } -};*/ - -__declspec(noreturn) void CxbxKrnlCleanupEx(CXBXR_MODULE cxbxr_module, const char *szErrorMessage, ...) -{ - g_bEmuException = true; - - CxbxKrnlResume(); - - // print out error message (if exists) - if(szErrorMessage != NULL) - { - char szBuffer2[1024]; - va_list argp; - - va_start(argp, szErrorMessage); - vsprintf(szBuffer2, szErrorMessage, argp); - va_end(argp); - - (void)PopupCustomEx(nullptr, cxbxr_module, LOG_LEVEL::FATAL, PopupIcon::Error, PopupButtons::Ok, PopupReturn::Ok, "Received Fatal Message:\n\n* %s\n", szBuffer2); // Will also EmuLogEx - } - - EmuLogInit(LOG_LEVEL::INFO, "MAIN: Terminating Process"); - fflush(stdout); - - // cleanup debug output - { - FreeConsole(); - - char buffer[16]; - - if(GetConsoleTitle(buffer, 16) != NULL) - freopen("nul", "w", stdout); - } - - CxbxKrnlShutDown(); -} - -void CxbxKrnlRegisterThread(HANDLE hThread) -{ - // we must duplicate this handle in order to retain Suspend/Resume thread rights from a remote thread - { - HANDLE hDupHandle = NULL; - - if (DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - hThread = hDupHandle; // Thread handle was duplicated, continue registration with the duplicate - } - else { - auto message = CxbxGetLastErrorString("DuplicateHandle"); - EmuLog(LOG_LEVEL::WARNING, message.c_str()); - } - } - - g_hThreads.push_back(hThread); -} - -void CxbxKrnlSuspend() -{ - if(g_bEmuSuspended || g_bEmuException) - return; - - for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it) - { - DWORD dwExitCode; - - if(GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) { - // suspend thread if it is active - SuspendThread(*it); - } else { - // remove thread from thread list if it is dead - g_hThreads.erase(it); - } - } - - // append 'paused' to rendering window caption text - { - char szBuffer[256]; - - HWND hWnd = GET_FRONT_WINDOW_HANDLE; - - GetWindowText(hWnd, szBuffer, 255 - 10); - - strcat(szBuffer, " (paused)"); - SetWindowText(hWnd, szBuffer); - } - - g_bEmuSuspended = true; -} - -void CxbxKrnlResume() -{ - if(!g_bEmuSuspended) - return; - - // remove 'paused' from rendering window caption text - { - char szBuffer[256]; - - HWND hWnd = GET_FRONT_WINDOW_HANDLE; - - GetWindowText(hWnd, szBuffer, 255); - - szBuffer[strlen(szBuffer)-9] = '\0'; - - SetWindowText(hWnd, szBuffer); - } - - for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it) - { - DWORD dwExitCode; - - if (GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) { - // resume thread if it is active - ResumeThread(*it); - } - else { - // remove thread from thread list if it is dead - g_hThreads.erase(it); - } - } - - g_bEmuSuspended = false; -} - -void CxbxKrnlShutDown() -{ - // Clear all kernel boot flags. These (together with the shared memory) persist until Cxbx-Reloaded is closed otherwise. - int BootFlags = 0; - g_EmuShared->SetBootFlags(&BootFlags); - - // NOTE: This causes a hang when exiting while NV2A is processing - // This is okay for now: It won't leak memory or resources since TerminateProcess will free everything - // delete g_NV2A; // TODO : g_pXbox - - // Shutdown the input device manager - g_InputDeviceManager.Shutdown(); - - // Shutdown the memory manager - g_VMManager.Shutdown(); - - CxbxUnlockFilePath(); - - if (CxbxKrnl_hEmuParent != NULL) { - SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0); - } - - EmuShared::Cleanup(); - - if (g_ExceptionManager) { - delete g_ExceptionManager; - g_ExceptionManager = nullptr; - } - - TerminateProcess(g_CurrentProcessHandle, 0); -} - -void CxbxKrnlPrintUEM(ULONG ErrorCode) -{ - ULONG Type; - xboxkrnl::XBOX_EEPROM Eeprom; - ULONG ResultSize; - - xboxkrnl::NTSTATUS status = xboxkrnl::ExQueryNonVolatileSetting(xboxkrnl::XC_MAX_ALL, &Type, &Eeprom, sizeof(Eeprom), &ResultSize); - - if (status == STATUS_SUCCESS) - { - xboxkrnl::XBOX_UEM_INFO* UEMInfo = (xboxkrnl::XBOX_UEM_INFO*)&(Eeprom.UEMInfo[0]); - - if (UEMInfo->ErrorCode == FATAL_ERROR_NONE) - { - // ergo720: the Xbox sets the error code and displays the UEM only for non-manufacturing xbe's (it power cycles - // otherwise). Considering that this flag can be easily tampered with in the xbe and the typical end user of cxbx - // can't fix the cause of the fatal error, I decided to always display it anyway. - - UEMInfo->ErrorCode = (UCHAR)ErrorCode; - UEMInfo->History |= (1 << (ErrorCode - 5)); - } - else { - UEMInfo->ErrorCode = FATAL_ERROR_NONE; - } - xboxkrnl::ExSaveNonVolatileSetting(xboxkrnl::XC_MAX_ALL, Type, &Eeprom, sizeof(Eeprom)); - } - else { - CxbxKrnlCleanup("Could not display the fatal error screen"); - } - - if (g_bIsChihiro) - { - // The Chihiro doesn't display the UEM - CxbxKrnlCleanup("The running Chihiro xbe has encountered a fatal error and needs to close"); - } - - g_CxbxFatalErrorCode = ErrorCode; - g_CxbxPrintUEM = true; // print the UEM - - CxbxPrintUEMInfo(ErrorCode); - - // Sleep forever to prevent continuing the initialization - Sleep(INFINITE); -} - -void CxbxPrintUEMInfo(ULONG ErrorCode) -{ - // See here for a description of the error codes and their meanings: - // https://www.reddit.com/r/originalxbox/wiki/error_codes - - std::map UEMErrorTable; - - UEMErrorTable.emplace(FATAL_ERROR_CORE_DIGITAL, "General motherboard issue"); - UEMErrorTable.emplace(FATAL_ERROR_BAD_EEPROM, "General EEPROM issue"); - UEMErrorTable.emplace(FATAL_ERROR_BAD_RAM, "RAM failure"); - UEMErrorTable.emplace(FATAL_ERROR_HDD_NOT_LOCKED, "HDD is not locked"); - UEMErrorTable.emplace(FATAL_ERROR_HDD_CANNOT_UNLOCK, "Unable to unlock HDD (bad password?)"); - UEMErrorTable.emplace(FATAL_ERROR_HDD_TIMEOUT, "HDD failed to respond"); - UEMErrorTable.emplace(FATAL_ERROR_HDD_NOT_FOUND, "Missing HDD"); - UEMErrorTable.emplace(FATAL_ERROR_HDD_BAD_CONFIG, "Invalid / missing HDD parameter(s)"); - UEMErrorTable.emplace(FATAL_ERROR_DVD_TIMEOUT, "DVD drive failed to respond"); - UEMErrorTable.emplace(FATAL_ERROR_DVD_NOT_FOUND, "Missing DVD drive"); - UEMErrorTable.emplace(FATAL_ERROR_DVD_BAD_CONFIG, "Invalid / missing DVD drive parameter(s)"); - UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_GENERIC, "Generic MS dashboard issue (dashboard not installed?)"); - UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_ERROR, "General MS dashboard issue"); - UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_SETTINGS, "MS dashboard issue: cannot reset console clock"); - UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_X2_PASS, "General MS dashboard issue, DVD drive authentication was successfull"); - UEMErrorTable.emplace(FATAL_ERROR_REBOOT_ROUTINE, "The console was instructed to reboot to this error screen"); - - auto it = UEMErrorTable.find(ErrorCode); - if (it != UEMErrorTable.end()) - { - std::string ErrorMessage = "Fatal error. " + it->second + ". This error screen will persist indefinitely. Stop the emulation to close it."; - PopupFatal(nullptr, ErrorMessage.c_str()); - } - else - { - PopupFatal(nullptr, "Unknown fatal error. This error screen will persist indefinitely. Stop the emulation to close it."); - } -} - -__declspec(noreturn) void CxbxKrnlTerminateThread() -{ - TerminateThread(GetCurrentThread(), 0); -} - -void CxbxKrnlPanic() -{ - CxbxKrnlCleanup("Kernel Panic!"); -} - -static clock_t g_DeltaTime = 0; // Used for benchmarking/fps count -static unsigned int g_Frames = 0; - -// ****************************************************************** -// * update the current milliseconds per frame -// ****************************************************************** -static void UpdateCurrentMSpFAndFPS() { - if (g_EmuShared) { - static float currentFPSVal = 30; - - currentFPSVal = (float)(g_Frames*0.5 + currentFPSVal * 0.5); - g_EmuShared->SetCurrentFPS(¤tFPSVal); - } -} - -void UpdateFPSCounter() -{ - static clock_t lastDrawFunctionCallTime = 0; - clock_t currentDrawFunctionCallTime = clock(); - - g_DeltaTime += currentDrawFunctionCallTime - lastDrawFunctionCallTime; - lastDrawFunctionCallTime = currentDrawFunctionCallTime; - g_Frames++; - - if (g_DeltaTime >= CLOCKS_PER_SEC) { - UpdateCurrentMSpFAndFPS(); - g_Frames = 0; - g_DeltaTime -= CLOCKS_PER_SEC; - } -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::CXBXR +#define LOG_PREFIX_INIT CXBXR_MODULE::INIT + + +#include +#include "gui/resource/ResCxbx.h" +#include "core\kernel\init\CxbxKrnl.h" +#include "common\xbdm\CxbxXbdm.h" // For Cxbx_LibXbdmThunkTable +#include "CxbxVersion.h" +#include "core\kernel\support\Emu.h" +#include "devices\x86\EmuX86.h" +#include "core\kernel\support\EmuFile.h" +#include "core\kernel\support\EmuFS.h" // EmuInitFS +#include "EmuEEPROM.h" // For CxbxRestoreEEPROM, EEPROM, XboxFactoryGameRegion +#include "core\kernel\exports\EmuKrnl.h" +#include "core\kernel\exports\EmuKrnlKi.h" +#include "EmuShared.h" +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxInitWindow, EmuD3DInit +#include "core\hle\DSOUND\DirectSound\DirectSound.hpp" // For CxbxInitAudio +#include "core\hle\Intercept.hpp" +#include "ReservedMemory.h" // For virtual_memory_placeholder +#include "core\kernel\memory-manager\VMManager.h" +#include "CxbxDebugger.h" +#include "common/util/cliConfig.hpp" +#include "common/util/xxhash.h" +#include "common/ReserveAddressRanges.h" +#include "common/xbox/Types.hpp" + +#include +#include +#include // For time() +#include // For std::ostringstream + +#include "devices\EEPROMDevice.h" // For g_EEPROM +#include "devices\Xbox.h" // For InitXboxHardware() +#include "devices\LED.h" // For LED::Sequence +#include "devices\SMCDevice.h" // For SMC Access +#include "common\crypto\EmuSha.h" // For the SHA1 functions +#include "Timer.h" // For Timer_Init +#include "common\input\InputManager.h" // For the InputDeviceManager + +/*! thread local storage */ +Xbe::TLS *CxbxKrnl_TLS = NULL; +/*! thread local storage data */ +void *CxbxKrnl_TLSData = NULL; +/*! xbe header structure */ +Xbe::Header *CxbxKrnl_XbeHeader = NULL; +/*! parent window handle */ + +/*! indicates a debug kernel */ +bool g_bIsDebugKernel = false; + +HWND CxbxKrnl_hEmuParent = NULL; +DebugMode CxbxKrnl_DebugMode = DebugMode::DM_NONE; +std::string CxbxKrnl_DebugFileName = ""; +Xbe::Certificate *g_pCertificate = NULL; + +/*! thread handles */ +static std::vector g_hThreads; + +char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 }; +char szFolder_CxbxReloadedData[MAX_PATH] = { 0 }; +char szFilePath_EEPROM_bin[MAX_PATH] = { 0 }; +char szFilePath_Xbe[MAX_PATH*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is MAX_PATH*2 = 520 + +std::string CxbxBasePath; +HANDLE CxbxBasePathHandle; +Xbe* CxbxKrnl_Xbe = NULL; +bool g_bIsChihiro = false; +bool g_bIsDebug = false; +bool g_bIsRetail = false; +DWORD_PTR g_CPUXbox = 0; +DWORD_PTR g_CPUOthers = 0; + +// Indicates to disable/enable all interrupts when cli and sti instructions are executed +std::atomic_bool g_bEnableAllInterrupts = true; + +// Set by the VMManager during initialization. Exported because it's needed in other parts of the emu +size_t g_SystemMaxMemory = 0; + +HANDLE g_CurrentProcessHandle = 0; // Set in CxbxKrnlMain +bool g_bIsWine = false; + +bool g_CxbxPrintUEM = false; +ULONG g_CxbxFatalErrorCode = FATAL_ERROR_NONE; + +// Define function located in EmuXApi so we can call it from here +void SetupXboxDeviceTypes(); + +void ApplyMediaPatches() +{ + // Patch the XBE Header to allow running from all media types + g_pCertificate->dwAllowedMedia |= 0 + | XBEIMAGE_MEDIA_TYPE_HARD_DISK + | XBEIMAGE_MEDIA_TYPE_DVD_X2 + | XBEIMAGE_MEDIA_TYPE_DVD_CD + | XBEIMAGE_MEDIA_TYPE_CD + | XBEIMAGE_MEDIA_TYPE_DVD_5_RO + | XBEIMAGE_MEDIA_TYPE_DVD_9_RO + | XBEIMAGE_MEDIA_TYPE_DVD_5_RW + | XBEIMAGE_MEDIA_TYPE_DVD_9_RW + ; + // Patch the XBE Header to allow running on all regions + g_pCertificate->dwGameRegion = 0 + | XBEIMAGE_GAME_REGION_MANUFACTURING + | XBEIMAGE_GAME_REGION_NA + | XBEIMAGE_GAME_REGION_JAPAN + | XBEIMAGE_GAME_REGION_RESTOFWORLD + ; + // Patch the XBE Security Flag + // This field is only present if the Xbe Size is >= than our Certificate Structure + // This works as our structure is large enough to fit the newer certificate size, + // while dwSize is the actual size of the certificate in the Xbe. + // Source: Various Hacked Kernels + if (g_pCertificate->dwSize >= sizeof(Xbe::Certificate)) { + g_pCertificate->dwSecurityFlags &= ~1; + } +} + +void SetupPerTitleKeys() +{ + // Generate per-title keys from the XBE Certificate + UCHAR Digest[20] = {}; + + // Set the LAN Key + xboxkrnl::XcHMAC(xboxkrnl::XboxCertificateKey, xboxkrnl::XBOX_KEY_LENGTH, g_pCertificate->bzLanKey, xboxkrnl::XBOX_KEY_LENGTH, NULL, 0, Digest); + memcpy(xboxkrnl::XboxLANKey, Digest, xboxkrnl::XBOX_KEY_LENGTH); + + // Signature Key + xboxkrnl::XcHMAC(xboxkrnl::XboxCertificateKey, xboxkrnl::XBOX_KEY_LENGTH, g_pCertificate->bzSignatureKey, xboxkrnl::XBOX_KEY_LENGTH, NULL, 0, Digest); + memcpy(xboxkrnl::XboxSignatureKey, Digest, xboxkrnl::XBOX_KEY_LENGTH); + + // Alternate Signature Keys + for (int i = 0; i < xboxkrnl::ALTERNATE_SIGNATURE_COUNT; i++) { + xboxkrnl::XcHMAC(xboxkrnl::XboxCertificateKey, xboxkrnl::XBOX_KEY_LENGTH, g_pCertificate->bzTitleAlternateSignatureKey[i], xboxkrnl::XBOX_KEY_LENGTH, NULL, 0, Digest); + memcpy(xboxkrnl::XboxAlternateSignatureKeys[i], Digest, xboxkrnl::XBOX_KEY_LENGTH); + } + +} + +void CxbxLaunchXbe(void(*Entry)()) +{ + Entry(); +} + +// Entry point address XOR keys per Xbe type (Retail, Debug or Chihiro) : +const DWORD XOR_EP_KEY[3] = { XOR_EP_RETAIL, XOR_EP_DEBUG, XOR_EP_CHIHIRO }; +// Kernel thunk address XOR keys per Xbe type (Retail, Debug or Chihiro) : +const DWORD XOR_KT_KEY[3] = { XOR_KT_RETAIL, XOR_KT_DEBUG, XOR_KT_CHIHIRO }; + +// Executable image header pointers (it's contents can be switched between +// Exe-compatibility and Xbe-identical mode, using RestoreExeImageHeader +// vs RestoreXbeImageHeader) : +const PIMAGE_DOS_HEADER ExeDosHeader = (PIMAGE_DOS_HEADER)XBE_IMAGE_BASE; +PIMAGE_NT_HEADERS ExeNtHeader = nullptr; +PIMAGE_OPTIONAL_HEADER ExeOptionalHeader = nullptr; + +// Copy of original executable image headers, used both as backup and valid replacement structure : +PIMAGE_DOS_HEADER NewDosHeader = nullptr; +PIMAGE_NT_HEADERS NewNtHeader = nullptr; +PIMAGE_OPTIONAL_HEADER NewOptionalHeader = nullptr; + +// Xbe backup values. RestoreXbeImageHeader place these into ExeHeader to restore loaded Xbe contents. +WORD Xbe_magic = 0; +LONG Xbe_lfanew = 0; +IMAGE_DATA_DIRECTORY Xbe_TLS = { }; + +// Remember the current XBE contents of the executable image +// header fields that RestoreExeImageHeader needs to restore. +void StoreXbeImageHeader() +{ + Xbe_magic = ExeDosHeader->e_magic; // Normally 0x4258 = 'XB'; (...'EH') + Xbe_lfanew = ExeDosHeader->e_lfanew; + Xbe_TLS = ExeOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; +} + +// Restore memory to the exact contents as loaded from the current XBE. +// Avoid threadswitches and calling Windows API's while this in effect +// because those can fail. Hence, RestoreExeImageHeader quickly again! +void RestoreXbeImageHeader() +{ + ExeDosHeader->e_magic = Xbe_magic; // Sets XbeHeader.dwMagic + ExeDosHeader->e_lfanew = Xbe_lfanew; // Sets part of XbeHeader.pbDigitalSignature + ExeOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS] = Xbe_TLS; +} + +// Restore memory to the exact contents loaded from the running EXE. +// This is required to keep thread-switching and Windows API's working. +void RestoreExeImageHeader() +{ + ExeDosHeader->e_magic = NewDosHeader->e_magic; // = 0x5A4D = 'MZ'; Overwrites XbeHeader.dwMagic + ExeDosHeader->e_lfanew = NewDosHeader->e_lfanew; // Overwrites part of XbeHeader.pbDigitalSignature + ExeOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS] = NewOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; +} + +typedef const char* (CDECL *LPFN_WINEGETVERSION)(void); +LPFN_WINEGETVERSION wine_get_version; + +// Forward declaration to avoid moving the definition of LoadXboxKeys +void LoadXboxKeys(std::string path); + +// Returns the Win32 error in string format. Returns an empty string if there is no error. +std::string CxbxGetErrorCodeAsString(DWORD errorCode) +{ + std::string result; + LPSTR lpMessageBuffer = nullptr; + DWORD dwLength = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, // lpSource + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMessageBuffer, + 0, // nSize + NULL); // Arguments + if (dwLength > 0) { + result = std::string(lpMessageBuffer, dwLength); + } + + LocalFree(lpMessageBuffer); + return result; +} + +// Returns the last Win32 error, in string format. Returns an empty string if there is no error. +std::string CxbxGetLastErrorString(char * lpszFunction) +{ + DWORD errorCode = ::GetLastError(); // Do this first, before any following code changes it + std::string result = "No error"; + if (errorCode > 0) { + std::ostringstream stringStream; + stringStream << lpszFunction << " failed with error " << errorCode << ": " << CxbxGetErrorCodeAsString(errorCode); + result = stringStream.str(); + } + + return result; +} + +#pragma optimize("", off) + +void PrintCurrentConfigurationLog() +{ + if (g_bIsWine) { + EmuLogInit(LOG_LEVEL::INFO, "Running under Wine Version %s", wine_get_version()); + } + + // HACK: For API TRace.. + // bLLE_GPU = true; + + // Print current LLE configuration + { + EmuLogInit(LOG_LEVEL::INFO, "---------------------------- LLE CONFIG ----------------------------"); + EmuLogInit(LOG_LEVEL::INFO, "LLE for APU is %s", bLLE_APU ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "LLE for GPU is %s", bLLE_GPU ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "LLE for USB is %s", bLLE_USB ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "LLE for JIT is %s", bLLE_JIT ? "enabled" : "disabled"); + } + + // Print current video configuration (DirectX/HLE) + if (!bLLE_GPU) { + Settings::s_video XBVideoConf; + g_EmuShared->GetVideoSettings(&XBVideoConf); + + EmuLogInit(LOG_LEVEL::INFO, "--------------------------- VIDEO CONFIG ---------------------------"); + EmuLogInit(LOG_LEVEL::INFO, "Direct3D Device: %s", XBVideoConf.direct3DDevice == 0 ? "Direct3D HAL (Hardware Accelerated)" : "Direct3D REF (Software)"); + EmuLogInit(LOG_LEVEL::INFO, "Video Resolution: %s", XBVideoConf.szVideoResolution); + EmuLogInit(LOG_LEVEL::INFO, "Force VSync is %s", XBVideoConf.bVSync ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "Fullscreen is %s", XBVideoConf.bFullScreen ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "Hardware YUV is %s", XBVideoConf.bHardwareYUV ? "enabled" : "disabled"); + } + + // Print current audio configuration + { + Settings::s_audio XBAudioConf; + g_EmuShared->GetAudioSettings(&XBAudioConf); + + EmuLogInit(LOG_LEVEL::INFO, "--------------------------- AUDIO CONFIG ---------------------------"); + EmuLogInit(LOG_LEVEL::INFO, "Audio Adapter: %s", XBAudioConf.adapterGUID.Data1 == 0 ? "Primary Audio Device" : "Secondary Audio Device"); + EmuLogInit(LOG_LEVEL::INFO, "PCM is %s", XBAudioConf.codec_pcm ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "XADPCM is %s", XBAudioConf.codec_xadpcm ? "enabled" : "disabled"); + EmuLogInit(LOG_LEVEL::INFO, "Unknown Codec is %s", XBAudioConf.codec_unknown ? "enabled" : "disabled"); + } + + // Print current network configuration + { + Settings::s_network XBNetworkConf; + g_EmuShared->GetNetworkSettings(&XBNetworkConf); + + EmuLogInit(LOG_LEVEL::INFO, "--------------------------- NETWORK CONFIG -------------------------"); + EmuLogInit(LOG_LEVEL::INFO, "Network Adapter Name: %s", strlen(XBNetworkConf.adapter_name) == 0 ? "Not Configured" : XBNetworkConf.adapter_name); + } + + // Print Enabled Hacks + { + EmuLogInit(LOG_LEVEL::INFO, "--------------------------- HACKS CONFIG ---------------------------"); + EmuLogInit(LOG_LEVEL::INFO, "Disable Pixel Shaders: %s", g_DisablePixelShaders == 1 ? "On" : "Off (Default)"); + EmuLogInit(LOG_LEVEL::INFO, "Run Xbox threads on all cores: %s", g_UseAllCores == 1 ? "On" : "Off (Default)"); + EmuLogInit(LOG_LEVEL::INFO, "Skip RDTSC Patching: %s", g_SkipRdtscPatching == 1 ? "On" : "Off (Default)"); + } + + EmuLogInit(LOG_LEVEL::INFO, "------------------------- END OF CONFIG LOG ------------------------"); + +} + +#if 0 +BOOLEAN ApcInterrupt +( + IN struct _KINTERRUPT *Interrupt, + IN PVOID ServiceContext +) +{ + +} + +BOOLEAN DispatchInterrupt +( + IN struct _KINTERRUPT *Interrupt, + IN PVOID ServiceContext +) +{ + ExecuteDpcQueue(); +} + +void InitSoftwareInterrupts() +{ + // Init software interrupt 1 (for APC dispatching) + xboxkrnl::KINTERRUPT SoftwareInterrupt_1; + SoftwareInterrupt_1.BusInterruptLevel = 1; + SoftwareInterrupt_1.ServiceRoutine = ApcInterrupt; + xboxkrnl::KeConnectInterrupt(&SoftwareInterrupt_1); + + // Init software interrupt 2 (for DPC dispatching) + xboxkrnl::KINTERRUPT SoftwareInterrupt_2; + SoftwareInterrupt_2.BusInterruptLevel = 2; + SoftwareInterrupt_2.ServiceRoutine = DispatchInterrupt; + xboxkrnl::KeConnectInterrupt(&SoftwareInterrupt_2); +} +#endif + +void TriggerPendingConnectedInterrupts() +{ + for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) { + // If the interrupt is pending and connected, process it + if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) { + HalSystemInterrupts[i].Trigger(EmuInterruptList[i]); + } + SwitchToThread(); + } +} + +static unsigned int WINAPI CxbxKrnlInterruptThread(PVOID param) +{ + CxbxSetThreadName("CxbxKrnl Interrupts"); + + // Make sure Xbox1 code runs on one core : + InitXboxThread(g_CPUXbox); + +#if 0 + InitSoftwareInterrupts(); +#endif + + while (true) { + if (g_bEnableAllInterrupts) { + TriggerPendingConnectedInterrupts(); + } + Sleep(1); + } + + return 0; +} + +static void CxbxKrnlClockThread(void* pVoid) +{ + LARGE_INTEGER CurrentTicks; + uint64_t Delta; + uint64_t Microseconds; + unsigned int IncrementScaling; + static uint64_t LastTicks = 0; + static uint64_t Error = 0; + static uint64_t UnaccountedMicroseconds = 0; + + // This keeps track of how many us have elapsed between two cycles, so that the xbox clocks are updated + // with the proper increment (instead of blindly adding a single increment at every step) + + if (LastTicks == 0) { + QueryPerformanceCounter(&CurrentTicks); + LastTicks = CurrentTicks.QuadPart; + CurrentTicks.QuadPart = 0; + } + + QueryPerformanceCounter(&CurrentTicks); + Delta = CurrentTicks.QuadPart - LastTicks; + LastTicks = CurrentTicks.QuadPart; + + Error += (Delta * SCALE_S_IN_US); + Microseconds = Error / HostClockFrequency; + Error -= (Microseconds * HostClockFrequency); + + UnaccountedMicroseconds += Microseconds; + IncrementScaling = (unsigned int)(UnaccountedMicroseconds / 1000); // -> 1 ms = 1000us -> time between two xbox clock interrupts + UnaccountedMicroseconds -= (IncrementScaling * 1000); + + xboxkrnl::KiClockIsr(IncrementScaling); +} + +std::vector g_RdtscPatches; + +#define OPCODE_PATCH_RDTSC 0x90EF // OUT DX, EAX; NOP + +bool IsRdtscInstruction(xbaddr addr) +{ + // First the fastest check - does addr contain exact patch from PatchRdtsc? + // Second check - is addr on the rdtsc patch list? + return (*(uint16_t*)addr == OPCODE_PATCH_RDTSC) + // Note : It's not needed to check for g_SkipRdtscPatching, + // as when that's set, the g_RdtscPatches vector will be empty + // anyway, failing this lookup : + && (std::find(g_RdtscPatches.begin(), g_RdtscPatches.end(), addr) != g_RdtscPatches.end()); +} + +void PatchRdtsc(xbaddr addr) +{ + // Patch away rdtsc with an opcode we can intercept + // We use a privilaged instruction rather than int 3 for debugging + // When using int 3, attached debuggers trap and rdtsc is used often enough + // that it makes Cxbx-Reloaded unusable + // A privilaged instruction (like OUT) does not suffer from this + EmuLogInit(LOG_LEVEL::DEBUG, "Patching rdtsc opcode at 0x%.8X", (DWORD)addr); + *(uint16_t*)addr = OPCODE_PATCH_RDTSC; + g_RdtscPatches.push_back(addr); +} + +const uint8_t rdtsc_pattern[] = { + 0x89,//{ 0x0F,0x31,0x89 }, + 0xC3,//{ 0x0F,0x31,0xC3 }, + 0x8B,//{ 0x0F,0x31,0x8B }, //one false positive in Sonic Rider .text 88 5C 0F 31 + 0xB9,//{ 0x0F,0x31,0xB9 }, + 0xC7,//{ 0x0F,0x31,0xC7 }, + 0x8D,//{ 0x0F,0x31,0x8D }, + 0x68,//{ 0x0F,0x31,0x68 }, + 0x5A,//{ 0x0F,0x31,0x5A }, + 0x29,//{ 0x0F,0x31,0x29 }, + 0xF3,//{ 0x0F,0x31,0xF3 }, + 0xE9,//{ 0x0F,0x31,0xE9 }, + 0x2B,//{ 0x0F,0x31,0x2B }, + 0x50,//{ 0x0F,0x31,0x50 }, // 0x50 only used in ExaSkeleton .text , but encounter false positive in RalliSport .text 83 E2 0F 31 + 0x0F,//{ 0x0F,0x31,0x0F }, + 0x3B,//{ 0x0F,0x31,0x3B }, + 0xD9,//{ 0x0F,0x31,0xD9 }, + 0x57,//{ 0x0F,0x31,0x57 }, + 0xB9,//{ 0x0F,0x31,0xB9 }, + 0x85,//{ 0x0F,0x31,0x85 }, + 0x83,//{ 0x0F,0x31,0x83 }, + 0x33,//{ 0x0F,0x31,0x33 }, + 0xF7,//{ 0x0F,0x31,0xF7 }, + 0x8A,//{ 0x0F,0x31,0x8A }, // 8A and 56 only apears in RalliSport 2 .text , need to watch whether any future false positive. + 0x56,//{ 0x0F,0x31,0x56 } + 0x6A, // 6A, 39, EB, F6, A1, 01 only appear in Unreal Championship, 01 is at WMVDEC section + 0x39, + 0xEB, + 0xF6, + 0xA1, + 0x01 +}; +const int sizeof_rdtsc_pattern = sizeof(rdtsc_pattern); + +void PatchRdtscInstructions() +{ + uint8_t rdtsc[2] = { 0x0F, 0x31 }; + DWORD sizeOfImage = CxbxKrnl_XbeHeader->dwSizeofImage; + + // Iterate through each CODE section + for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) { + if (!CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwFlags.bExecutable) { + continue; + } + + // Skip some segments known to never contain rdtsc (to avoid false positives) + if (std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "DSOUND" + || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "XGRPH" + || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == ".data" + || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == ".rdata" + || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "XMV" + || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "XONLINE" + || std::string(CxbxKrnl_Xbe->m_szSectionName[sectionIndex]) == "MDLPL") { + continue; + } + + EmuLogInit(LOG_LEVEL::INFO, "Searching for rdtsc in section %s", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); + xbaddr startAddr = CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwVirtualAddr; + //rdtsc is two bytes instruction, it needs at least one opcode byte after it to finish a function, so the endAddr need to substract 3 bytes. + xbaddr endAddr = startAddr + CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwSizeofRaw-3; + for (xbaddr addr = startAddr; addr <= endAddr; addr++) + { + if (memcmp((void*)addr, rdtsc, 2) == 0) + { + uint8_t next_byte = *(uint8_t*)(addr + 2); + // If the following byte matches the known pattern. + int i = 0; + for (i = 0; i= sizeof_rdtsc_pattern) + { + //no pattern matched, keep record for detections we treat as non-rdtsc for future debugging. + EmuLogInit(LOG_LEVEL::INFO, "Skipped potential rdtsc: Unknown opcode pattern 0x%.2X, @ 0x%.8X", next_byte, (DWORD)addr); + } + } + } + } + + EmuLogInit(LOG_LEVEL::INFO, "Done patching rdtsc, total %d rdtsc instructions patched", g_RdtscPatches.size()); +} + +void MapThunkTable(uint32_t* kt, uint32_t* pThunkTable) +{ + const bool SendDebugReports = (pThunkTable == CxbxKrnl_KernelThunkTable) && CxbxDebugger::CanReport(); + + uint32_t* kt_tbl = (uint32_t*)kt; + int i = 0; + while (kt_tbl[i] != 0) { + int t = kt_tbl[i] & 0x7FFFFFFF; + kt_tbl[i] = pThunkTable[t]; + if (SendDebugReports) { + // TODO: Update CxbxKrnl_KernelThunkTable to include symbol names + std::string importName = "KernelImport_" + std::to_string(t); + CxbxDebugger::ReportKernelPatch(importName.c_str(), kt_tbl[i]); + } + i++; + } +} + +typedef struct { + xbaddr ThunkAddr; + xbaddr LibNameAddr; +} XbeImportEntry; + +void ImportLibraries(XbeImportEntry *pImportDirectory) +{ + // assert(pImportDirectory); + + while (pImportDirectory->LibNameAddr && pImportDirectory->ThunkAddr) { + std::wstring LibName = std::wstring((wchar_t*)pImportDirectory->LibNameAddr); + + if (LibName == L"xbdm.dll") { + MapThunkTable((uint32_t *)pImportDirectory->ThunkAddr, Cxbx_LibXbdmThunkTable); + } + else { + // TODO: replace wprintf to EmuLogInit, how? + wprintf(L"LOAD : Skipping unrecognized import library : %s\n", LibName.c_str()); + } + + pImportDirectory++; + } +} + +bool CreateSettings() +{ + g_Settings = new Settings(); + if (g_Settings == nullptr) { + PopupError(nullptr, szSettings_alloc_error); + return false; + } + + if (!g_Settings->Init()) { + return false; + } + + log_get_settings(); + return true; +} + +bool HandleFirstLaunch() +{ + bool bFirstLaunch; + g_EmuShared->GetIsFirstLaunch(&bFirstLaunch); + + /* check if process is launch with elevated access then prompt for continue on or not. */ + if (!bFirstLaunch) { + if (!CreateSettings()) { + return false; + } + + bool bElevated = CxbxIsElevated(); + if (bElevated && !g_Settings->m_core.allowAdminPrivilege) { + PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No, + "Cxbx-Reloaded has detected that it has been launched with Administrator rights.\n" + "\nThis is dangerous, as a maliciously modified Xbox titles could take control of your system.\n" + "\nAre you sure you want to continue?"); + if (ret != PopupReturn::Yes) { + return false; + } + } + + g_EmuShared->SetIsFirstLaunch(true); + } + + return true; +} + +void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved) +{ + std::string tempStr; + + // NOTE: This is designated for standalone kernel mode launch without GUI + if (g_Settings != nullptr) { + + // Reset to default + g_EmuShared->Reset(); + + g_Settings->Verify(); + g_Settings->SyncToEmulator(); + + // We don't need to keep Settings open plus allow emulator to use unused memory. + delete g_Settings; + g_Settings = nullptr; + + // Perform identical to what GUI will do to certain EmuShared's variable before launch. + g_EmuShared->SetIsEmulating(true); + + // NOTE: This setting the ready status is optional. Internal kernel process is checking if GUI is running. + // Except if enforce check, then we need to re-set ready status every time for non-GUI. + //g_EmuShared->SetIsReady(true); + } + + /* Initialize popup message management from kernel side. */ + log_init_popup_msg(); + + /* Initialize Cxbx File Paths */ + CxbxInitFilePaths(); + + // Skip '/load' switch + // Get XBE Name : + std::string xbePath; + cli_config::GetValue(cli_config::load, &xbePath); + xbePath = std::filesystem::absolute(std::filesystem::path(xbePath)).string(); + + // Get DCHandle : + // We must save this handle now to keep the child window working in the case we need to display the UEM + HWND hWnd = nullptr; + if (cli_config::GetValue(cli_config::hwnd, &tempStr)) { + hWnd = (HWND)std::atoi(tempStr.c_str()); + } + CxbxKrnl_hEmuParent = IsWindow(hWnd) ? hWnd : nullptr; + + // Get KernelDebugMode : + DebugMode DbgMode = DebugMode::DM_NONE; + if (cli_config::GetValue(cli_config::debug_mode, &tempStr)) { + DbgMode = (DebugMode)std::atoi(tempStr.c_str()); + } + + // Get KernelDebugFileName : + std::string DebugFileName = ""; + if (cli_config::GetValue(cli_config::debug_file, &tempStr)) { + DebugFileName = tempStr; + } + + int BootFlags; + g_EmuShared->GetBootFlags(&BootFlags); + + g_CurrentProcessHandle = GetCurrentProcess(); // OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); + + // Set up the logging variables for the kernel process during initialization. + log_sync_config(); + + // When a reboot occur, we need to keep persistent memory buffer open before emulation process shutdown. + if ((BootFlags & BOOT_QUICK_REBOOT) != 0) { + g_VMManager.GetPersistentMemory(); + } + + if (CxbxKrnl_hEmuParent != NULL) { + ipc_send_gui_update(IPC_UPDATE_GUI::KRNL_IS_READY, static_cast(GetCurrentProcessId())); + + // Force wait until GUI process is ready + do { + int waitCounter = 10; + bool isReady = false; + + while (waitCounter > 0) { + g_EmuShared->GetIsReady(&isReady); + if (isReady) { + break; + } + waitCounter--; + Sleep(100); + } + if (!isReady) { + EmuLog(LOG_LEVEL::WARNING, "GUI process is not ready!"); + PopupReturn mbRet = PopupWarningEx(nullptr, PopupButtons::RetryCancel, PopupReturn::Cancel, + "GUI process is not ready, do you wish to retry?"); + if (mbRet == PopupReturn::Retry) { + continue; + } + CxbxKrnlShutDown(); + } + break; + } while (true); + } + + g_EmuShared->SetIsReady(false); + + UINT prevKrnlProcID = 0; + DWORD dwExitCode = EXIT_SUCCESS; + g_EmuShared->GetKrnlProcID(&prevKrnlProcID); + + // Save current kernel proccess id for next reboot if will occur in the future. + // And to tell previous kernel process we had take over. This allow reboot's shared memory buffer to survive. + g_EmuShared->SetKrnlProcID(GetCurrentProcessId()); + + // Force wait until previous kernel process is closed. + if (prevKrnlProcID != 0) { + HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, prevKrnlProcID); + // If we do receive valid handle, let's do the next step. + if (hProcess != NULL) { + + WaitForSingleObject(hProcess, INFINITE); + + GetExitCodeProcess(hProcess, &dwExitCode); + CloseHandle(hProcess); + } + } + + if (dwExitCode != EXIT_SUCCESS) {// Stop emulation + CxbxKrnlShutDown(); + } + + /* Must be called after CxbxInitFilePaths and previous kernel process shutdown. */ + if (!CxbxLockFilePath()) { + return; + } + + FILE* krnlLog = nullptr; + // debug console allocation (if configured) + if (DbgMode == DM_CONSOLE) + { + if (AllocConsole()) + { + HANDLE StdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + // Maximise the console scroll buffer height : + CONSOLE_SCREEN_BUFFER_INFO coninfo; + GetConsoleScreenBufferInfo(StdHandle, &coninfo); + coninfo.dwSize.Y = SHRT_MAX - 1; // = 32767-1 = 32766 = maximum value that works + SetConsoleScreenBufferSize(StdHandle, coninfo.dwSize); + (void)freopen("CONOUT$", "wt", stdout); + (void)freopen("CONIN$", "rt", stdin); + SetConsoleTitle("Cxbx-Reloaded : Kernel Debug Console"); + SetConsoleTextAttribute(StdHandle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); + } + } + else + { + FreeConsole(); + if (DbgMode == DM_FILE) { + // Peform clean write to kernel log for first boot. Unless multi-xbe boot occur then perform append to existing log. + krnlLog = freopen(DebugFileName.c_str(), ((BootFlags == DebugMode::DM_NONE) ? "wt" : "at"), stdout); + // Append separator for better readability after reboot. + if (BootFlags != DebugMode::DM_NONE) { + std::cout << "\n------REBOOT------REBOOT------REBOOT------REBOOT------REBOOT------\n" << std::endl; + } + } + else { + char buffer[16]; + if (GetConsoleTitle(buffer, 16) != NULL) + (void)freopen("nul", "w", stdout); + } + } + + bool isLogEnabled; + g_EmuShared->GetIsKrnlLogEnabled(&isLogEnabled); + g_bPrintfOn = isLogEnabled; + + g_EmuShared->ResetKrnl(); + + // Write a header to the log + { + EmuLogInit(LOG_LEVEL::INFO, "Cxbx-Reloaded Version %s", CxbxVersionStr); + + time_t startTime = time(nullptr); + struct tm* tm_info = localtime(&startTime); + char timeString[26]; + strftime(timeString, 26, "%F %T", tm_info); + EmuLogInit(LOG_LEVEL::INFO, "Log started at %s", timeString); + +#ifdef _DEBUG_TRACE + EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Enabled."); +#else + EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Disabled."); +#endif + } + + // Log once, since multi-xbe boot is appending to file instead of overwrite. + if (BootFlags == BOOT_NONE) { + log_generate_active_filter_output(CXBXR_MODULE::INIT); + } + + // Detect Wine + g_bIsWine = false; + HMODULE hNtDll = GetModuleHandle("ntdll.dll"); + + if (hNtDll != nullptr) { + wine_get_version = (LPFN_WINEGETVERSION)GetProcAddress(hNtDll, "wine_get_version"); + if (wine_get_version) { + g_bIsWine = true; + } + } + + // Now we got the arguments, start by initializing the Xbox memory map : + // PrepareXBoxMemoryMap() + { + // Our executable DOS image header must be loaded at 0x00010000 + // Assert(ExeDosHeader == XBE_IMAGE_BASE); + + // Determine EXE's header locations & size : + ExeNtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)ExeDosHeader + ExeDosHeader->e_lfanew); // = + 0x138 + ExeOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(ExeNtHeader->OptionalHeader); + + // verify base of code of our executable is 0x00001000 + if (ExeNtHeader->OptionalHeader.BaseOfCode != CXBX_BASE_OF_CODE) + { + PopupFatal(nullptr, "Cxbx-Reloaded executuable requires it's base of code to be 0x00001000"); + return; // TODO : Halt(0); + } + +#ifndef CXBXR_EMU + // verify virtual_memory_placeholder is located at 0x00011000 + if ((UINT_PTR)(&(virtual_memory_placeholder[0])) != (XBE_IMAGE_BASE + CXBX_BASE_OF_CODE)) + { + PopupFatal(nullptr, "virtual_memory_placeholder is not loaded to base address 0x00011000 (which is a requirement for Xbox emulation)"); + return; // TODO : Halt(0); + } +#endif + + // Create a safe copy of the complete EXE header: + DWORD ExeHeaderSize = ExeOptionalHeader->SizeOfHeaders; // Should end up as 0x400 + NewDosHeader = (PIMAGE_DOS_HEADER)VirtualAlloc(nullptr, ExeHeaderSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + memcpy(NewDosHeader, ExeDosHeader, ExeHeaderSize); + + // Determine NewOptionalHeader, required by RestoreExeImageHeader + NewNtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)NewDosHeader + ExeDosHeader->e_lfanew); + NewOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(NewNtHeader->OptionalHeader); + + // Make sure the new DOS header points to the new relative NtHeader location: + NewDosHeader->e_lfanew = (ULONG_PTR)NewNtHeader - XBE_IMAGE_BASE; + + // Note : NewOptionalHeader->ImageBase can stay at ExeOptionalHeader->ImageBase = 0x00010000 + + // Note : Since virtual_memory_placeholder prevents overlap between reserved xbox memory + // and Cxbx.exe sections, section headers don't have to be patched up. + + // Mark the virtual memory range completely accessible + DWORD OldProtection; + if (0 == VirtualProtect((void*)XBE_IMAGE_BASE, XBE_MAX_VA - XBE_IMAGE_BASE, PAGE_EXECUTE_READWRITE, &OldProtection)) { + DWORD err = GetLastError(); + + // Translate ErrorCode to String. + LPTSTR Error = 0; + if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + 0, + (LPTSTR)&Error, + 0, + NULL) == 0) { + // Failed in translating. + } + + // Free the buffer. + if (Error) { + ::LocalFree(Error); + Error = 0; + } + } + + // Clear out the virtual memory range + memset((void*)XBE_IMAGE_BASE, 0, XBE_MAX_VA - XBE_IMAGE_BASE); + + // Restore enough of the executable image headers to keep WinAPI's working : + RestoreExeImageHeader(); + } + + // Load Per-Xbe Keys from the Cxbx-Reloaded AppData directory + LoadXboxKeys(szFolder_CxbxReloadedData); + + EEPROM = CxbxRestoreEEPROM(szFilePath_EEPROM_bin); + if (EEPROM == nullptr) + { + PopupFatal(nullptr, "Couldn't init EEPROM!"); + return; // TODO : Halt(0); + } + + // TODO : Instead of loading an Xbe here, initialize the kernel so that it will launch the Xbe on itself. + // using XeLoadImage from LaunchDataPage->Header.szLaunchPath + + // Now we can load and run the XBE : + // MapAndRunXBE(XbePath, DCHandle); + XbeType xbeType = XbeType::xtRetail; + { + // NOTE: This is a safety to clean the file path for any malicious file path attempt. + // Might want to move this into a utility function. + size_t n, i; + // Remove useless slashes before and after semicolon. + std::string semicolon_search[] = { "\\;", ";\\", "/;", ";/" }; + std::string semicolon_str = ";"; + for (n = 0, i = 0; i < semicolon_search->size(); i++, n = 0) { + while ((n = xbePath.find(semicolon_search[i], n)) != std::string::npos) { + xbePath.replace(n, semicolon_search[i].size(), semicolon_str); + n += semicolon_str.size(); + } + } + // Remove extra slashes. + std::string slash_search[] = { "\\\\", "//" }; + std::string slash_str = "/"; + for (n = 0, i = 0; i < slash_search->size(); i++, n = 0) { + while ((n = xbePath.find(slash_search[i], n)) != std::string::npos) { + xbePath.replace(n, slash_search[i].size(), slash_str); + n += slash_str.size(); + } + } + + // Once clean up process is done, proceed set to global variable string. + strncpy(szFilePath_Xbe, xbePath.c_str(), MAX_PATH - 1); + std::replace(xbePath.begin(), xbePath.end(), ';', '/'); + // Load Xbe (this one will reside above WinMain's virtual_memory_placeholder) + CxbxKrnl_Xbe = new Xbe(xbePath.c_str(), false); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock() + + if (CxbxKrnl_Xbe->HasFatalError()) { + CxbxKrnlCleanup(CxbxKrnl_Xbe->GetError().c_str()); + return; + } + + // Check the signature of the xbe + if (CxbxKrnl_Xbe->CheckSignature()) { + EmuLogInit(LOG_LEVEL::INFO, "Valid xbe signature. Xbe is legit"); + } + else { + EmuLogInit(LOG_LEVEL::WARNING, "Invalid xbe signature. Homebrew, tampered or pirated xbe?"); + } + + // Check the integrity of the xbe sections + for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) { + if (CxbxKrnl_Xbe->CheckSectionIntegrity(sectionIndex)) { + EmuLogInit(LOG_LEVEL::INFO, "SHA hash check of section %s successful", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); + } + else { + EmuLogInit(LOG_LEVEL::WARNING, "SHA hash of section %s doesn't match, section is corrupted", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); + } + } + + // If CLI has given console type, then enforce it. + if (cli_config::hasKey(cli_config::system_chihiro)) { + EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as chihiro."); + xbeType = XbeType::xtChihiro; + } + else if (cli_config::hasKey(cli_config::system_devkit)) { + EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as devkit."); + xbeType = XbeType::xtDebug; + } + else if (cli_config::hasKey(cli_config::system_retail)) { + EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as retail."); + xbeType = XbeType::xtRetail; + } + // Otherwise, use auto detect method. + else { + // Detect XBE type : + xbeType = CxbxKrnl_Xbe->GetXbeType(); + EmuLogInit(LOG_LEVEL::INFO, "Auto detect: XbeType = %s", GetXbeTypeToStr(xbeType)); + } + + EmuLogInit(LOG_LEVEL::INFO, "Host's compatible system types: %2X", reserved_systems); + unsigned int emulate_system = 0; + // Set reserved_systems which system we will about to emulate. + if (isSystemFlagSupport(reserved_systems, SYSTEM_CHIHIRO) && xbeType == XbeType::xtChihiro) { + emulate_system = SYSTEM_CHIHIRO; + } + else if (isSystemFlagSupport(reserved_systems, SYSTEM_DEVKIT) && xbeType == XbeType::xtDebug) { + emulate_system = SYSTEM_DEVKIT; + } + else if (isSystemFlagSupport(reserved_systems, SYSTEM_XBOX) && xbeType == XbeType::xtRetail) { + emulate_system = SYSTEM_XBOX; + } + // If none of system type requested to emulate isn't supported on host's end. Then enforce failure. + else { + CxbxKrnlCleanup("Unable to emulate system type due to host is not able to reserve required memory ranges."); + return; + } + // Clear emulation system from reserved systems to be free. + reserved_systems &= ~emulate_system; + + // Once we have determine which system type to run as, enforce it in future reboots. + if ((BootFlags & BOOT_QUICK_REBOOT) == 0) { + const char* system_str = GetSystemTypeToStr(emulate_system); + cli_config::SetSystemType(system_str); + } + + // Register if we're running an Chihiro executable or a debug xbe, otherwise it's an Xbox retail executable + g_bIsChihiro = (xbeType == XbeType::xtChihiro); + g_bIsDebug = (xbeType == XbeType::xtDebug); + g_bIsRetail = (xbeType == XbeType::xtRetail); + + // Disabled: The media board rom fails to run because it REQUIRES LLE USB, which is not yet enabled. + // Chihiro games can be ran directly for now. + // This just means that you cannot access the Chihiro test menus and related stuff, games should still be okay +#if 0 + // If the Xbe is Chihiro, and we were not launched by SEGABOOT, we need to load SEGABOOT from the Chihiro Media Board rom instead! + // TODO: We also need to store the path of the loaded game, and mount it as the mediaboard filesystem + // TODO: How to we detect who launched us, to prevent a reboot-loop + if (g_bIsChihiro) { + std::string chihiroMediaBoardRom = std::string(szFolder_CxbxReloadedData) + std::string("/EmuDisk/") + MediaBoardRomFile; + if (!std::filesystem::exists(chihiroMediaBoardRom)) { + CxbxKrnlCleanup("Chihiro Media Board ROM (fpr21042_m29w160et.bin) could not be found"); + } + + delete CxbxKrnl_Xbe; + CxbxKrnl_Xbe = new Xbe(chihiroMediaBoardRom.c_str(), false); + } +#endif + +#ifndef CXBXR_EMU + // Only for GUI executable with emulation code. + blocks_reserved_t blocks_reserved_gui = { 0 }; + // Reserve console system's memory ranges before start initialize. + if (!ReserveAddressRanges(emulate_system, blocks_reserved_gui)) { + CxbxKrnlCleanup("Failed to reserve required memory ranges!", GetLastError()); + } + // Initialize the memory manager + g_VMManager.Initialize(emulate_system, BootFlags, blocks_reserved_gui); +#else + // Release unnecessary memory ranges to allow console/host to use those memory ranges. + FreeAddressRanges(emulate_system, reserved_systems, blocks_reserved); + // Initialize the memory manager + g_VMManager.Initialize(emulate_system, BootFlags, blocks_reserved); +#endif + + // Commit the memory used by the xbe header + size_t HeaderSize = CxbxKrnl_Xbe->m_Header.dwSizeofHeaders; + VAddr XbeBase = XBE_IMAGE_BASE; + g_VMManager.XbAllocateVirtualMemory(&XbeBase, 0, &HeaderSize, XBOX_MEM_COMMIT, XBOX_PAGE_READWRITE); + + + // Copy over loaded Xbe Headers to specified base address + memcpy((void*)CxbxKrnl_Xbe->m_Header.dwBaseAddr, &CxbxKrnl_Xbe->m_Header, sizeof(Xbe::Header)); + memcpy((void*)(CxbxKrnl_Xbe->m_Header.dwBaseAddr + sizeof(Xbe::Header)), CxbxKrnl_Xbe->m_HeaderEx, CxbxKrnl_Xbe->m_ExSize); + + // Load all sections marked as preload using the in-memory copy of the xbe header + xboxkrnl::PXBEIMAGE_SECTION sectionHeaders = (xboxkrnl::PXBEIMAGE_SECTION)CxbxKrnl_Xbe->m_Header.dwSectionHeadersAddr; + for (uint32_t i = 0; i < CxbxKrnl_Xbe->m_Header.dwSections; i++) { + if ((sectionHeaders[i].Flags & XBEIMAGE_SECTION_PRELOAD) != 0) { + NTSTATUS result = xboxkrnl::XeLoadSection(§ionHeaders[i]); + if (FAILED(result)) { + EmuLogInit(LOG_LEVEL::WARNING, "Failed to preload XBE section: %s", CxbxKrnl_Xbe->m_szSectionName[i]); + } + } + } + + // We need to remember a few XbeHeader fields, so we can switch between a valid ExeHeader and XbeHeader : + StoreXbeImageHeader(); + + // Restore enough of the executable image headers to keep WinAPI's working : + RestoreExeImageHeader(); + + // HACK: Attempt to patch out XBE header reads + // This works by searching for the XBEH signature and replacing it with what appears in host address space instead + // Test case: Half Life 2 + // Iterate through each CODE section + for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) { + if (!CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwFlags.bExecutable) { + continue; + } + + EmuLogInit(LOG_LEVEL::INFO, "Searching for XBEH in section %s", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]); + xbaddr startAddr = CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwVirtualAddr; + xbaddr endAddr = startAddr + CxbxKrnl_Xbe->m_SectionHeader[sectionIndex].dwSizeofRaw; + for (xbaddr addr = startAddr; addr < endAddr; addr++) { + if (*(uint32_t*)addr == 0x48454258) { + EmuLogInit(LOG_LEVEL::INFO, "Patching XBEH at 0x%08X", addr); + *((uint32_t*)addr) = *(uint32_t*)XBE_IMAGE_BASE; + } + } + } + } + + // Decode kernel thunk table address : + uint32_t kt = CxbxKrnl_Xbe->m_Header.dwKernelImageThunkAddr; + kt ^= XOR_KT_KEY[to_underlying(xbeType)]; + + // Process the Kernel thunk table to map Kernel function calls to their actual address : + MapThunkTable((uint32_t*)kt, CxbxKrnl_KernelThunkTable); + + // Does this xbe import any other libraries? + if (CxbxKrnl_Xbe->m_Header.dwNonKernelImportDirAddr) { + ImportLibraries((XbeImportEntry*)CxbxKrnl_Xbe->m_Header.dwNonKernelImportDirAddr); + } + + g_ExceptionManager = new ExceptionManager(); // If in need to add VEHs, move this line earlier. (just in case) + + // Launch the XBE : + { + // Load TLS + Xbe::TLS* XbeTls = (Xbe::TLS*)CxbxKrnl_Xbe->m_Header.dwTLSAddr; + void* XbeTlsData = (XbeTls != nullptr) ? (void*)CxbxKrnl_Xbe->m_TLS->dwDataStartAddr : nullptr; + // Decode Entry Point + xbaddr EntryPoint = CxbxKrnl_Xbe->m_Header.dwEntryAddr; + EntryPoint ^= XOR_EP_KEY[to_underlying(xbeType)]; + // Launch XBE + CxbxKrnlInit( + XbeTlsData, + XbeTls, + CxbxKrnl_Xbe->m_LibraryVersion, + DbgMode, + DebugFileName.c_str(), + (Xbe::Header*)CxbxKrnl_Xbe->m_Header.dwBaseAddr, + CxbxKrnl_Xbe->m_Header.dwSizeofHeaders, + (void(*)())EntryPoint, + BootFlags + ); + } + + if (!krnlLog) { + (void)fclose(krnlLog); + } +} +#pragma optimize("", on) + +// Loads a keys.bin file as generated by dump-xbox +// See https://github.com/JayFoxRox/xqemu-tools/blob/master/dump-xbox.c +void LoadXboxKeys(std::string path) +{ + std::string keys_path = path + "\\keys.bin"; + + // Attempt to open Keys.bin + FILE* fp = fopen(keys_path.c_str(), "rb"); + + if (fp != nullptr) { + // Determine size of Keys.bin + xboxkrnl::XBOX_KEY_DATA keys[2]; + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + rewind(fp); + + // If the size of Keys.bin is correct (two keys), read it + if (size == xboxkrnl::XBOX_KEY_LENGTH * 2) { + fread(keys, xboxkrnl::XBOX_KEY_LENGTH, 2, fp); + + memcpy(xboxkrnl::XboxEEPROMKey, &keys[0], xboxkrnl::XBOX_KEY_LENGTH); + memcpy(xboxkrnl::XboxCertificateKey, &keys[1], xboxkrnl::XBOX_KEY_LENGTH); + } + else { + EmuLog(LOG_LEVEL::WARNING, "Keys.bin has an incorrect filesize. Should be %d bytes", xboxkrnl::XBOX_KEY_LENGTH * 2); + } + + fclose(fp); + return; + } + + // If we didn't already exit the function, keys.bin could not be loaded + EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox"); +} + +__declspec(noreturn) void CxbxKrnlInit +( + void *pTLSData, + Xbe::TLS *pTLS, + Xbe::LibraryVersion *pLibraryVersion, + DebugMode DbgMode, + const char *szDebugFilename, + Xbe::Header *pXbeHeader, + uint32_t dwXbeHeaderSize, + void(*Entry)(), + int BootFlags) +{ + // Set windows timer period to 1ms + // Windows will automatically restore this value back to original on program exit + // But with this, we can replace some busy loops with sleeps. + timeBeginPeriod(1); + + xboxkrnl::InitializeFscCacheEvent(); + + // update caches + CxbxKrnl_TLS = pTLS; + CxbxKrnl_TLSData = pTLSData; + CxbxKrnl_XbeHeader = pXbeHeader; + CxbxKrnl_DebugMode = DbgMode; + CxbxKrnl_DebugFileName = (char*)szDebugFilename; + + // A patch to dwCertificateAddr is a requirement due to Windows TLS is overwriting dwGameRegion data address. + // By using unalternated certificate data, it should no longer cause any problem with titles running and Cxbx's log as well. + CxbxKrnl_XbeHeader->dwCertificateAddr = (uint32_t)&CxbxKrnl_Xbe->m_Certificate; + g_pCertificate = &CxbxKrnl_Xbe->m_Certificate; + + // Initialize timer subsystem + Timer_Init(); + // for unicode conversions + setlocale(LC_ALL, "English"); + // Initialize time-related variables for the kernel and the timers + CxbxInitPerformanceCounters(); +#ifdef _DEBUG +// PopupCustom(LOG_LEVEL::INFO, "Attach a Debugger"); +// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool +#endif + + // debug trace + { +#ifdef _DEBUG_TRACE + EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Enabled."); + EmuLogInit(LOG_LEVEL::INFO, "CxbxKrnlInit\n" + "(\n" + " hwndParent : 0x%.08p\n" + " pTLSData : 0x%.08p\n" + " pTLS : 0x%.08p\n" + " pLibraryVersion : 0x%.08p\n" + " DebugConsole : 0x%.08X\n" + " DebugFilename : \"%s\"\n" + " pXBEHeader : 0x%.08p\n" + " dwXBEHeaderSize : 0x%.08X\n" + " Entry : 0x%.08p\n" + ");", + CxbxKrnl_hEmuParent, pTLSData, pTLS, pLibraryVersion, DbgMode, szDebugFilename, pXbeHeader, dwXbeHeaderSize, Entry); +#else + EmuLogInit(LOG_LEVEL::INFO, "Debug Trace Disabled."); +#endif + } + +#ifdef _DEBUG_TRACE + // VerifyHLEDataBase(); +#endif + // TODO : The following seems to cause a crash when booting the game "Forza Motorsport", + // according to https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/101#issuecomment-277230140 + { + // Create a fake kernel header for XapiRestrictCodeSelectorLimit + // Thanks advancingdragon / DirtBox + PDUMMY_KERNEL DummyKernel = (PDUMMY_KERNEL)XBOX_KERNEL_BASE; + memset(DummyKernel, 0, sizeof(DUMMY_KERNEL)); + + // XapiRestrictCodeSelectorLimit only checks these fields. + DummyKernel->DosHeader.e_lfanew = sizeof(IMAGE_DOS_HEADER); // RVA of NtHeaders + DummyKernel->FileHeader.SizeOfOptionalHeader = 0; + DummyKernel->FileHeader.NumberOfSections = 1; + // as long as this doesn't start with "INIT" + strncpy_s((PSTR)DummyKernel->SectionHeader.Name, 8, "DONGS", 8); + EmuLogInit(LOG_LEVEL::INFO, "Initialized dummy kernel image header."); + } + + // Read which components need to be LLE'ed per user request + { + unsigned int CxbxLLE_Flags; + g_EmuShared->GetFlagsLLE(&CxbxLLE_Flags); + bLLE_APU = (CxbxLLE_Flags & LLE_APU) > 0; + bLLE_GPU = (CxbxLLE_Flags & LLE_GPU) > 0; + //bLLE_USB = (CxbxLLE_Flags & LLE_USB) > 0; // Reenable this when LLE USB actually works + bLLE_JIT = (CxbxLLE_Flags & LLE_JIT) > 0; + } + + // Process Hacks + { + int HackEnabled = 0; + g_EmuShared->GetDisablePixelShaders(&HackEnabled); + g_DisablePixelShaders = !!HackEnabled; + g_EmuShared->GetUseAllCores(&HackEnabled); + g_UseAllCores = !!HackEnabled; + g_EmuShared->GetSkipRdtscPatching(&HackEnabled); + g_SkipRdtscPatching = !!HackEnabled; + } + +#ifdef _DEBUG_PRINT_CURRENT_CONF + PrintCurrentConfigurationLog(); +#endif + + // Initialize devices : + char szBuffer[sizeof(szFilePath_Xbe)]; + g_EmuShared->GetStorageLocation(szBuffer); + + CxbxBasePath = std::string(szBuffer) + "\\EmuDisk\\"; + + // Determine XBE Path + strncpy(szBuffer, szFilePath_Xbe, sizeof(szBuffer)-1); + szBuffer[sizeof(szBuffer) - 1] = '\0'; // Safely null terminate at the end. + + std::string xbePath(szBuffer); + std::replace(xbePath.begin(), xbePath.end(), ';', '/'); + std::string xbeDirectory(szBuffer); + size_t lastFind = xbeDirectory.find(';'); + // First find if there is a semicolon when dashboard or title disc (such as demo disc) has it. + // Then we must obey the current directory it asked for. + if (lastFind != std::string::npos) { + if (xbeDirectory.find(';', lastFind + 1) != std::string::npos) { + CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "Cannot contain multiple of ; symbol."); + } + xbeDirectory = xbeDirectory.substr(0, lastFind); + } + else { + xbeDirectory = xbeDirectory.substr(0, xbeDirectory.find_last_of("\\/")); + } + CxbxBasePathHandle = CreateFile(CxbxBasePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + memset(szBuffer, 0, sizeof(szBuffer)); + // Games may assume they are running from CdRom : + CxbxDefaultXbeDriveIndex = CxbxRegisterDeviceHostPath(DeviceCdrom0, xbeDirectory); + // Partition 0 contains configuration data, and is accessed as a native file, instead as a folder : + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition0, CxbxBasePath + "Partition0", /*IsFile=*/true); + // The first two partitions are for Data and Shell files, respectively : + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition1, CxbxBasePath + "Partition1"); + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition2, CxbxBasePath + "Partition2"); + // The following partitions are for caching purposes - for now we allocate up to 7 (as xbmp needs that many) : + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition3, CxbxBasePath + "Partition3"); + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition4, CxbxBasePath + "Partition4"); + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition5, CxbxBasePath + "Partition5"); + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition6, CxbxBasePath + "Partition6"); + CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition7, CxbxBasePath + "Partition7"); + + // Create default symbolic links : + EmuLogInit(LOG_LEVEL::DEBUG, "Creating default symbolic links."); + { + // TODO: DriveD should always point to the Xbe Path + // This is the only symbolic link the Xbox Kernel sets, the rest are set by the application, usually via XAPI. + // If the Xbe is located outside of the emulated HDD, mounting it as DeviceCdrom0 is correct + // If the Xbe is located inside the emulated HDD, the full path should be used, eg: "\\Harddisk0\\partition2\\xboxdash.xbe" + CxbxCreateSymbolicLink(DriveD, DeviceCdrom0); + // Arrange that the Xbe path can reside outside the partitions, and put it to g_hCurDir : + EmuNtSymbolicLinkObject* xbePathSymbolicLinkObject = FindNtSymbolicLinkObjectByDriveLetter(CxbxDefaultXbeDriveLetter); + g_hCurDir = xbePathSymbolicLinkObject->RootDirectoryHandle; + } + + // Determine Xbox path to XBE and place it in XeImageFileName + { + std::string fileName(xbePath); + // Strip out the path, leaving only the XBE file name + // NOTE: we assume that the XBE is always on the root of the D: drive + // This is a safe assumption as the Xbox kernel ALWAYS mounts D: as the Xbe Path + if (fileName.rfind('\\') != std::string::npos) + fileName = fileName.substr(fileName.rfind('\\') + 1); + + if (xboxkrnl::XeImageFileName.Buffer != NULL) + free(xboxkrnl::XeImageFileName.Buffer); + + // Assign the running Xbe path, so it can be accessed via the kernel thunk 'XeImageFileName' : + xboxkrnl::XeImageFileName.MaximumLength = MAX_PATH; + xboxkrnl::XeImageFileName.Buffer = (PCHAR)g_VMManager.Allocate(MAX_PATH); + sprintf(xboxkrnl::XeImageFileName.Buffer, "%c:\\%s", CxbxDefaultXbeDriveLetter, fileName.c_str()); + xboxkrnl::XeImageFileName.Length = (USHORT)strlen(xboxkrnl::XeImageFileName.Buffer); + EmuLogInit(LOG_LEVEL::INFO, "XeImageFileName = %s", xboxkrnl::XeImageFileName.Buffer); + } + + // Dump Xbe information + { + if (CxbxKrnl_Xbe != nullptr) { + EmuLogInit(LOG_LEVEL::INFO, "Title : %s", CxbxKrnl_Xbe->m_szAsciiTitle); + } + + // Dump Xbe certificate + if (g_pCertificate != NULL) { + std::stringstream titleIdHex; + titleIdHex << std::hex << g_pCertificate->dwTitleId; + + EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID : %s", FormatTitleId(g_pCertificate->dwTitleId).c_str()); + EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID (Hex) : 0x%s", titleIdHex.str().c_str()); + EmuLogInit(LOG_LEVEL::INFO, "XBE Version : 1.%02d", g_pCertificate->dwVersion); + EmuLogInit(LOG_LEVEL::INFO, "XBE TitleName : %ls", g_pCertificate->wszTitleName); + EmuLogInit(LOG_LEVEL::INFO, "XBE Region : %s", CxbxKrnl_Xbe->GameRegionToString()); + } + + // Dump Xbe library build numbers + Xbe::LibraryVersion* libVersionInfo = pLibraryVersion;// (LibraryVersion *)(CxbxKrnl_XbeHeader->dwLibraryVersionsAddr); + if (libVersionInfo != NULL) { + for (uint32_t v = 0; v < CxbxKrnl_XbeHeader->dwLibraryVersions; v++) { + EmuLogInit(LOG_LEVEL::INFO, "XBE Library %u : %.8s (version %d)", v, libVersionInfo->szName, libVersionInfo->wBuildVersion); + libVersionInfo++; + } + } + } + + CxbxKrnlRegisterThread(GetCurrentThread()); + + // Make sure the Xbox1 code runs on one core (as the box itself has only 1 CPU, + // this will better aproximate the environment with regard to multi-threading) : + EmuLogInit(LOG_LEVEL::DEBUG, "Determining CPU affinity."); + { + if (!GetProcessAffinityMask(g_CurrentProcessHandle, &g_CPUXbox, &g_CPUOthers)) + CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "GetProcessAffinityMask failed."); + + // For the other threads, remove one bit from the processor mask: + g_CPUOthers = ((g_CPUXbox - 1) & g_CPUXbox); + + // Test if there are any other cores available : + if (g_CPUOthers > 0) { + // If so, make sure the Xbox threads run on the core NOT running Xbox code : + g_CPUXbox = g_CPUXbox & (~g_CPUOthers); + } else { + // Else the other threads must run on the same core as the Xbox code : + g_CPUOthers = g_CPUXbox; + } + } + + // initialize graphics + EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window."); + CxbxInitWindow(true); + + // Now process the boot flags to see if there are any special conditions to handle + if (BootFlags & BOOT_EJECT_PENDING) {} // TODO + if (BootFlags & BOOT_FATAL_ERROR) + { + // If we are here it means we have been rebooted to display the fatal error screen. The error code is set + // to 0x15 and the led flashes with the sequence green, red, red, red + + SetLEDSequence(0xE1); + CxbxKrnlPrintUEM(FATAL_ERROR_REBOOT_ROUTINE); // won't return + } + if (BootFlags & BOOT_SKIP_ANIMATION) {} // TODO + if (BootFlags & BOOT_RUN_DASHBOARD) {} // TODO + + CxbxInitAudio(); + + EmuHLEIntercept(pXbeHeader); + + if (!bLLE_USB) { + SetupXboxDeviceTypes(); + } + + InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable + + // Read Xbox video mode from the SMC, store it in HalBootSMCVideoMode + xboxkrnl::HalReadSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_AV_PACK, FALSE, &xboxkrnl::HalBootSMCVideoMode); + + g_InputDeviceManager.Initialize(false); + + // Now the hardware devices exist, couple the EEPROM buffer to it's device + g_EEPROM->SetEEPROM((uint8_t*)EEPROM); + + if (!bLLE_GPU) + { + EmuLogInit(LOG_LEVEL::DEBUG, "Initializing Direct3D."); + EmuD3DInit(); + } + + if (CxbxDebugger::CanReport()) + { + CxbxDebugger::ReportDebuggerInit(CxbxKrnl_Xbe->m_szAsciiTitle); + } + + // Apply Media Patches to bypass Anti-Piracy checks + // Required until we perfect emulation of X2 DVD Authentication + // See: https://multimedia.cx/eggs/xbox-sphinx-protocol/ + ApplyMediaPatches(); + + // Chihiro games require more patches + // The chihiro BIOS does this to bypass XAPI cache init + if (g_bIsChihiro) { + CxbxKrnl_XbeHeader->dwInitFlags.bDontSetupHarddisk = true; + } + + if(!g_SkipRdtscPatching) + { + PatchRdtscInstructions(); + } + + // Setup per-title encryption keys + SetupPerTitleKeys(); + + EmuInitFS(); + + InitXboxThread(g_CPUXbox); + xboxkrnl::ObInitSystem(); + xboxkrnl::KiInitSystem(); + + EmuX86_Init(); + // Create the interrupt processing thread + DWORD dwThreadId; + HANDLE hThread = (HANDLE)_beginthreadex(NULL, NULL, CxbxKrnlInterruptThread, NULL, NULL, (unsigned int*)&dwThreadId); + // Start the kernel clock thread + TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", &g_CPUOthers); + Timer_Start(KernelClockThr, SCALE_MS_IN_NS); + + EmuLogInit(LOG_LEVEL::DEBUG, "Calling XBE entry point..."); + CxbxLaunchXbe(Entry); + + // FIXME: Wait for Cxbx to exit or error fatally + Sleep(INFINITE); + + EmuLogInit(LOG_LEVEL::DEBUG, "XBE entry point returned"); + fflush(stdout); + + CxbxUnlockFilePath(); + + // EmuShared::Cleanup(); FIXME: commenting this line is a bad workaround for issue #617 (https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/617) + CxbxKrnlTerminateThread(); +} + +void CxbxInitFilePaths() +{ + if (g_Settings) { + std::string dataLoc = g_Settings->GetDataLocation(); + std::strncpy(szFolder_CxbxReloadedData, dataLoc.c_str(), dataLoc.length() + 1); + } + else { + g_EmuShared->GetStorageLocation(szFolder_CxbxReloadedData); + } + + // Make sure our data folder exists : + bool result = std::filesystem::exists(szFolder_CxbxReloadedData); + if (!result && !std::filesystem::create_directory(szFolder_CxbxReloadedData)) { + CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded's data folder!", __func__); + } + + // Make sure the EmuDisk folder exists + std::string emuDisk = std::string(szFolder_CxbxReloadedData) + std::string("\\EmuDisk"); + result = std::filesystem::exists(emuDisk); + if (!result && !std::filesystem::create_directory(emuDisk)) { + CxbxKrnlCleanup("%s : Couldn't create Cxbx-Reloaded EmuDisk folder!", __func__); + } + + snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", szFolder_CxbxReloadedData); + + GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH); +} + +HANDLE hMapDataHash = nullptr; + +bool CxbxLockFilePath() +{ + std::stringstream filePathHash("Local\\"); + uint64_t hashValue = XXH3_64bits(szFolder_CxbxReloadedData, strlen(szFolder_CxbxReloadedData) + 1); + if (!hashValue) { + CxbxKrnlCleanup("%s : Couldn't generate Cxbx-Reloaded's data folder hash!", __func__); + } + + filePathHash << std::hex << hashValue; + + hMapDataHash = CreateFileMapping + ( + INVALID_HANDLE_VALUE, // Paging file + nullptr, // default security attributes + PAGE_READONLY, // readonly access + 0, // size: high 32 bits + /*Dummy size*/4, // size: low 32 bits + filePathHash.str().c_str() // name of map object + ); + + if (hMapDataHash == nullptr) { + return false; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) { + PopupError(nullptr, "Data path directory is currently in used.\nUse different data path directory or stop emulation from another process."); + CloseHandle(hMapDataHash); + return false; + } + + return true; +} + +void CxbxUnlockFilePath() +{ + // Close opened file path lockdown shared memory. + if (hMapDataHash) { + CloseHandle(hMapDataHash); + hMapDataHash = nullptr; + } +} + +// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes +/*xboxkrnl::LAUNCH_DATA_PAGE DefaultLaunchDataPage = +{ + { // header + 2, // 2: dashboard, 0: title + 0, + "D:\\default.xbe", + 0 + } +};*/ + +__declspec(noreturn) void CxbxKrnlCleanupEx(CXBXR_MODULE cxbxr_module, const char *szErrorMessage, ...) +{ + g_bEmuException = true; + + CxbxKrnlResume(); + + // print out error message (if exists) + if(szErrorMessage != NULL) + { + char szBuffer2[1024]; + va_list argp; + + va_start(argp, szErrorMessage); + vsprintf(szBuffer2, szErrorMessage, argp); + va_end(argp); + + (void)PopupCustomEx(nullptr, cxbxr_module, LOG_LEVEL::FATAL, PopupIcon::Error, PopupButtons::Ok, PopupReturn::Ok, "Received Fatal Message:\n\n* %s\n", szBuffer2); // Will also EmuLogEx + } + + EmuLogInit(LOG_LEVEL::INFO, "MAIN: Terminating Process"); + fflush(stdout); + + // cleanup debug output + { + FreeConsole(); + + char buffer[16]; + + if(GetConsoleTitle(buffer, 16) != NULL) + freopen("nul", "w", stdout); + } + + CxbxKrnlShutDown(); +} + +void CxbxKrnlRegisterThread(HANDLE hThread) +{ + // we must duplicate this handle in order to retain Suspend/Resume thread rights from a remote thread + { + HANDLE hDupHandle = NULL; + + if (DuplicateHandle(g_CurrentProcessHandle, hThread, g_CurrentProcessHandle, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + hThread = hDupHandle; // Thread handle was duplicated, continue registration with the duplicate + } + else { + auto message = CxbxGetLastErrorString("DuplicateHandle"); + EmuLog(LOG_LEVEL::WARNING, message.c_str()); + } + } + + g_hThreads.push_back(hThread); +} + +void CxbxKrnlSuspend() +{ + if(g_bEmuSuspended || g_bEmuException) + return; + + for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it) + { + DWORD dwExitCode; + + if(GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) { + // suspend thread if it is active + SuspendThread(*it); + } else { + // remove thread from thread list if it is dead + g_hThreads.erase(it); + } + } + + // append 'paused' to rendering window caption text + { + char szBuffer[256]; + + HWND hWnd = GET_FRONT_WINDOW_HANDLE; + + GetWindowText(hWnd, szBuffer, 255 - 10); + + strcat(szBuffer, " (paused)"); + SetWindowText(hWnd, szBuffer); + } + + g_bEmuSuspended = true; +} + +void CxbxKrnlResume() +{ + if(!g_bEmuSuspended) + return; + + // remove 'paused' from rendering window caption text + { + char szBuffer[256]; + + HWND hWnd = GET_FRONT_WINDOW_HANDLE; + + GetWindowText(hWnd, szBuffer, 255); + + szBuffer[strlen(szBuffer)-9] = '\0'; + + SetWindowText(hWnd, szBuffer); + } + + for (auto it = g_hThreads.begin(); it != g_hThreads.end(); ++it) + { + DWORD dwExitCode; + + if (GetExitCodeThread(*it, &dwExitCode) && dwExitCode == STILL_ACTIVE) { + // resume thread if it is active + ResumeThread(*it); + } + else { + // remove thread from thread list if it is dead + g_hThreads.erase(it); + } + } + + g_bEmuSuspended = false; +} + +void CxbxKrnlShutDown() +{ + // Clear all kernel boot flags. These (together with the shared memory) persist until Cxbx-Reloaded is closed otherwise. + int BootFlags = 0; + g_EmuShared->SetBootFlags(&BootFlags); + + // NOTE: This causes a hang when exiting while NV2A is processing + // This is okay for now: It won't leak memory or resources since TerminateProcess will free everything + // delete g_NV2A; // TODO : g_pXbox + + // Shutdown the input device manager + g_InputDeviceManager.Shutdown(); + + // Shutdown the memory manager + g_VMManager.Shutdown(); + + CxbxUnlockFilePath(); + + if (CxbxKrnl_hEmuParent != NULL) { + SendMessage(CxbxKrnl_hEmuParent, WM_PARENTNOTIFY, WM_DESTROY, 0); + } + + EmuShared::Cleanup(); + + if (g_ExceptionManager) { + delete g_ExceptionManager; + g_ExceptionManager = nullptr; + } + + TerminateProcess(g_CurrentProcessHandle, 0); +} + +void CxbxKrnlPrintUEM(ULONG ErrorCode) +{ + ULONG Type; + xboxkrnl::XBOX_EEPROM Eeprom; + ULONG ResultSize; + + xboxkrnl::NTSTATUS status = xboxkrnl::ExQueryNonVolatileSetting(xboxkrnl::XC_MAX_ALL, &Type, &Eeprom, sizeof(Eeprom), &ResultSize); + + if (status == STATUS_SUCCESS) + { + xboxkrnl::XBOX_UEM_INFO* UEMInfo = (xboxkrnl::XBOX_UEM_INFO*)&(Eeprom.UEMInfo[0]); + + if (UEMInfo->ErrorCode == FATAL_ERROR_NONE) + { + // ergo720: the Xbox sets the error code and displays the UEM only for non-manufacturing xbe's (it power cycles + // otherwise). Considering that this flag can be easily tampered with in the xbe and the typical end user of cxbx + // can't fix the cause of the fatal error, I decided to always display it anyway. + + UEMInfo->ErrorCode = (UCHAR)ErrorCode; + UEMInfo->History |= (1 << (ErrorCode - 5)); + } + else { + UEMInfo->ErrorCode = FATAL_ERROR_NONE; + } + xboxkrnl::ExSaveNonVolatileSetting(xboxkrnl::XC_MAX_ALL, Type, &Eeprom, sizeof(Eeprom)); + } + else { + CxbxKrnlCleanup("Could not display the fatal error screen"); + } + + if (g_bIsChihiro) + { + // The Chihiro doesn't display the UEM + CxbxKrnlCleanup("The running Chihiro xbe has encountered a fatal error and needs to close"); + } + + g_CxbxFatalErrorCode = ErrorCode; + g_CxbxPrintUEM = true; // print the UEM + + CxbxPrintUEMInfo(ErrorCode); + + // Sleep forever to prevent continuing the initialization + Sleep(INFINITE); +} + +void CxbxPrintUEMInfo(ULONG ErrorCode) +{ + // See here for a description of the error codes and their meanings: + // https://www.reddit.com/r/originalxbox/wiki/error_codes + + std::map UEMErrorTable; + + UEMErrorTable.emplace(FATAL_ERROR_CORE_DIGITAL, "General motherboard issue"); + UEMErrorTable.emplace(FATAL_ERROR_BAD_EEPROM, "General EEPROM issue"); + UEMErrorTable.emplace(FATAL_ERROR_BAD_RAM, "RAM failure"); + UEMErrorTable.emplace(FATAL_ERROR_HDD_NOT_LOCKED, "HDD is not locked"); + UEMErrorTable.emplace(FATAL_ERROR_HDD_CANNOT_UNLOCK, "Unable to unlock HDD (bad password?)"); + UEMErrorTable.emplace(FATAL_ERROR_HDD_TIMEOUT, "HDD failed to respond"); + UEMErrorTable.emplace(FATAL_ERROR_HDD_NOT_FOUND, "Missing HDD"); + UEMErrorTable.emplace(FATAL_ERROR_HDD_BAD_CONFIG, "Invalid / missing HDD parameter(s)"); + UEMErrorTable.emplace(FATAL_ERROR_DVD_TIMEOUT, "DVD drive failed to respond"); + UEMErrorTable.emplace(FATAL_ERROR_DVD_NOT_FOUND, "Missing DVD drive"); + UEMErrorTable.emplace(FATAL_ERROR_DVD_BAD_CONFIG, "Invalid / missing DVD drive parameter(s)"); + UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_GENERIC, "Generic MS dashboard issue (dashboard not installed?)"); + UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_ERROR, "General MS dashboard issue"); + UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_SETTINGS, "MS dashboard issue: cannot reset console clock"); + UEMErrorTable.emplace(FATAL_ERROR_XBE_DASH_X2_PASS, "General MS dashboard issue, DVD drive authentication was successfull"); + UEMErrorTable.emplace(FATAL_ERROR_REBOOT_ROUTINE, "The console was instructed to reboot to this error screen"); + + auto it = UEMErrorTable.find(ErrorCode); + if (it != UEMErrorTable.end()) + { + std::string ErrorMessage = "Fatal error. " + it->second + ". This error screen will persist indefinitely. Stop the emulation to close it."; + PopupFatal(nullptr, ErrorMessage.c_str()); + } + else + { + PopupFatal(nullptr, "Unknown fatal error. This error screen will persist indefinitely. Stop the emulation to close it."); + } +} + +__declspec(noreturn) void CxbxKrnlTerminateThread() +{ + TerminateThread(GetCurrentThread(), 0); +} + +void CxbxKrnlPanic() +{ + CxbxKrnlCleanup("Kernel Panic!"); +} + +static clock_t g_DeltaTime = 0; // Used for benchmarking/fps count +static unsigned int g_Frames = 0; + +// ****************************************************************** +// * update the current milliseconds per frame +// ****************************************************************** +static void UpdateCurrentMSpFAndFPS() { + if (g_EmuShared) { + static float currentFPSVal = 30; + + currentFPSVal = (float)(g_Frames*0.5 + currentFPSVal * 0.5); + g_EmuShared->SetCurrentFPS(¤tFPSVal); + } +} + +void UpdateFPSCounter() +{ + static clock_t lastDrawFunctionCallTime = 0; + clock_t currentDrawFunctionCallTime = clock(); + + g_DeltaTime += currentDrawFunctionCallTime - lastDrawFunctionCallTime; + lastDrawFunctionCallTime = currentDrawFunctionCallTime; + g_Frames++; + + if (g_DeltaTime >= CLOCKS_PER_SEC) { + UpdateCurrentMSpFAndFPS(); + g_Frames = 0; + g_DeltaTime -= CLOCKS_PER_SEC; + } +} diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index 0031cead7..ec93ab32f 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -28,7 +28,7 @@ #include "Cxbx.h" #include "common\AddressRanges.h" #include "common/ReserveAddressRanges.h" -#include "common\xbe\Xbe.h" +#include "common\xbe\Xbe.h" #include "Logging.h" #undef FIELD_OFFSET // prevent macro redefinition warnings @@ -81,11 +81,11 @@ extern "C" { // (called "$$XTIMAG") at 0x031C5260+0x00002800, which would // fit in 51 MB. If we ever encounter an even larger XBE, this // value will have to be increased likewise (maybe up to 64 MB -// for XBOX_MEMORY_SIZE or even 128 MB for CHIHIRO_MEMORY_SIZE). -#ifdef CXBXR_EMU -#define XBE_MAX_VA (128 * ONE_MB) -#else -#define XBE_MAX_VA (64 * ONE_MB) +// for XBOX_MEMORY_SIZE or even 128 MB for CHIHIRO_MEMORY_SIZE). +#ifdef CXBXR_EMU +#define XBE_MAX_VA (128 * ONE_MB) +#else +#define XBE_MAX_VA (64 * ONE_MB) #endif /*! base address of Cxbx host executable, see Cxbx project options, Linker, Advanced, Base Address */ @@ -142,12 +142,12 @@ bool CxbxKrnlVerifyVersion(const char *szVersion); extern bool g_bIsDebugKernel; -bool CreateSettings(); +bool CreateSettings(); -bool HandleFirstLaunch(); +bool HandleFirstLaunch(); /*! Cxbx Kernel Entry Point */ -void CxbxKrnlEmulate(unsigned int system, blocks_reserved_t blocks_reserved); +void CxbxKrnlEmulate(unsigned int system, blocks_reserved_t blocks_reserved); /*! initialize emulation */ __declspec(noreturn) void CxbxKrnlInit(void *pTLSData, Xbe::TLS *pTLS, Xbe::LibraryVersion *LibraryVersion, DebugMode DbgMode, const char *szDebugFilename, Xbe::Header *XbeHeader, uint32_t XbeHeaderSize, void (*Entry)(), int BootFlags); @@ -187,7 +187,7 @@ void CxbxKrnlNoFunc(); void CxbxInitPerformanceCounters(); // Implemented in EmuKrnlKe.cpp void CxbxInitFilePaths(); - + // For emulation usage only bool CxbxLockFilePath(); void CxbxUnlockFilePath(); diff --git a/src/core/kernel/memory-manager/PhysicalMemory.h b/src/core/kernel/memory-manager/PhysicalMemory.h index d930486d8..d15ff5a7c 100644 --- a/src/core/kernel/memory-manager/PhysicalMemory.h +++ b/src/core/kernel/memory-manager/PhysicalMemory.h @@ -81,7 +81,7 @@ typedef union _MMPTE /* PFN entry used by the memory manager */ -typedef union _XBOX_PFN +typedef union _XBOX_PFN { ULONG Default; struct { @@ -115,14 +115,14 @@ typedef enum _PageType ContiguousType, // Used by MmAllocateContiguousMemoryEx and others DebuggerType, // xbdm-related COUNTtype // The size of the array containing the page usage per type -}PageType; - - -/* enum describing the memory layouts the memory manager can use */ -typedef enum _MmLayout +}PageType; + + +/* enum describing the memory layouts the memory manager can use */ +typedef enum _MmLayout { - MmChihiro = 1, - MmDebug, + MmChihiro = 1, + MmDebug, MmRetail, }MmLayout; @@ -213,10 +213,10 @@ class PhysicalMemory // number of allocated bytes for the nv2a instance memory size_t m_NV2AInstanceMemoryBytes = NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT; // boolean that indicates that the extra 64 MiB on a devkit can be used for heap/Nt allocations - bool m_bAllowNonDebuggerOnTop64MiB = true; - // the memory layout that the VMManager is emulating - bool m_MmLayoutChihiro = false; - bool m_MmLayoutDebug = false; + bool m_bAllowNonDebuggerOnTop64MiB = true; + // the memory layout that the VMManager is emulating + bool m_MmLayoutChihiro = false; + bool m_MmLayoutDebug = false; bool m_MmLayoutRetail = false; diff --git a/src/core/kernel/memory-manager/VMManager.cpp b/src/core/kernel/memory-manager/VMManager.cpp index 0fd4ee74a..eb2f0c5bf 100644 --- a/src/core/kernel/memory-manager/VMManager.cpp +++ b/src/core/kernel/memory-manager/VMManager.cpp @@ -27,9 +27,9 @@ // Acknowledgment: // Some of the functions with the suffix VMA are from the vm_manager.cpp file of the citra emulator - -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version + +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version // Refer to the license.txt file of Citra at https://github.com/citra-emu/citra/blob/master/license.txt. @@ -42,10 +42,10 @@ #include "core\kernel\exports\EmuKrnl.h" // For InitializeListHead(), etc. #include "common/util/cliConfig.hpp" // For GetSessionID #include -// Temporary usage for need ReserveAddressRanges func with cxbx.exe's emulation. -#ifndef CXBXR_EMU -#include "common/ReserveAddressRanges.h" -#endif +// Temporary usage for need ReserveAddressRanges func with cxbx.exe's emulation. +#ifndef CXBXR_EMU +#include "common/ReserveAddressRanges.h" +#endif constexpr char str_persistent_memory_s[] = "PersistentMemory-s"; @@ -79,13 +79,13 @@ void VMManager::Initialize(unsigned int SystemType, int BootFlags, blocks_reserv if ((BootFlags & BOOT_QUICK_REBOOT) == 0) { g_EmuShared->SetMmLayout(&SystemType); } - else { + else { g_EmuShared->GetMmLayout(&SystemType); - } + } m_MmLayoutChihiro = (SystemType == SYSTEM_CHIHIRO); m_MmLayoutDebug = (SystemType == SYSTEM_DEVKIT); - m_MmLayoutRetail = (SystemType == SYSTEM_XBOX); + m_MmLayoutRetail = (SystemType == SYSTEM_XBOX); // Set up the critical section to synchronize accesses InitializeCriticalSectionAndSpinCount(&m_CriticalSection, 0x400); @@ -99,31 +99,31 @@ void VMManager::Initialize(unsigned int SystemType, int BootFlags, blocks_reserv ConstructMemoryRegion(SYSTEM_MEMORY_BASE, SYSTEM_MEMORY_SIZE, SystemRegion); ConstructMemoryRegion(DEVKIT_MEMORY_BASE, DEVKIT_MEMORY_SIZE, DevkitRegion); - // Commit all the memory reserved by the loader for the PTs - // We are looping here because memory-reservation happens in 64 KiB increments - for (int i = 0; i < 64; i++) { - LPVOID ret = VirtualAlloc((LPVOID)(PAGE_TABLES_BASE + i * m_AllocationGranularity), m_AllocationGranularity, MEM_COMMIT, PAGE_READWRITE); - if (ret != (LPVOID)(PAGE_TABLES_BASE + i * KiB(64))) { - CxbxKrnlCleanup("VirtualAlloc failed to commit the memory for the page tables. The error was 0x%08X", GetLastError()); - } - } + // Commit all the memory reserved by the loader for the PTs + // We are looping here because memory-reservation happens in 64 KiB increments + for (int i = 0; i < 64; i++) { + LPVOID ret = VirtualAlloc((LPVOID)(PAGE_TABLES_BASE + i * m_AllocationGranularity), m_AllocationGranularity, MEM_COMMIT, PAGE_READWRITE); + if (ret != (LPVOID)(PAGE_TABLES_BASE + i * KiB(64))) { + CxbxKrnlCleanup("VirtualAlloc failed to commit the memory for the page tables. The error was 0x%08X", GetLastError()); + } + } // Construct VMAs base on reserved bit indexes for devkit and system region blocks. // See "blocks_reserved_t" notes. - if (SystemType == SYSTEM_DEVKIT) { - for (unsigned int i = BLOCK_REGION_DEVKIT_INDEX_BEGIN; i < BLOCK_REGION_DEVKIT_INDEX_END; i++) { - if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { - // The loader was unable to reserve this block, so discard it from the memory region - ConstructVMA(DEVKIT_MEMORY_BASE + i * KiB(64), KiB(64), DevkitRegion, ReservedVma, false); - } - } - } - for (unsigned int i = BLOCK_REGION_SYSTEM_INDEX_BEGIN; i < BLOCK_REGION_SYSTEM_INDEX_END; i++) { - if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { - // The loader was unable to reserve this block, so discard it from the memory region - ConstructVMA(SYSTEM_MEMORY_BASE + i * KiB(64), KiB(64), SystemRegion, ReservedVma, false); - } - } + if (SystemType == SYSTEM_DEVKIT) { + for (unsigned int i = BLOCK_REGION_DEVKIT_INDEX_BEGIN; i < BLOCK_REGION_DEVKIT_INDEX_END; i++) { + if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { + // The loader was unable to reserve this block, so discard it from the memory region + ConstructVMA(DEVKIT_MEMORY_BASE + i * KiB(64), KiB(64), DevkitRegion, ReservedVma, false); + } + } + } + for (unsigned int i = BLOCK_REGION_SYSTEM_INDEX_BEGIN; i < BLOCK_REGION_SYSTEM_INDEX_END; i++) { + if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { + // The loader was unable to reserve this block, so discard it from the memory region + ConstructVMA(SYSTEM_MEMORY_BASE + i * KiB(64), KiB(64), SystemRegion, ReservedVma, false); + } + } // Set up general memory variables according to the xbe type if (m_MmLayoutChihiro) @@ -248,7 +248,7 @@ void VMManager::InitializeSystemAllocations() } addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); - AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); PersistMemory(addr, (pfn_end - pfn + 1) << PAGE_SHIFT, true); if (m_MmLayoutDebug) { m_PhysicalPagesAvailable += 16; m_DebuggerPagesAvailable -= 16; } @@ -266,9 +266,9 @@ void VMManager::InitializeSystemAllocations() PointerPte = GetPteAddress(addr); EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); while (PointerPte <= EndingPte) { - DISABLE_CACHING(*PointerPte); + DISABLE_CACHING(*PointerPte); PointerPte++; } @@ -282,95 +282,95 @@ void VMManager::InitializeSystemAllocations() PointerPte = GetPteAddress(addr); EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); while (PointerPte <= EndingPte) { - DISABLE_CACHING(*PointerPte); + DISABLE_CACHING(*PointerPte); PointerPte++; } } } -void VMManager::GetPersistentMemory() -{ - if (m_PersistentMemoryHandle != nullptr) { - CxbxKrnlCleanup("Persistent memory is already opened!"); - return; - } +void VMManager::GetPersistentMemory() +{ + if (m_PersistentMemoryHandle != nullptr) { + CxbxKrnlCleanup("Persistent memory is already opened!"); + return; + } std::string persisted_mem_sid = str_persistent_memory_s + std::to_string(cli_config::GetSessionID()); - m_PersistentMemoryHandle = OpenFileMapping(FILE_MAP_READ, FALSE, persisted_mem_sid.c_str()); - if (m_PersistentMemoryHandle == nullptr) { - CxbxKrnlCleanup("Couldn't open persistent memory! OpenFileMapping failed with error 0x%08X", GetLastError()); - return; - } + m_PersistentMemoryHandle = OpenFileMapping(FILE_MAP_READ, FALSE, persisted_mem_sid.c_str()); + if (m_PersistentMemoryHandle == nullptr) { + CxbxKrnlCleanup("Couldn't open persistent memory! OpenFileMapping failed with error 0x%08X", GetLastError()); + return; + } } -void VMManager::RestorePersistentMemory() -{ - if (m_PersistentMemoryHandle == nullptr) { - CxbxKrnlCleanup("Persistent memory is not open!"); - return; - } +void VMManager::RestorePersistentMemory() +{ + if (m_PersistentMemoryHandle == nullptr) { + CxbxKrnlCleanup("Persistent memory is not open!"); + return; + } - PersistedMemory* persisted_mem = (PersistedMemory*)MapViewOfFile(m_PersistentMemoryHandle, FILE_MAP_READ, 0, 0, 0); - if (persisted_mem == nullptr) { - CxbxKrnlCleanup("Couldn't restore persistent memory! MapViewOfFile failed with error 0x%08X", GetLastError()); - return; - } + PersistedMemory* persisted_mem = (PersistedMemory*)MapViewOfFile(m_PersistentMemoryHandle, FILE_MAP_READ, 0, 0, 0); + if (persisted_mem == nullptr) { + CxbxKrnlCleanup("Couldn't restore persistent memory! MapViewOfFile failed with error 0x%08X", GetLastError()); + return; + } - if (persisted_mem->LaunchFrameAddresses[0] != 0 && IS_PHYSICAL_ADDRESS(persisted_mem->LaunchFrameAddresses[0])) { - xboxkrnl::LaunchDataPage = (xboxkrnl::PLAUNCH_DATA_PAGE)persisted_mem->LaunchFrameAddresses[0]; - EmuLog(LOG_LEVEL::INFO, "Restored LaunchDataPage\n"); - } + if (persisted_mem->LaunchFrameAddresses[0] != 0 && IS_PHYSICAL_ADDRESS(persisted_mem->LaunchFrameAddresses[0])) { + xboxkrnl::LaunchDataPage = (xboxkrnl::PLAUNCH_DATA_PAGE)persisted_mem->LaunchFrameAddresses[0]; + EmuLog(LOG_LEVEL::INFO, "Restored LaunchDataPage\n"); + } - if (persisted_mem->LaunchFrameAddresses[1] != 0 && IS_PHYSICAL_ADDRESS(persisted_mem->LaunchFrameAddresses[1])) { - xboxkrnl::AvSavedDataAddress = (xboxkrnl::PVOID)persisted_mem->LaunchFrameAddresses[1]; - EmuLog(LOG_LEVEL::INFO, "Restored Framebuffer\n"); - } + if (persisted_mem->LaunchFrameAddresses[1] != 0 && IS_PHYSICAL_ADDRESS(persisted_mem->LaunchFrameAddresses[1])) { + xboxkrnl::AvSavedDataAddress = (xboxkrnl::PVOID)persisted_mem->LaunchFrameAddresses[1]; + EmuLog(LOG_LEVEL::INFO, "Restored Framebuffer\n"); + } - MMPTE pte; - PFN pfn; - for (unsigned int i = 0; i < persisted_mem->NumOfPtes; i++) { - pte.Default = persisted_mem->Data[persisted_mem->NumOfPtes + i]; - assert(pte.Hardware.Valid != 0 && pte.Hardware.Persist != 0); - memcpy(GetPteAddress(persisted_mem->Data[i]), &pte.Default, sizeof(MMPTE)); - RemoveFree(1, &pfn, 0, pte.Hardware.PFN, pte.Hardware.PFN); - if (m_MmLayoutChihiro) { - memcpy(CHIHIRO_PFN_ELEMENT(pte.Hardware.PFN), - &((PXBOX_PFN)&persisted_mem->Data[(persisted_mem->NumOfPtes * 2) + (persisted_mem->NumOfPtes - 32) * KiB(1)])[pte.Hardware.PFN], - sizeof(XBOX_PFN)); - if ((uint32_t*)persisted_mem->Data[i] < (uint32_t*)CHIHIRO_PFN_ADDRESS) { - memcpy((void*)(persisted_mem->Data[i]), &persisted_mem->Data[persisted_mem->NumOfPtes * 2 + i * KiB(1)], PAGE_SIZE); - } - } - else { - memcpy(XBOX_PFN_ELEMENT(pte.Hardware.PFN), - &((PXBOX_PFN)&persisted_mem->Data[(persisted_mem->NumOfPtes * 2) + (persisted_mem->NumOfPtes - 16) * KiB(1)])[pte.Hardware.PFN], - sizeof(XBOX_PFN)); - if ((uint32_t*)persisted_mem->Data[i] < (uint32_t*)XBOX_PFN_ADDRESS) { - memcpy((void*)(persisted_mem->Data[i]), &persisted_mem->Data[persisted_mem->NumOfPtes * 2 + i * KiB(1)], PAGE_SIZE); - } - } - } + MMPTE pte; + PFN pfn; + for (unsigned int i = 0; i < persisted_mem->NumOfPtes; i++) { + pte.Default = persisted_mem->Data[persisted_mem->NumOfPtes + i]; + assert(pte.Hardware.Valid != 0 && pte.Hardware.Persist != 0); + memcpy(GetPteAddress(persisted_mem->Data[i]), &pte.Default, sizeof(MMPTE)); + RemoveFree(1, &pfn, 0, pte.Hardware.PFN, pte.Hardware.PFN); + if (m_MmLayoutChihiro) { + memcpy(CHIHIRO_PFN_ELEMENT(pte.Hardware.PFN), + &((PXBOX_PFN)&persisted_mem->Data[(persisted_mem->NumOfPtes * 2) + (persisted_mem->NumOfPtes - 32) * KiB(1)])[pte.Hardware.PFN], + sizeof(XBOX_PFN)); + if ((uint32_t*)persisted_mem->Data[i] < (uint32_t*)CHIHIRO_PFN_ADDRESS) { + memcpy((void*)(persisted_mem->Data[i]), &persisted_mem->Data[persisted_mem->NumOfPtes * 2 + i * KiB(1)], PAGE_SIZE); + } + } + else { + memcpy(XBOX_PFN_ELEMENT(pte.Hardware.PFN), + &((PXBOX_PFN)&persisted_mem->Data[(persisted_mem->NumOfPtes * 2) + (persisted_mem->NumOfPtes - 16) * KiB(1)])[pte.Hardware.PFN], + sizeof(XBOX_PFN)); + if ((uint32_t*)persisted_mem->Data[i] < (uint32_t*)XBOX_PFN_ADDRESS) { + memcpy((void*)(persisted_mem->Data[i]), &persisted_mem->Data[persisted_mem->NumOfPtes * 2 + i * KiB(1)], PAGE_SIZE); + } + } + } - PFN_COUNT pages_num = 1; - for (unsigned int i = 0; i < persisted_mem->NumOfPtes; i++) { + PFN_COUNT pages_num = 1; + for (unsigned int i = 0; i < persisted_mem->NumOfPtes; i++) { pte.Default = persisted_mem->Data[persisted_mem->NumOfPtes + i]; if (pte.Hardware.GuardOrEnd == 0) { pages_num++; continue; } - size_t size = pages_num << PAGE_SHIFT; - VAddr addr = persisted_mem->Data[i] - (size - PAGE_SIZE); - AllocatePT(size, addr); - ConstructVMA(addr, size, ContiguousRegion, AllocatedVma, false); - GetPfnOfPT(GetPteAddress(addr))->PTPageFrame.PtesUsed += pages_num; + size_t size = pages_num << PAGE_SHIFT; + VAddr addr = persisted_mem->Data[i] - (size - PAGE_SIZE); + AllocatePT(size, addr); + ConstructVMA(addr, size, ContiguousRegion, AllocatedVma, false); + GetPfnOfPT(GetPteAddress(addr))->PTPageFrame.PtesUsed += pages_num; pages_num = 1; } - if (m_MmLayoutDebug) { m_PhysicalPagesAvailable += 16; m_DebuggerPagesAvailable -= 16; } + if (m_MmLayoutDebug) { m_PhysicalPagesAvailable += 16; m_DebuggerPagesAvailable -= 16; } - PFN pfn_end; + PFN pfn_end; if (m_MmLayoutRetail || m_MmLayoutDebug) { pfn = XBOX_INSTANCE_PHYSICAL_PAGE; pfn_end = XBOX_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; @@ -383,9 +383,9 @@ void VMManager::RestorePersistentMemory() PMMPTE PointerPte = GetPteAddress(addr); PMMPTE EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); while (PointerPte <= EndingPte) { - DISABLE_CACHING(*PointerPte); + DISABLE_CACHING(*PointerPte); PointerPte++; } @@ -399,86 +399,86 @@ void VMManager::RestorePersistentMemory() PointerPte = GetPteAddress(addr); EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); while (PointerPte <= EndingPte) { - DISABLE_CACHING(*PointerPte); + DISABLE_CACHING(*PointerPte); PointerPte++; } } - UnmapViewOfFile(persisted_mem); - CloseHandle(m_PersistentMemoryHandle); + UnmapViewOfFile(persisted_mem); + CloseHandle(m_PersistentMemoryHandle); m_PersistentMemoryHandle = nullptr; } -void VMManager::SavePersistentMemory() +void VMManager::SavePersistentMemory() { - PersistedMemory* persisted_mem; - size_t num_persisted_ptes; - std::vector cached_persisted_ptes; - LPVOID addr; - PMMPTE PointerPte; - PMMPTE EndingPte; - int i; + PersistedMemory* persisted_mem; + size_t num_persisted_ptes; + std::vector cached_persisted_ptes; + LPVOID addr; + PMMPTE PointerPte; + PMMPTE EndingPte; + int i; - Lock(); - - num_persisted_ptes = 0; - PointerPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE); - - if (m_MmLayoutRetail) { - EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + XBOX_CONTIGUOUS_MEMORY_SIZE - 1); + Lock(); + + num_persisted_ptes = 0; + PointerPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE); + + if (m_MmLayoutRetail) { + EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + XBOX_CONTIGUOUS_MEMORY_SIZE - 1); } - else { - EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1); - } - - while (PointerPte <= EndingPte) + else { + EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1); + } + + while (PointerPte <= EndingPte) { - if (PointerPte->Hardware.Valid != 0 && PointerPte->Hardware.Persist != 0) { - cached_persisted_ptes.push_back(PointerPte); - num_persisted_ptes++; + if (PointerPte->Hardware.Valid != 0 && PointerPte->Hardware.Persist != 0) { + cached_persisted_ptes.push_back(PointerPte); + num_persisted_ptes++; } - PointerPte++; - } + PointerPte++; + } std::string persistent_mem_sid = str_persistent_memory_s + std::to_string(cli_config::GetSessionID()); - m_PersistentMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, num_persisted_ptes * PAGE_SIZE + num_persisted_ptes * 4 * 2 + sizeof(PersistedMemory), persistent_mem_sid.c_str()); - if (m_PersistentMemoryHandle == NULL) { - CxbxKrnlCleanup("Couldn't persist memory! CreateFileMapping failed with error 0x%08X", GetLastError()); - return; - } - addr = MapViewOfFile(m_PersistentMemoryHandle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); - if (addr == nullptr) { - CxbxKrnlCleanup("Couldn't persist memory! MapViewOfFile failed with error 0x%08X", GetLastError()); - return; - } - - persisted_mem = (PersistedMemory*)addr; - persisted_mem->NumOfPtes = num_persisted_ptes; - - if (xboxkrnl::LaunchDataPage != xbnullptr) { - persisted_mem->LaunchFrameAddresses[0] = (VAddr)xboxkrnl::LaunchDataPage; - EmuLog(LOG_LEVEL::INFO, "Persisted LaunchDataPage\n"); + m_PersistentMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, num_persisted_ptes * PAGE_SIZE + num_persisted_ptes * 4 * 2 + sizeof(PersistedMemory), persistent_mem_sid.c_str()); + if (m_PersistentMemoryHandle == NULL) { + CxbxKrnlCleanup("Couldn't persist memory! CreateFileMapping failed with error 0x%08X", GetLastError()); + return; + } + addr = MapViewOfFile(m_PersistentMemoryHandle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + if (addr == nullptr) { + CxbxKrnlCleanup("Couldn't persist memory! MapViewOfFile failed with error 0x%08X", GetLastError()); + return; } - if (xboxkrnl::AvSavedDataAddress != xbnullptr) { - persisted_mem->LaunchFrameAddresses[1] = (VAddr)xboxkrnl::AvSavedDataAddress; - EmuLog(LOG_LEVEL::INFO, "Persisted Framebuffer\n"); - } + persisted_mem = (PersistedMemory*)addr; + persisted_mem->NumOfPtes = num_persisted_ptes; - i = 0; - - for (const auto &pte : cached_persisted_ptes) { - persisted_mem->Data[i] = GetVAddrMappedByPte(pte); - persisted_mem->Data[num_persisted_ptes + i] = pte->Default; - memcpy(&persisted_mem->Data[num_persisted_ptes * 2 + i * KiB(1)], (void *)(persisted_mem->Data[i]), PAGE_SIZE); - i++; + if (xboxkrnl::LaunchDataPage != xbnullptr) { + persisted_mem->LaunchFrameAddresses[0] = (VAddr)xboxkrnl::LaunchDataPage; + EmuLog(LOG_LEVEL::INFO, "Persisted LaunchDataPage\n"); } - assert(i == num_persisted_ptes); + if (xboxkrnl::AvSavedDataAddress != xbnullptr) { + persisted_mem->LaunchFrameAddresses[1] = (VAddr)xboxkrnl::AvSavedDataAddress; + EmuLog(LOG_LEVEL::INFO, "Persisted Framebuffer\n"); + } - Unlock(); + i = 0; + + for (const auto &pte : cached_persisted_ptes) { + persisted_mem->Data[i] = GetVAddrMappedByPte(pte); + persisted_mem->Data[num_persisted_ptes + i] = pte->Default; + memcpy(&persisted_mem->Data[num_persisted_ptes * 2 + i * KiB(1)], (void *)(persisted_mem->Data[i]), PAGE_SIZE); + i++; + } + + assert(i == num_persisted_ptes); + + Unlock(); } VAddr VMManager::DbgTestPte(VAddr addr, PMMPTE Pte, bool bWriteCheck) @@ -641,14 +641,14 @@ void VMManager::PersistMemory(VAddr addr, size_t Size, bool bPersist) while (PointerPte <= EndingPte) { PointerPte->Hardware.Persist = 1; - PointerPte++; + PointerPte++; } } else { while (PointerPte <= EndingPte) { PointerPte->Hardware.Persist = 0; - PointerPte++; + PointerPte++; } } @@ -677,7 +677,7 @@ VAddr VMManager::Allocate(size_t Size) ConvertXboxToPtePermissions(XBOX_PAGE_EXECUTE_READWRITE, &TempPte); addr = MapMemoryBlock(UserRegion, PteNumber, MEM_RESERVE | MEM_COMMIT, false); - if (!addr) { goto Fail; } + if (!addr) { goto Fail; } // Check if we have to construct the PT's for this allocation if (!AllocatePT(PteNumber << PAGE_SHIFT, addr)) @@ -744,10 +744,10 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz // NOTE: AllocateSystemMemory won't allocate a physical page for the guard page (if requested) and just adds an extra // unallocated virtual page in front of the mapped allocation. For this reason we will decommmit later the extra guard page allocated - if (!Size || !ConvertXboxToSystemPtePermissions(Perms, &TempPte)) { RETURN(NULL); } - - LowestAcceptablePfn = 0; - HighestAcceptablePfn = m_MmLayoutDebug ? XBOX_HIGHEST_PHYSICAL_PAGE : m_HighestPage; + if (!Size || !ConvertXboxToSystemPtePermissions(Perms, &TempPte)) { RETURN(NULL); } + + LowestAcceptablePfn = 0; + HighestAcceptablePfn = m_MmLayoutDebug ? XBOX_HIGHEST_PHYSICAL_PAGE : m_HighestPage; MemoryType = SystemRegion; Lock(); @@ -770,7 +770,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz addr = MapMemoryBlock(MemoryType, PteNumber, MEM_COMMIT, true); - if (!addr) { goto Fail; } + if (!addr) { goto Fail; } // check if we have to construct the PT's for this allocation if (!AllocatePT(PteNumber << PAGE_SHIFT, addr)) @@ -843,19 +843,19 @@ VAddr VMManager::AllocateContiguousMemory(size_t Size, PAddr LowestAddress, PAdd Lock(); - if (!IsMappable(PteNumber, true, false)) { - Unlock(); - RETURN(NULL); - } - - Addr = AllocateContiguousMemoryInternal(PteNumber, LowestPfn, HighestPfn, PfnAlignment, Perms); - + if (!IsMappable(PteNumber, true, false)) { + Unlock(); + RETURN(NULL); + } + + Addr = AllocateContiguousMemoryInternal(PteNumber, LowestPfn, HighestPfn, PfnAlignment, Perms); + Unlock(); RETURN(Addr); } - -VAddr VMManager::AllocateContiguousMemoryInternal(PFN_COUNT NumberOfPages, PFN LowestPfn, PFN HighestPfn, PFN PfnAlignment, DWORD Perms) + +VAddr VMManager::AllocateContiguousMemoryInternal(PFN_COUNT NumberOfPages, PFN LowestPfn, PFN HighestPfn, PFN PfnAlignment, DWORD Perms) { MMPTE TempPte; PMMPTE PointerPte; @@ -984,7 +984,7 @@ void VMManager::Deallocate(VAddr addr) } PointerPte = GetPteAddress(addr); - EndingPte = PointerPte + (it->second.size >> PAGE_SHIFT) - 1; + EndingPte = PointerPte + (it->second.size >> PAGE_SHIFT) - 1; StartingPte = PointerPte; PteNumber = EndingPte - StartingPte + 1; @@ -1093,7 +1093,7 @@ PFN_COUNT VMManager::DeallocateSystemMemory(PageType BusyType, VAddr addr, size_ bGuardPageAdded = true; } - EndingPte = PointerPte + (Size >> PAGE_SHIFT) - 1; + EndingPte = PointerPte + (Size >> PAGE_SHIFT) - 1; StartingPte = PointerPte; PteNumber = EndingPte - PointerPte + 1; @@ -1561,8 +1561,8 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit goto Exit; } - // Attempt to commit the requested range with VirtualAlloc *before* setting up and reserving the PT - // This allows an early-out in a failure scenario (Test Case: Star Wars Battlefront DVD Demo: LA-018 v1.02) + // Attempt to commit the requested range with VirtualAlloc *before* setting up and reserving the PT + // This allows an early-out in a failure scenario (Test Case: Star Wars Battlefront DVD Demo: LA-018 v1.02) // We don't commit the requested range if it's within our placeholder, since that was already allocated earlier if (AlignedCapturedBase >= XBE_MAX_VA) { @@ -1570,10 +1570,10 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit (ConvertXboxToWinPermissions(PatchXboxPermissions(Protect))) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE))) { EmuLog(LOG_LEVEL::DEBUG, "%s: VirtualAlloc failed to commit the memory! The error was 0x%08X", __func__, GetLastError()); - status = STATUS_NO_MEMORY; + status = STATUS_NO_MEMORY; goto Exit; } - } + } // Check if we have to construct the PT's for this allocation @@ -2076,13 +2076,13 @@ VAddr VMManager::MapMemoryBlock(MemoryRegionType Type, PFN_COUNT PteNumber, DWOR addr = ROUND_UP(addr, m_AllocationGranularity); } - if (Permissions == 0xFFFFFFFF) { - if (addr + Size - 1 < it->first + it->second.size) { - return addr; - } - ++it; - continue; - } + if (Permissions == 0xFFFFFFFF) { + if (addr + Size - 1 < it->first + it->second.size) { + return addr; + } + ++it; + continue; + } // Note that, even in free regions, somebody outside the manager could have allocated the memory so we just // keep on trying until we succeed or fail entirely. @@ -2091,23 +2091,23 @@ VAddr VMManager::MapMemoryBlock(MemoryRegionType Type, PFN_COUNT PteNumber, DWOR if (HighestAddress && (it->first + it->second.size > HighestAddress + 1)) { vma_end = HighestAddress + 1; } else { vma_end = it->first + it->second.size; } - if (b64Blocks) { - if (addr + Size - 1 < vma_end) { - // The memory was reserved by the loader, commit it in 64kb blocks - VAddr start_addr = addr; - size_t start_size = 0; - while (addr < vma_end) { - addr = MapHostMemory(addr, m_AllocationGranularity, vma_end, Permissions); - assert(addr); - start_size += m_AllocationGranularity; + if (b64Blocks) { + if (addr + Size - 1 < vma_end) { + // The memory was reserved by the loader, commit it in 64kb blocks + VAddr start_addr = addr; + size_t start_size = 0; + while (addr < vma_end) { + addr = MapHostMemory(addr, m_AllocationGranularity, vma_end, Permissions); + assert(addr); + start_size += m_AllocationGranularity; if (start_size >= Size) { return start_addr; } addr += m_AllocationGranularity; - } - assert(0); + } + assert(0); } - } + } else { addr = MapHostMemory(addr, Size, vma_end, Permissions); if (addr) { return addr; } @@ -2144,31 +2144,31 @@ VAddr VMManager::MapMemoryBlock(MemoryRegionType Type, PFN_COUNT PteNumber, DWOR size_t vma_end = it->first + it->second.size; - if (Permissions == 0xFFFFFFFF) { - if (addr + Size - 1 < vma_end) { - return addr; - } - --it; - continue; + if (Permissions == 0xFFFFFFFF) { + if (addr + Size - 1 < vma_end) { + return addr; + } + --it; + continue; } - if (b64Blocks) { - if (addr + Size - 1 < vma_end) { - // The memory was reserved by the loader, commit it in 64kb blocks - VAddr start_addr = addr; - size_t start_size = 0; - while (addr < vma_end) { - addr = MapHostMemory(addr, m_AllocationGranularity, vma_end, Permissions); - assert(addr); - start_size += m_AllocationGranularity; + if (b64Blocks) { + if (addr + Size - 1 < vma_end) { + // The memory was reserved by the loader, commit it in 64kb blocks + VAddr start_addr = addr; + size_t start_size = 0; + while (addr < vma_end) { + addr = MapHostMemory(addr, m_AllocationGranularity, vma_end, Permissions); + assert(addr); + start_size += m_AllocationGranularity; if (start_size >= Size) { return start_addr; } addr += m_AllocationGranularity; - } - assert(0); + } + assert(0); } - } + } else { addr = MapHostMemory(addr, Size, vma_end, Permissions); if (addr) { return addr; } @@ -2239,7 +2239,7 @@ PAddr VMManager::TranslateVAddrToPAddr(const VAddr addr) PAddr PAddr; PMMPTE PointerPte; - // ergo720: horrendous hack, this identity maps all allocations done by the VMManager to keep the LLE USB working. + // ergo720: horrendous hack, this identity maps all allocations done by the VMManager to keep the LLE USB working. // The problem is that if the user buffer pointed to by the TD is allocated by the VMManager with VirtualAlloc, then // the physical allocation will not reside in the contiguous memory and if we tried to access the physical address of it, // we would access a random page with undefined contents. @@ -2252,7 +2252,7 @@ PAddr VMManager::TranslateVAddrToPAddr(const VAddr addr) RETURN(NULL); } - Lock(); + Lock(); PointerPte = GetPdeAddress(addr); if (PointerPte->Hardware.Valid == 0) { // invalid pde -> addr is invalid @@ -2499,7 +2499,7 @@ void VMManager::ConstructVMA(VAddr Start, size_t Size, MemoryRegionType Type, VM m_MemoryRegionArray[Type].LastFree = m_MemoryRegionArray[Type].RegionMap.end(); return; -} +} void VMManager::DestructVMA(VAddr addr, MemoryRegionType Type, size_t Size) { @@ -2510,9 +2510,9 @@ void VMManager::DestructVMA(VAddr addr, MemoryRegionType Type, size_t Size) if ((addr >= XBE_MAX_VA) && (Type != ContiguousRegion)) { - if (Type == SystemRegion || Type == DevkitRegion) - { - // This memory was reserved by the loader, do not release it, only decommit + if (Type == SystemRegion || Type == DevkitRegion) + { + // This memory was reserved by the loader, do not release it, only decommit ret = VirtualFree((void*)addr, Size, MEM_DECOMMIT); } diff --git a/src/core/kernel/support/Emu.cpp b/src/core/kernel/support/Emu.cpp index a6ffd4654..528bff5d3 100644 --- a/src/core/kernel/support/Emu.cpp +++ b/src/core/kernel/support/Emu.cpp @@ -53,39 +53,39 @@ bool g_UseAllCores = false; bool g_SkipRdtscPatching = false; int g_RenderScaleFactor = 1; -// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime -// This shouldn't need to be atomic, but because raising the IRQL to high lv in KeSetSystemTime doesn't really stop KiClockIsr from running, +// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime +// This shouldn't need to be atomic, but because raising the IRQL to high lv in KeSetSystemTime doesn't really stop KiClockIsr from running, // we need it for now to prevent reading a corrupted value while KeSetSystemTime is in the middle of updating it std::atomic_int64_t HostSystemTimeDelta(0); // Static Function(s) -static int ExitException(LPEXCEPTION_POINTERS e); - -std::string FormatTitleId(uint32_t title_id) -{ - std::stringstream ss; - - // If the Title ID prefix is a printable character, parse it - // This shows the correct game serial number for retail titles! - // EG: MS-001 for 1st tile published by MS, EA-002 for 2nd title by EA, etc - // Some special Xbes (Dashboard, XDK Samples) use non-alphanumeric serials - // We fall back to Hex for those - // ergo720: we cannot use isalnum() here because it will treat chars in the range -1 - 255 as valid ascii chars which can - // lead to unicode characters being printed in the title (e.g.: dashboard uses 0xFE and 0xFF) - uint8_t pTitleId1 = (title_id >> 24) & 0xFF; - uint8_t pTitleId2 = (title_id >> 16) & 0xFF; - - if ((pTitleId1 < 65 || pTitleId1 > 90) || (pTitleId2 < 65 || pTitleId2 > 90)) { - // Prefix was non-printable, so we need to print a hex reprentation of the entire title_id - ss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << title_id; - return ss.str(); - } - - ss << pTitleId1 << pTitleId2; - ss << "-"; - ss << std::setfill('0') << std::setw(3) << std::dec << (title_id & 0x0000FFFF); - - return ss.str(); +static int ExitException(LPEXCEPTION_POINTERS e); + +std::string FormatTitleId(uint32_t title_id) +{ + std::stringstream ss; + + // If the Title ID prefix is a printable character, parse it + // This shows the correct game serial number for retail titles! + // EG: MS-001 for 1st tile published by MS, EA-002 for 2nd title by EA, etc + // Some special Xbes (Dashboard, XDK Samples) use non-alphanumeric serials + // We fall back to Hex for those + // ergo720: we cannot use isalnum() here because it will treat chars in the range -1 - 255 as valid ascii chars which can + // lead to unicode characters being printed in the title (e.g.: dashboard uses 0xFE and 0xFF) + uint8_t pTitleId1 = (title_id >> 24) & 0xFF; + uint8_t pTitleId2 = (title_id >> 16) & 0xFF; + + if ((pTitleId1 < 65 || pTitleId1 > 90) || (pTitleId2 < 65 || pTitleId2 > 90)) { + // Prefix was non-printable, so we need to print a hex reprentation of the entire title_id + ss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << title_id; + return ss.str(); + } + + ss << pTitleId1 << pTitleId2; + ss << "-"; + ss << std::setfill('0') << std::setw(3) << std::dec << (title_id & 0x0000FFFF); + + return ss.str(); } std::string EIPToString(xbaddr EIP) @@ -149,16 +149,16 @@ void EmuExceptionExitProcess() } bool EmuExceptionBreakpointAsk(LPEXCEPTION_POINTERS e) -{ +{ EmuExceptionPrintDebugInformation(e, /*IsBreakpointException=*/true); - - // We can skip Xbox as long as they are logged so we know about them - // There's no need to prevent emulation, we can just pretend we have a debugger attached and continue - // This is because some games (such as Crash Bandicoot) spam exceptions; + + // We can skip Xbox as long as they are logged so we know about them + // There's no need to prevent emulation, we can just pretend we have a debugger attached and continue + // This is because some games (such as Crash Bandicoot) spam exceptions; e->ContextRecord->Eip += EmuX86_OpcodeSize((uint8_t*)e->ContextRecord->Eip); // Skip 1 size bytes - return true; - -#if 1 + return true; + +#if 1 #else char buffer[256]; sprintf(buffer, @@ -184,9 +184,9 @@ bool EmuExceptionBreakpointAsk(LPEXCEPTION_POINTERS e) return true; } - return false; + return false; #endif -} +} void EmuExceptionNonBreakpointUnhandledShow(LPEXCEPTION_POINTERS e) { @@ -212,18 +212,18 @@ bool IsXboxCodeAddress(xbaddr addr) // TODO : Replace the following with a (fast) check weither // the given address lies in xbox allocated virtual memory, // for example by g_VMManager.CheckConflictingVMA(addr, 0). - return (addr >= XBE_IMAGE_BASE) && (addr <= XBE_MAX_VA); - // Note : Not IS_USER_ADDRESS(), that would include host DLL code -} - -#include "distorm.h" -bool EmuX86_DecodeOpcode(const uint8_t* Eip, _DInst& info); -void EmuX86_DistormLogInstruction(const uint8_t* Eip, _DInst& info, LOG_LEVEL log_level); -void genericException(EXCEPTION_POINTERS *e) { - _DInst info; - if (EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, info)) { - EmuX86_DistormLogInstruction((uint8_t*)e->ContextRecord->Eip, info, LOG_LEVEL::FATAL); - } + return (addr >= XBE_IMAGE_BASE) && (addr <= XBE_MAX_VA); + // Note : Not IS_USER_ADDRESS(), that would include host DLL code +} + +#include "distorm.h" +bool EmuX86_DecodeOpcode(const uint8_t* Eip, _DInst& info); +void EmuX86_DistormLogInstruction(const uint8_t* Eip, _DInst& info, LOG_LEVEL log_level); +void genericException(EXCEPTION_POINTERS *e) { + _DInst info; + if (EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, info)) { + EmuX86_DistormLogInstruction((uint8_t*)e->ContextRecord->Eip, info, LOG_LEVEL::FATAL); + } // Try to report this exception to the debugger, which may allow handling of this exception if (CxbxDebugger::CanReport()) { bool DebuggerHandled = false; @@ -231,7 +231,7 @@ void genericException(EXCEPTION_POINTERS *e) { if (!DebuggerHandled) { // Kill the process immediately without the Cxbx notifier EmuExceptionExitProcess(); - } + } // Bypass exception } @@ -240,20 +240,20 @@ void genericException(EXCEPTION_POINTERS *e) { EmuExceptionNonBreakpointUnhandledShow(e); } } - -bool IsRdtscInstruction(xbaddr addr); // Implemented in CxbxKrnl.cpp -void EmuX86_Opcode_RDTSC(EXCEPTION_POINTERS *e); // Implemented in EmuX86.cpp -bool lleTryHandleException(EXCEPTION_POINTERS *e) -{ - // Initalize local thread variable - bOverrideEmuException = false; - - // Only handle exceptions which originate from Xbox code - if (!IsXboxCodeAddress(e->ContextRecord->Eip)) { - return false; - } - - // Make sure access-violations reach EmuX86_DecodeException() as soon as possible + +bool IsRdtscInstruction(xbaddr addr); // Implemented in CxbxKrnl.cpp +void EmuX86_Opcode_RDTSC(EXCEPTION_POINTERS *e); // Implemented in EmuX86.cpp +bool lleTryHandleException(EXCEPTION_POINTERS *e) +{ + // Initalize local thread variable + bOverrideEmuException = false; + + // Only handle exceptions which originate from Xbox code + if (!IsXboxCodeAddress(e->ContextRecord->Eip)) { + return false; + } + + // Make sure access-violations reach EmuX86_DecodeException() as soon as possible if (e->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { switch (e->ExceptionRecord->ExceptionCode) { case STATUS_PRIVILEGED_INSTRUCTION: @@ -267,82 +267,82 @@ bool lleTryHandleException(EXCEPTION_POINTERS *e) break; case STATUS_BREAKPOINT: // Pass breakpoint down to EmuException since VEH doesn't have call stack viewable. - return false; - default: + return false; + default: // Skip past CxbxDebugger-specific exceptions thrown when an unsupported was attached (ie Visual Studio) if (CxbxDebugger::IsDebuggerException(e->ExceptionRecord->ExceptionCode)) { - return true; + return true; } } } - + // Pass the exception to our X86 implementation, to try and execute the failing instruction if (EmuX86_DecodeException(e)) { // We're allowed to continue : return true; - } - - // We do not need EmuException to handle it again. + } + + // We do not need EmuException to handle it again. bOverrideEmuException = true; // Unhandled exception : return false; } - -// Only for LLE emulation coding (to help performance a little bit better) + +// Only for LLE emulation coding (to help performance a little bit better) long WINAPI lleException(EXCEPTION_POINTERS *e) { - g_bEmuException = true; - long result = lleTryHandleException(e) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; - g_bEmuException = false; + g_bEmuException = true; + long result = lleTryHandleException(e) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; + g_bEmuException = false; return result; -} - -// Only for Cxbx emulation coding (to catch all of last resort exception may occur.) -bool EmuTryHandleException(EXCEPTION_POINTERS *e) -{ - - // Check if lle exception is already called first before emu exception. +} + +// Only for Cxbx emulation coding (to catch all of last resort exception may occur.) +bool EmuTryHandleException(EXCEPTION_POINTERS *e) +{ + + // Check if lle exception is already called first before emu exception. if (bOverrideEmuException) { genericException(e); return false; - } - - if (e->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { - bool isInt2Dh = *(uint16_t*)(e->ContextRecord->Eip - 2) == 0x2DCD; + } + + if (e->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { + bool isInt2Dh = *(uint16_t*)(e->ContextRecord->Eip - 2) == 0x2DCD; switch (e->ExceptionRecord->ExceptionCode) { - case STATUS_BREAKPOINT: - // First, check if the breakpoint was prefixed with int 0x2dh, if so, it's NOT a breakpoint, but a Debugger Command! - // Note: Because of a Windows quirk, this does NOT work when a debugger is attached, we'll get an UNCATCHABLE breakpoint instead - // But at least this gives us a working implementaton of Xbox DbgPrint when we don't attach a debugger - if (isInt2Dh ){ - e->ContextRecord->Eip += 1; - - // Now perform the command (stored in EAX) - switch (e->ContextRecord->Eax) { - case 1: // DEBUG_PRINT - // In this case, ECX should point to an ANSI String - printf("DEBUG_PRINT: %s\n", ((xboxkrnl::PANSI_STRING)e->ContextRecord->Ecx)->Buffer); - break; - default: - printf("Unhandled Debug Command: int 2Dh, EAX = %d", e->ContextRecord->Eip); - } - - return true; - } - + case STATUS_BREAKPOINT: + // First, check if the breakpoint was prefixed with int 0x2dh, if so, it's NOT a breakpoint, but a Debugger Command! + // Note: Because of a Windows quirk, this does NOT work when a debugger is attached, we'll get an UNCATCHABLE breakpoint instead + // But at least this gives us a working implementaton of Xbox DbgPrint when we don't attach a debugger + if (isInt2Dh ){ + e->ContextRecord->Eip += 1; + + // Now perform the command (stored in EAX) + switch (e->ContextRecord->Eax) { + case 1: // DEBUG_PRINT + // In this case, ECX should point to an ANSI String + printf("DEBUG_PRINT: %s\n", ((xboxkrnl::PANSI_STRING)e->ContextRecord->Ecx)->Buffer); + break; + default: + printf("Unhandled Debug Command: int 2Dh, EAX = %d", e->ContextRecord->Eip); + } + + return true; + } + // Otherwise, let the user choose between continue or break - return EmuExceptionBreakpointAsk(e); - default: - printf("Unhandled Debug Command: int 2Dh, EAX = %d", e->ContextRecord->Eip); + return EmuExceptionBreakpointAsk(e); + default: + printf("Unhandled Debug Command: int 2Dh, EAX = %d", e->ContextRecord->Eip); // Skip past CxbxDebugger-specific exceptions thrown when an unsupported was attached (ie Visual Studio) if (CxbxDebugger::IsDebuggerException(e->ExceptionRecord->ExceptionCode)) { - return true; + return true; } } - } - + } + genericException(e); // Unhandled exception : diff --git a/src/core/kernel/support/Emu.h b/src/core/kernel/support/Emu.h index 8b809733f..2c41ce109 100644 --- a/src/core/kernel/support/Emu.h +++ b/src/core/kernel/support/Emu.h @@ -25,13 +25,13 @@ #ifndef EMU_H #define EMU_H -#include "common\xbe\Xbe.h" +#include "common\xbe\Xbe.h" #include "Logging.h" #undef FIELD_OFFSET // prevent macro redefinition warnings #include -#include - +#include + std::string FormatTitleId(uint32_t title_id); // exception handler @@ -67,8 +67,8 @@ extern void * funcExclude[2048]; // partition emulation directory handles extern HANDLE g_hCurDir; extern CHAR *g_strCurDrive; -extern HWND g_hEmuWindow; - +extern HWND g_hEmuWindow; + #define GET_FRONT_WINDOW_HANDLE ((CxbxKrnl_hEmuParent != nullptr) ? CxbxKrnl_hEmuParent : g_hEmuWindow) // thread notification routine @@ -91,10 +91,10 @@ typedef struct DUMMY_KERNEL IMAGE_SECTION_HEADER SectionHeader; } *PDUMMY_KERNEL; -typedef WORD INDEX16; +typedef WORD INDEX16; extern bool g_DisablePixelShaders; extern bool g_UseAllCores; -extern bool g_SkipRdtscPatching; +extern bool g_SkipRdtscPatching; extern int g_RenderScaleFactor; #endif diff --git a/src/core/kernel/support/EmuFS.cpp b/src/core/kernel/support/EmuFS.cpp index c1d8217b7..08cc23a95 100644 --- a/src/core/kernel/support/EmuFS.cpp +++ b/src/core/kernel/support/EmuFS.cpp @@ -29,8 +29,8 @@ #include -#include "core\kernel\exports\EmuKrnl.h" // For InitializeListHead(), etc. -#include "core\kernel\exports\EmuKrnlKe.h" +#include "core\kernel\exports\EmuKrnl.h" // For InitializeListHead(), etc. +#include "core\kernel\exports\EmuKrnlKe.h" #include "core\kernel\support\EmuFS.h" // For fs_instruction_t #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\memory-manager\VMManager.h" @@ -38,7 +38,7 @@ #undef FIELD_OFFSET // prevent macro redefinition warnings #include -#include +#include #include // NT_TIB (Thread Information Block) offsets - see https://www.microsoft.com/msj/archive/S2CE.aspx @@ -122,23 +122,23 @@ uint32_t fs_lock = 0; __declspec(naked) void LockFS() { __asm { - // Backup Registers - pushfd - pushad - jmp entry - - // Spin until we can aquire the lock - spinlock : - call SwitchToThread // Give other threads a chance to run if we couldn't get the lock - entry: - mov eax, 1 - xchg eax, fs_lock - test eax, eax - jnz spinlock - - // Restore registers and return - popad - popfd + // Backup Registers + pushfd + pushad + jmp entry + + // Spin until we can aquire the lock + spinlock : + call SwitchToThread // Give other threads a chance to run if we couldn't get the lock + entry: + mov eax, 1 + xchg eax, fs_lock + test eax, eax + jnz spinlock + + // Restore registers and return + popad + popfd ret } } @@ -386,14 +386,14 @@ __declspec(naked) void EmuFS_MovFs00Esp() { // Note : eax must be preserved here, hence the push/pop __asm - { + { pushfd call EmuFS_RefreshKPCR - push eax - mov eax, fs : [TIB_ArbitraryDataSlot] - mov [eax], esp + push eax + mov eax, fs : [TIB_ArbitraryDataSlot] + mov [eax], esp add [eax], 12 // account for esp changes from pushed registers and return address - pop eax + pop eax popfd ret } @@ -534,7 +534,7 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData) } /* + HACK: extra safety padding 0x100 */ - pNewTLS = (void*)g_VMManager.AllocateZeroed(dwCopySize + dwZeroSize + 0x100 + 0xC); + pNewTLS = (void*)g_VMManager.AllocateZeroed(dwCopySize + dwZeroSize + 0x100 + 0xC); /* Skip the first 12 bytes so that TLSData will be 16 byte aligned (addr returned by AllocateZeroed is 4K aligned) */ pNewTLS = (uint8_t*)pNewTLS + 12; @@ -601,11 +601,11 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData) // Fixup the TIB self pointer : NewPcr->NtTib.Self = XbTib; // Set the stack base - TODO : Verify this, doesn't look right? - NewPcr->NtTib.StackBase = pNewTLS; - - // Write the Xbox stack base to the Host, allows ConvertThreadToFiber to work correctly - // Test case: DOA3 - __writefsdword(TIB_StackBase, (DWORD)NewPcr->NtTib.StackBase); + NewPcr->NtTib.StackBase = pNewTLS; + + // Write the Xbox stack base to the Host, allows ConvertThreadToFiber to work correctly + // Test case: DOA3 + __writefsdword(TIB_StackBase, (DWORD)NewPcr->NtTib.StackBase); __writefsdword(TIB_StackLimit, (DWORD)NewPcr->NtTib.StackLimit); } @@ -631,19 +631,19 @@ void EmuGenerateFS(Xbe::TLS *pTLS, void *pTLSData) EThread->Tcb.TlsData = pNewTLS; EThread->UniqueThread = GetCurrentThreadId(); // Set PrcbData.CurrentThread - Prcb->CurrentThread = (xboxkrnl::KTHREAD*)EThread; - // Initialize the thread header and its wait list - Prcb->CurrentThread->Header.Type = xboxkrnl::ThreadObject; - Prcb->CurrentThread->Header.Size = sizeof(xboxkrnl::KTHREAD) / sizeof(xboxkrnl::LONG); - InitializeListHead(&Prcb->CurrentThread->Header.WaitListHead); - // Also initialize the timer associated with the thread - xboxkrnl::KeInitializeTimer(&Prcb->CurrentThread->Timer); - xboxkrnl::PKWAIT_BLOCK WaitBlock = &Prcb->CurrentThread->TimerWaitBlock; - WaitBlock->Object = &Prcb->CurrentThread->Timer; - WaitBlock->WaitKey = (xboxkrnl::CSHORT)STATUS_TIMEOUT; - WaitBlock->WaitType = xboxkrnl::WaitAny; - WaitBlock->Thread = Prcb->CurrentThread; - WaitBlock->WaitListEntry.Flink = &Prcb->CurrentThread->Timer.Header.WaitListHead; + Prcb->CurrentThread = (xboxkrnl::KTHREAD*)EThread; + // Initialize the thread header and its wait list + Prcb->CurrentThread->Header.Type = xboxkrnl::ThreadObject; + Prcb->CurrentThread->Header.Size = sizeof(xboxkrnl::KTHREAD) / sizeof(xboxkrnl::LONG); + InitializeListHead(&Prcb->CurrentThread->Header.WaitListHead); + // Also initialize the timer associated with the thread + xboxkrnl::KeInitializeTimer(&Prcb->CurrentThread->Timer); + xboxkrnl::PKWAIT_BLOCK WaitBlock = &Prcb->CurrentThread->TimerWaitBlock; + WaitBlock->Object = &Prcb->CurrentThread->Timer; + WaitBlock->WaitKey = (xboxkrnl::CSHORT)STATUS_TIMEOUT; + WaitBlock->WaitType = xboxkrnl::WaitAny; + WaitBlock->Thread = Prcb->CurrentThread; + WaitBlock->WaitListEntry.Flink = &Prcb->CurrentThread->Timer.Header.WaitListHead; WaitBlock->WaitListEntry.Blink = &Prcb->CurrentThread->Timer.Header.WaitListHead; } diff --git a/src/core/kernel/support/EmuFile.cpp b/src/core/kernel/support/EmuFile.cpp index 9a69ff75b..d6d91d09f 100644 --- a/src/core/kernel/support/EmuFile.cpp +++ b/src/core/kernel/support/EmuFile.cpp @@ -1,1969 +1,1969 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::FILE - -#include "EmuFile.h" -#include -#include -#include -#include -#include -#include -#pragma warning(disable:4005) // Ignore redefined status values -#include -#pragma warning(default:4005) -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\memory-manager\VMManager.h" -#include "Logging.h" - -#include - -// Default Xbox Partition Table -#define PE_PARTFLAGS_IN_USE 0x80000000 -#define XBOX_SWAPPART1_LBA_START 0x400 -#define XBOX_SWAPPART_LBA_SIZE 0x177000 -#define XBOX_SWAPPART2_LBA_START (XBOX_SWAPPART1_LBA_START + XBOX_SWAPPART_LBA_SIZE) -#define XBOX_SWAPPART3_LBA_START (XBOX_SWAPPART2_LBA_START + XBOX_SWAPPART_LBA_SIZE) - -#define XBOX_SYSPART_LBA_START (XBOX_SWAPPART3_LBA_START + XBOX_SWAPPART_LBA_SIZE) -#define XBOX_SYSPART_LBA_SIZE 0xfa000 - -#define XBOX_MUSICPART_LBA_START (XBOX_SYSPART_LBA_START + XBOX_SYSPART_LBA_SIZE) -#define XBOX_MUSICPART_LBA_SIZE 0x9896b0 - -XboxPartitionTable BackupPartTbl = -{ - {'*', '*', '*', '*', 'P', 'A', 'R', 'T', 'I', 'N', 'F', 'O', '*', '*', '*', '*'}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - { - {{'X', 'B', 'O', 'X', ' ', 'S', 'H', 'E', 'L', 'L', ' ', ' ', ' ', ' ', ' ', ' '}, PE_PARTFLAGS_IN_USE, XBOX_MUSICPART_LBA_START, XBOX_MUSICPART_LBA_SIZE, 0}, - {{'X', 'B', 'O', 'X', ' ', 'D', 'A', 'T', 'A', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, PE_PARTFLAGS_IN_USE, XBOX_SYSPART_LBA_START, XBOX_SYSPART_LBA_SIZE, 0}, - {{'X', 'B', 'O', 'X', ' ', 'G', 'A', 'M', 'E', ' ', 'S', 'W', 'A', 'P', ' ', '1'}, PE_PARTFLAGS_IN_USE, XBOX_SWAPPART1_LBA_START, XBOX_SWAPPART_LBA_SIZE, 0}, - {{'X', 'B', 'O', 'X', ' ', 'G', 'A', 'M', 'E', ' ', 'S', 'W', 'A', 'P', ' ', '2'}, PE_PARTFLAGS_IN_USE, XBOX_SWAPPART2_LBA_START, XBOX_SWAPPART_LBA_SIZE, 0}, - {{'X', 'B', 'O', 'X', ' ', 'G', 'A', 'M', 'E', ' ', 'S', 'W', 'A', 'P', ' ', '3'}, PE_PARTFLAGS_IN_USE, XBOX_SWAPPART3_LBA_START, XBOX_SWAPPART_LBA_SIZE, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, - } -}; - -void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false) -{ - HANDLE hf = CreateFile(filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); - if (!hf) { - CxbxKrnlCleanup("CxbxCreatePartitionHeaderFile Failed\nUnable to create file: %s (%s)", filename.c_str()); - return; - } - - // If this is partition 0, install the partiton table - if (partition0) { - DWORD NumberOfBytesWritten = 0; - WriteFile(hf, &BackupPartTbl, sizeof(XboxPartitionTable), &NumberOfBytesWritten, 0); - } - - SetFilePointer(hf, 512 * ONE_KB, 0, FILE_BEGIN); - SetEndOfFile(hf); - CloseHandle(hf); -} - -XboxPartitionTable CxbxGetPartitionTable() -{ - XboxPartitionTable table; - FILE* fp = fopen((CxbxBasePath + "Partition0.bin").c_str(), "rb"); - if (fp == nullptr) { - CxbxKrnlCleanup("CxbxGetPartitionTable Failed:\nUnable to open file: %s", (CxbxBasePath + "Partition0.bin").c_str()); - } - - fread(&table, sizeof(XboxPartitionTable), 1, fp); - fclose(fp); - - // If the partition table is not valid, format it - // This allows recovery from corrupted partition tables - // Or invalid partition tables left behind from previous versions - // of Cxbx-Reloaded - if (memcmp(table.Magic, BackupPartTbl.Magic, 16) != 0) { - DeleteFile((CxbxBasePath + "Partition0.bin").c_str()); - CxbxCreatePartitionHeaderFile(CxbxBasePath + "Partition0.bin", true); - memcpy(&table, &BackupPartTbl, sizeof(XboxPartitionTable)); - } - - return table; -} - -FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber) -{ - FATX_SUPERBLOCK superblock; - std::stringstream ss; - - ss << CxbxBasePath << "Partition" << partitionNumber << ".bin"; - FILE* fp = fopen(ss.str().c_str(), "rb"); - fread(&superblock, sizeof(FATX_SUPERBLOCK), 1, fp); - fclose(fp); - return superblock; -} - -int CxbxGetPartitionNumberFromHandle(HANDLE hFile) -{ - // Get which partition number is being accessed, by parsing the filename and extracting the last portion - char buffer[MAX_PATH] = {0}; - if (!GetFinalPathNameByHandle(hFile, buffer, MAX_PATH, VOLUME_NAME_DOS)) { - CxbxKrnlCleanup("CxbxGetPartitionNumberFromHandle Failed:\nUnable to determine path for HANDLE 0x%08X", hFile); - } - - std::string bufferString(buffer); - std::string partitionString = "\\Partition"; - std::string partitionNumberString = bufferString.substr(bufferString.find(partitionString) + partitionString.length(), 1); - - // atoi returns 0 on non-numeric characters, so we don't need to error check here - return atoi(partitionNumberString.c_str()); -} - -std::string CxbxGetPartitionDataPathFromHandle(HANDLE hFile) -{ - // Get which partition number is being accessed, by parsing the filename and extracting the last portion - char buffer[MAX_PATH] = {0}; - if (!GetFinalPathNameByHandle(hFile, buffer, MAX_PATH, VOLUME_NAME_DOS)) { - CxbxKrnlCleanup("CxbxGetPartitionDataPathFromHandle Failed:\nUnable to determine path for HANDLE 0x%08X", hFile); - } - - std::string bufferString(buffer); - std::string partitionString = "\\Partition"; - std::string partitionPath = bufferString.substr(0, bufferString.find(partitionString) + partitionString.length() + 1); - return partitionPath; -} - -void CxbxFormatPartitionByHandle(HANDLE hFile) -{ - std::string partitionPath = CxbxGetPartitionDataPathFromHandle(hFile); - - // Sanity check, make sure we are actually deleting something within the Cxbx-Reloaded folder - if (partitionPath.find("Cxbx-Reloaded") == std::string::npos) { - EmuLog(LOG_LEVEL::WARNING, "Attempting to format a path that is not within a Cxbx-Reloaded data folder... Ignoring!\n"); - return; - } - - - // Format the partition, by iterating through the contents and removing all files/folders within - // Previously, we deleted and re-created the folder, but that caused permission issues for some users - try - { - for (auto& directoryEntry : std::filesystem::recursive_directory_iterator(partitionPath)) { - std::filesystem::remove_all(directoryEntry); - } - } - catch (std::filesystem::filesystem_error fsException) - { - printf("std::filesystem failed with message: %s\n", fsException.what()); - } - - - printf("Formatted EmuDisk Partition%d\n", CxbxGetPartitionNumberFromHandle(hFile)); -} - -const std::string MediaBoardRomFile = "Chihiro\\fpr21042_m29w160et.bin"; -const std::string DrivePrefix = "\\??\\"; -const std::string DriveSerial = DrivePrefix + "serial:"; -const std::string DriveCdRom0 = DrivePrefix + "CdRom0:"; // CD-ROM device -const std::string DriveMbfs = "mbfs:"; // media board's file system area device -const std::string DriveMbcom = "mbcom:"; // media board's communication area device -const std::string DriveMbrom0 = "mbrom0:"; // media board's boot ROM device (first image) -const std::string DriveMbrom1 = "mbrom1:"; // media board's boot ROM device (second image) -const std::string DriveA = DrivePrefix + "A:"; // A: could be CDROM -const std::string DriveC = DrivePrefix + "C:"; // C: is HDD0 -const std::string DriveD = DrivePrefix + "D:"; // D: is DVD Player -const std::string DriveE = DrivePrefix + "E:"; -const std::string DriveF = DrivePrefix + "F:"; -const std::string DriveS = DrivePrefix + "S:"; -const std::string DriveT = DrivePrefix + "T:"; // T: is Title persistent data region -const std::string DriveU = DrivePrefix + "U:"; // U: is User persistent data region -const std::string DriveV = DrivePrefix + "V:"; -const std::string DriveW = DrivePrefix + "W:"; -const std::string DriveX = DrivePrefix + "X:"; -const std::string DriveY = DrivePrefix + "Y:"; // Y: is Dashboard volume (contains "xboxdash.xbe" and "XDASH" folder + contents) -const std::string DriveZ = DrivePrefix + "Z:"; // Z: is Title utility data region -const std::string DevicePrefix = "\\Device"; -const std::string DeviceCdrom0 = DevicePrefix + "\\CdRom0"; -const std::string DeviceHarddisk0 = DevicePrefix + "\\Harddisk0"; -const std::string DeviceHarddisk0PartitionPrefix = DevicePrefix + "\\Harddisk0\\partition"; -const std::string DeviceHarddisk0Partition0 = DeviceHarddisk0PartitionPrefix + "0"; // Contains raw config sectors (like XBOX_REFURB_INFO) + entire hard disk -const std::string DeviceHarddisk0Partition1 = DeviceHarddisk0PartitionPrefix + "1"; // Data partition. Contains TDATA and UDATA folders. -const std::string DeviceHarddisk0Partition2 = DeviceHarddisk0PartitionPrefix + "2"; // Shell partition. Contains Dashboard (cpxdash.xbe, evoxdash.xbe or xboxdash.xbe) -const std::string DeviceHarddisk0Partition3 = DeviceHarddisk0PartitionPrefix + "3"; // First cache partition. Contains cache data (from here up to largest number) -const std::string DeviceHarddisk0Partition4 = DeviceHarddisk0PartitionPrefix + "4"; -const std::string DeviceHarddisk0Partition5 = DeviceHarddisk0PartitionPrefix + "5"; -const std::string DeviceHarddisk0Partition6 = DeviceHarddisk0PartitionPrefix + "6"; -const std::string DeviceHarddisk0Partition7 = DeviceHarddisk0PartitionPrefix + "7"; -const std::string DeviceHarddisk0Partition8 = DeviceHarddisk0PartitionPrefix + "8"; -const std::string DeviceHarddisk0Partition9 = DeviceHarddisk0PartitionPrefix + "9"; -const std::string DeviceHarddisk0Partition10 = DeviceHarddisk0PartitionPrefix + "10"; -const std::string DeviceHarddisk0Partition11 = DeviceHarddisk0PartitionPrefix + "11"; -const std::string DeviceHarddisk0Partition12 = DeviceHarddisk0PartitionPrefix + "12"; -const std::string DeviceHarddisk0Partition13 = DeviceHarddisk0PartitionPrefix + "13"; -const std::string DeviceHarddisk0Partition14 = DeviceHarddisk0PartitionPrefix + "14"; -const std::string DeviceHarddisk0Partition15 = DeviceHarddisk0PartitionPrefix + "15"; -const std::string DeviceHarddisk0Partition16 = DeviceHarddisk0PartitionPrefix + "16"; -const std::string DeviceHarddisk0Partition17 = DeviceHarddisk0PartitionPrefix + "17"; -const std::string DeviceHarddisk0Partition18 = DeviceHarddisk0PartitionPrefix + "18"; -const std::string DeviceHarddisk0Partition19 = DeviceHarddisk0PartitionPrefix + "19"; -const std::string DeviceHarddisk0Partition20 = DeviceHarddisk0PartitionPrefix + "20"; // 20 = Largest possible partition number -const char CxbxDefaultXbeDriveLetter = 'D'; - -int CxbxDefaultXbeDriveIndex = -1; -EmuNtSymbolicLinkObject* NtSymbolicLinkObjects[26]; -std::vector Devices; - -EmuHandle::EmuHandle(EmuNtObject* ntObject) -{ - NtObject = ntObject; -} - -NTSTATUS EmuHandle::NtClose() -{ - return NtObject->NtClose(); -} - -NTSTATUS EmuHandle::NtDuplicateObject(PHANDLE TargetHandle, DWORD Options) -{ - *TargetHandle = NtObject->NtDuplicateObject(Options)->NewHandle(); - return STATUS_SUCCESS; -} - -EmuNtObject::EmuNtObject() -{ - RefCount = 1; -} - -HANDLE EmuNtObject::NewHandle() -{ - RefCount++; - return EmuHandleToHandle(new EmuHandle(this)); -} - -NTSTATUS EmuNtObject::NtClose() -{ - if (--RefCount <= 0) { - delete this; - } - - return STATUS_SUCCESS; -} - -EmuNtObject* EmuNtObject::NtDuplicateObject(DWORD Options) -{ - RefCount++; - return this; -} - -bool IsEmuHandle(HANDLE Handle) -{ - return ((uint32_t)Handle > 0x80000000) && ((uint32_t)Handle < 0xFFFFFFFE); -} - -EmuHandle* HandleToEmuHandle(HANDLE Handle) -{ - return (EmuHandle*)((uint32_t)Handle & 0x7FFFFFFF); -} - -HANDLE EmuHandleToHandle(EmuHandle* emuHandle) -{ - return (HANDLE)((uint32_t)emuHandle | 0x80000000); -} - -std::wstring string_to_wstring(std::string const & src) -{ - std::wstring result = std::wstring(src.length(), L' '); - std::copy(src.begin(), src.end(), result.begin()); - return result; -} - -std::wstring PUNICODE_STRING_to_wstring(NtDll::PUNICODE_STRING const & src) -{ -return std::wstring(src->Buffer, src->Length / sizeof(NtDll::WCHAR)); -} - -std::string PSTRING_to_string(xboxkrnl::PSTRING const & src) -{ - return std::string(src->Buffer, src->Length); -} - -void copy_string_to_PSTRING_to(std::string const & src, const xboxkrnl::PSTRING & dest) -{ - dest->Length = (USHORT)src.size(); - memcpy(dest->Buffer, src.c_str(), src.size()); -} - -void replace_all(std::string& str, const std::string& from, const std::string& to) { - if (from.empty()) - return; - size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' - } -} - -NTSTATUS CxbxConvertFilePath( - std::string RelativeXboxPath, - OUT std::wstring &RelativeHostPath, - IN OUT NtDll::HANDLE *RootDirectory, - std::string aFileAPIName, - bool partitionHeader) -{ - std::string OriginalPath = RelativeXboxPath; - std::string RelativePath = RelativeXboxPath; - std::string XboxFullPath; - std::string HostPath; - EmuNtSymbolicLinkObject* NtSymbolicLinkObject = NULL; - - // Always trim '\??\' off : - if (RelativePath.compare(0, DrivePrefix.length(), DrivePrefix.c_str()) == 0) - RelativePath.erase(0, 4); - - // Check if we where called from a File-handling API : - if (!aFileAPIName.empty()) { - if (RelativePath.compare(DriveMbrom0) == 0 || RelativePath.compare(DriveMbrom1) == 0) { - *RootDirectory = CxbxBasePathHandle; - HostPath = CxbxBasePath; - RelativePath = MediaBoardRomFile; - } - else if (!partitionHeader) { - // Check if the path starts with a volume indicator : - if ((RelativePath.length() >= 2) && (RelativePath[1] == ':')) { - // Look up the symbolic link information using the drive letter : - NtSymbolicLinkObject = FindNtSymbolicLinkObjectByDriveLetter(RelativePath[0]); - RelativePath.erase(0, 2); // Remove 'C:' - - // If the remaining path starts with a ':', remove it (to prevent errors) : - if ((RelativePath.length() > 0) && (RelativePath[0] == ':')) - RelativePath.erase(0, 1); // xbmp needs this, as it accesses 'e::\' - } - else if (RelativePath[0] == '$') { - if (RelativePath.compare(0, 5, "$HOME") == 0) // "xbmp" needs this - { - NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); - RelativePath.erase(0, 5); // Remove '$HOME' - } - else - CxbxKrnlCleanup(("Unsupported path macro : " + OriginalPath).c_str()); - } - // Check if the path starts with a relative path indicator : - else if (RelativePath[0] == '.') {// "4x4 Evo 2" needs this - NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); - RelativePath.erase(0, 1); // Remove the '.' - } - else { - // TODO : How should we handle accesses to the serial: (?semi-)volume? - if (RelativePath.compare(0, 7, "serial:") == 0) - return STATUS_UNRECOGNIZED_VOLUME; - - // The path seems to be a device path, look it up : - NtSymbolicLinkObject = FindNtSymbolicLinkObjectByDevice(RelativePath); - // Fixup RelativePath path here - if (NtSymbolicLinkObject != NULL) - RelativePath.erase(0, NtSymbolicLinkObject->XboxSymbolicLinkPath.length()); // Remove '\Device\Harddisk0\Partition2' - } - - if (NtSymbolicLinkObject == NULL) { - // Check if the path accesses a partition from Harddisk0 : - if (_strnicmp(RelativePath.c_str(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { - XboxFullPath = RelativePath; - // Remove Harddisk0 prefix, in the hope that the remaining path might work : - RelativePath.erase(0, DeviceHarddisk0.length() + 1); - // And set Root to the folder containing the partition-folders : - *RootDirectory = CxbxBasePathHandle; - HostPath = CxbxBasePath; - } - // NOTE: RootDirectory cannot be ignored. - // Any special handling for it should be done below. - else if (*RootDirectory == nullptr) { - // Assume relative to Xbe path - NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); - } - else if (*RootDirectory == (NtDll::HANDLE)-3) { - // This is a special handle that tells the API that this is a DOS device - // We can safely remove it and forward to the Xbe directory. - // Test case GTA3 - NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); - } - else if (*RootDirectory == (NtDll::HANDLE)-4) { - // NOTE: A handle of -4 on the Xbox signifies the path should be in the BaseNamedObjects namespace. - // This handle doesn't exist on Windows, so we prefix the name instead. (note from LukeUsher) - // Handle special root directory constants - *RootDirectory = NULL; - - if (OriginalPath.size() == 0){ - RelativePath = "\\BaseNamedObjects"; - } else { - RelativePath = "\\BaseNamedObjects\\" + OriginalPath; - } - } - // else {} // NOTE: Allow RootDirectory handle to take control of relative path. - // Test-case: Turok Evolution - } - - if (NtSymbolicLinkObject != NULL) { - HostPath = NtSymbolicLinkObject->HostSymbolicLinkPath; - - XboxFullPath = NtSymbolicLinkObject->XboxSymbolicLinkPath; - - // If accessing a partition as a directly, set the root directory handle and keep relative path as is - *RootDirectory = NtSymbolicLinkObject->RootDirectoryHandle; - } - } else { - *RootDirectory = CxbxBasePathHandle; - HostPath = CxbxBasePath; - RelativePath = RelativeXboxPath.substr(DeviceHarddisk0.length()) + ".bin"; - } - - // If the remaining path starts with a '\', remove it (to prevent working in a native root) : - if ((RelativePath.length() > 0) && (RelativePath[0] == '\\')) { - RelativePath.erase(0, 1); - // And if needed, add it to the host path instead : - if (HostPath.back() != '\\') - HostPath.append(1, '\\'); - } - - // Lastly, remove any '\\' sequences in the string (this should fix the problem with Azurik game saves) - replace_all( RelativePath, "\\\\", "\\" ); - - if (g_bPrintfOn) { - EmuLog(LOG_LEVEL::DEBUG, "%s Corrected path...", aFileAPIName.c_str()); - EmuLog(LOG_LEVEL::DEBUG, " Org:\"%s\"", OriginalPath.c_str()); - if (_strnicmp(HostPath.c_str(), CxbxBasePath.c_str(), CxbxBasePath.length()) == 0) { - EmuLog(LOG_LEVEL::DEBUG, " New:\"$CxbxPath\\%s%s\"", (HostPath.substr(CxbxBasePath.length(), std::string::npos)).c_str(), RelativePath.c_str()); - } - else - EmuLog(LOG_LEVEL::DEBUG, " New:\"$XbePath\\%s\"", RelativePath.c_str()); - } - } - else - { - // For non-file API calls, prefix with '\??\' again : - RelativePath = DrivePrefix + RelativePath; - *RootDirectory = 0; - } - - // Convert the relative path to unicode - RelativeHostPath = string_to_wstring(RelativePath); - - return STATUS_SUCCESS; -} - -NTSTATUS CxbxObjectAttributesToNT( - xboxkrnl::POBJECT_ATTRIBUTES ObjectAttributes, - OUT NativeObjectAttributes& nativeObjectAttributes, - const std::string aFileAPIName, - bool partitionHeader) -{ - if (ObjectAttributes == NULL) - { - // When the pointer is nil, make sure we pass nil to Windows too : - nativeObjectAttributes.NtObjAttrPtr = nullptr; - return STATUS_SUCCESS; - } - - // Pick up the ObjectName, and let's see what to make of it : - std::string ObjectName = ""; - if (ObjectAttributes->ObjectName != NULL) { - ObjectName = PSTRING_to_string(ObjectAttributes->ObjectName); - } - std::wstring RelativeHostPath; - NtDll::HANDLE RootDirectory = ObjectAttributes->RootDirectory; - - // Is there a filename API given? - if (aFileAPIName.size() > 0) { - // Then interpret the ObjectName as a filename, and update it to host relative : - NTSTATUS result = CxbxConvertFilePath(ObjectName, /*OUT*/RelativeHostPath, /*IN OUT*/&RootDirectory, aFileAPIName, partitionHeader); - if (FAILED(result)) { - return result; - } - } - else { - // When not called from a file-handling API, just convert the ObjectName to a wide string : - RelativeHostPath = string_to_wstring(ObjectName); - } - - // Copy the wide string to the unicode string - wcscpy_s(nativeObjectAttributes.wszObjectName, RelativeHostPath.c_str()); - NtDll::RtlInitUnicodeString(&nativeObjectAttributes.NtUnicodeString, nativeObjectAttributes.wszObjectName); - // And initialize the NT ObjectAttributes with that : - InitializeObjectAttributes(&nativeObjectAttributes.NtObjAttr, &nativeObjectAttributes.NtUnicodeString, ObjectAttributes->Attributes, RootDirectory, NULL); - // ObjectAttributes are given, so make sure the pointer we're going to pass to Windows is assigned : - nativeObjectAttributes.NtObjAttrPtr = &nativeObjectAttributes.NtObjAttr; - - return STATUS_SUCCESS; -} - -int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath) -{ - for (size_t i = 0; i < Devices.size(); i++) - if (_strnicmp(XboxDevicePath, Devices[i].XboxDevicePath.c_str(), Devices[i].XboxDevicePath.length()) == 0) - return(i); - - return -1; -} - -XboxDevice *CxbxDeviceByDevicePath(const std::string XboxDevicePath) -{ - int DeviceIndex = CxbxDeviceIndexByDevicePath(XboxDevicePath.c_str()); - if (DeviceIndex >= 0) - return &Devices[DeviceIndex]; - - return nullptr; -} - -int CxbxRegisterDeviceHostPath(std::string XboxDevicePath, std::string HostDevicePath, bool IsFile) -{ - int result = -1; - NTSTATUS status = (NTSTATUS)-1; - - XboxDevice newDevice; - newDevice.XboxDevicePath = XboxDevicePath; - newDevice.HostDevicePath = HostDevicePath; - - // All HDD partitions have a .bin file to allow direct file io on the partition info - if (_strnicmp(XboxDevicePath.c_str(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { - std::string partitionHeaderPath = (HostDevicePath + ".bin").c_str(); - if (!PathFileExists(partitionHeaderPath.c_str())) { - CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0); - } - - status = STATUS_SUCCESS; - } - - // If this path is not a raw file partition, create the directory for it - if (!IsFile) { - status = SHCreateDirectoryEx(NULL, HostDevicePath.c_str(), NULL); - } - - if (status == STATUS_SUCCESS || status == ERROR_ALREADY_EXISTS) { - Devices.push_back(newDevice); - result = Devices.size() - 1; - } - - return result; -} - - -NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath) -{ - NTSTATUS result = 0; - EmuNtSymbolicLinkObject* SymbolicLinkObject = FindNtSymbolicLinkObjectByName(SymbolicLinkName); - - if (SymbolicLinkObject != NULL) - // In that case, close it (will also delete if reference count drops to zero) - SymbolicLinkObject->NtClose(); - - // Now (re)create a symbolic link object, and initialize it with the new definition : - SymbolicLinkObject = new EmuNtSymbolicLinkObject(); - result = SymbolicLinkObject->Init(SymbolicLinkName, FullPath); - - if (result != STATUS_SUCCESS) - SymbolicLinkObject->NtClose(); - - return result; -} - - -NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::string aFullPath) -{ - NTSTATUS result = STATUS_OBJECT_NAME_INVALID; - int i = 0; - int DeviceIndex = 0; - - // If aFullPath is an empty string, set it to the CD-ROM drive - // This should work for all titles, as CD-ROM is mapped to the current working directory - // This fixes the issue where titles crash after being launched from the update.xbe - if (aFullPath.length() == 0) { - aFullPath = DeviceCdrom0; - } - - DriveLetter = SymbolicLinkToDriveLetter(aSymbolicLinkName); - if (DriveLetter >= 'A' && DriveLetter <= 'Z') - { - result = STATUS_OBJECT_NAME_COLLISION; - if (FindNtSymbolicLinkObjectByDriveLetter(DriveLetter) == NULL) - { - // Look up the partition in the list of pre-registered devices : - result = STATUS_DEVICE_DOES_NOT_EXIST; // TODO : Is this the correct error? - - // If aFullPath starts with a Drive letter, find the originating path and substitute that - if (aFullPath[1] == ':' && aFullPath[0] >= 'A' && aFullPath[0] <= 'Z') { - EmuNtSymbolicLinkObject* DriveLetterLink = FindNtSymbolicLinkObjectByDriveLetter(aFullPath[0]); - if (DriveLetterLink != NULL) { - aFullPath = DriveLetterLink->XboxSymbolicLinkPath; - } - } - - // Make a distinction between Xbox paths (starting with '\Device'...) and host paths : - IsHostBasedPath = _strnicmp(aFullPath.c_str(), DevicePrefix.c_str(), DevicePrefix.length()) != 0; - if (IsHostBasedPath) - DeviceIndex = CxbxDefaultXbeDriveIndex; - else - DeviceIndex = CxbxDeviceIndexByDevicePath(aFullPath.c_str()); - - if (DeviceIndex >= 0) - { - result = STATUS_SUCCESS; - SymbolicLinkName = aSymbolicLinkName; - if (IsHostBasedPath) - { - XboxSymbolicLinkPath = ""; - HostSymbolicLinkPath = aFullPath; - } - else - { - XboxSymbolicLinkPath = aFullPath; - HostSymbolicLinkPath = Devices[DeviceIndex].HostDevicePath; - // Handle the case where a sub folder of the partition is mounted (instead of it's root) : - std::string ExtraPath = aFullPath.substr(Devices[DeviceIndex].XboxDevicePath.length(), std::string::npos); - - if (!ExtraPath.empty()) - HostSymbolicLinkPath = HostSymbolicLinkPath + ExtraPath; - } - - SHCreateDirectoryEx(NULL, HostSymbolicLinkPath.c_str(), NULL); - RootDirectoryHandle = CreateFile(HostSymbolicLinkPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (RootDirectoryHandle == INVALID_HANDLE_VALUE) - { - result = STATUS_DEVICE_DOES_NOT_EXIST; // TODO : Is this the correct error? - CxbxKrnlCleanup((std::string("Could not map ") + HostSymbolicLinkPath).c_str()); - } - else - { - NtSymbolicLinkObjects[DriveLetter - 'A'] = this; - EmuLog(LOG_LEVEL::DEBUG, "Linked \"%s\" to \"%s\" (residing at \"%s\")", aSymbolicLinkName.c_str(), aFullPath.c_str(), HostSymbolicLinkPath.c_str()); - } - } - } - } - - return result; -} - - EmuNtSymbolicLinkObject::~EmuNtSymbolicLinkObject() -{ - if (DriveLetter >= 'A' && DriveLetter <= 'Z') { - NtSymbolicLinkObjects[DriveLetter - 'A'] = NULL; - NtDll::NtClose(RootDirectoryHandle); - } -} - -char SymbolicLinkToDriveLetter(std::string SymbolicLinkName) -{ - char result = '\0'; - // SymbolicLinkName must look like this : "\??\D:" - if ((SymbolicLinkName.size() == 6) && (SymbolicLinkName[0] == '\\') && (SymbolicLinkName[1] == '?') && (SymbolicLinkName[2] == '?') && (SymbolicLinkName[3] == '\\') && (SymbolicLinkName[5] == ':')) - { - result = SymbolicLinkName[4]; - if (result >= 'A' && result <= 'Z') - return result; - - if (result >= 'a' && result <= 'z') { - return result + 'A' - 'a'; - } - } - - return NULL; -} - -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveLetter) -{ - if (DriveLetter >= 'A' && DriveLetter <= 'Z') - return NtSymbolicLinkObjects[DriveLetter - 'A']; - - if (DriveLetter >= 'a' && DriveLetter <= 'z') - return NtSymbolicLinkObjects[DriveLetter - 'a']; - - return NULL; -} - -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName) -{ - return FindNtSymbolicLinkObjectByDriveLetter(SymbolicLinkToDriveLetter(SymbolicLinkName)); -} - - -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDevice(std::string DeviceName) -{ - for (char DriveLetter = 'A'; DriveLetter <= 'Z'; DriveLetter++) - { - EmuNtSymbolicLinkObject* result = NtSymbolicLinkObjects[DriveLetter - 'A']; - if ((result != NULL) && _strnicmp(DeviceName.c_str(), result->XboxSymbolicLinkPath.c_str(), result->XboxSymbolicLinkPath.length()) == 0) - return result; - } - - return NULL; -} - - -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(const HANDLE Handle) -{ - for (char DriveLetter = 'A'; DriveLetter <= 'Z'; DriveLetter++) - { - EmuNtSymbolicLinkObject* result = NtSymbolicLinkObjects[DriveLetter - 'A']; - if ((result != NULL) && (Handle == result->RootDirectoryHandle)) - return result; - } - - return NULL; -} - - -void _CxbxPVOIDDeleter(PVOID *ptr) -{ - if (*ptr) - g_VMManager.Deallocate((VAddr)*ptr); -} - -// ---------------------------------------------------------------------------- -// Xbox to NT converters -// ---------------------------------------------------------------------------- - -NtDll::FILE_LINK_INFORMATION * _XboxToNTLinkInfo(xboxkrnl::FILE_LINK_INFORMATION *xboxLinkInfo, ULONG *Length) -{ - // Convert the path from Xbox to native - std::string originalFileName(xboxLinkInfo->FileName, xboxLinkInfo->FileNameLength); - std::wstring convertedFileName; - NtDll::HANDLE RootDirectory = nullptr; - NTSTATUS res = CxbxConvertFilePath(originalFileName, /*OUT*/convertedFileName, /*IN OUT*/&RootDirectory, "NtSetInformationFile"); - // TODO : handle if(FAILED(res)) - - // Build the native FILE_LINK_INFORMATION struct - *Length = sizeof(NtDll::FILE_LINK_INFORMATION) + convertedFileName.size() * sizeof(wchar_t); - NtDll::FILE_LINK_INFORMATION *ntLinkInfo = (NtDll::FILE_LINK_INFORMATION *) g_VMManager.AllocateZeroed(*Length); - ntLinkInfo->ReplaceIfExists = xboxLinkInfo->ReplaceIfExists; - ntLinkInfo->RootDirectory = RootDirectory; - ntLinkInfo->FileNameLength = convertedFileName.size() * sizeof(wchar_t); - wmemcpy_s(ntLinkInfo->FileName, convertedFileName.size(), convertedFileName.c_str(), convertedFileName.size()); - - return ntLinkInfo; -} - -NtDll::FILE_RENAME_INFORMATION * _XboxToNTRenameInfo(xboxkrnl::FILE_RENAME_INFORMATION *xboxRenameInfo, ULONG *Length) -{ - // Convert the path from Xbox to native - std::string originalFileName(xboxRenameInfo->FileName.Buffer, xboxRenameInfo->FileName.Length); - std::wstring convertedFileName; - NtDll::HANDLE RootDirectory = nullptr; - NTSTATUS res = CxbxConvertFilePath(originalFileName, /*OUT*/convertedFileName, /*IN OUT*/&RootDirectory, "NtSetInformationFile"); - // TODO : handle if(FAILED(res)) - - // Build the native FILE_RENAME_INFORMATION struct - *Length = sizeof(NtDll::FILE_RENAME_INFORMATION) + convertedFileName.size() * sizeof(wchar_t); - NtDll::FILE_RENAME_INFORMATION *ntRenameInfo = (NtDll::FILE_RENAME_INFORMATION *) g_VMManager.AllocateZeroed(*Length); - ntRenameInfo->ReplaceIfExists = xboxRenameInfo->ReplaceIfExists; - ntRenameInfo->RootDirectory = RootDirectory; - ntRenameInfo->FileNameLength = convertedFileName.size() * sizeof(wchar_t); - wmemcpy_s(ntRenameInfo->FileName, convertedFileName.size(), convertedFileName.c_str(), convertedFileName.size()); - - return ntRenameInfo; -} - -// ---------------------------------------------------------------------------- -// NT to Xbox converters - common functions -// ---------------------------------------------------------------------------- - -void _ConvertXboxBasicInfo(xboxkrnl::FILE_BASIC_INFORMATION *xboxBasicInfo) -{ - // Fix up attributes - xboxBasicInfo->FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS; -} - -NTSTATUS _ConvertXboxNameInfo(NtDll::FILE_NAME_INFORMATION *ntNameInfo, xboxkrnl::FILE_NAME_INFORMATION *xboxNameInfo, int convertedFileNameLength, ULONG Length) -{ - // Convert the file name to ANSI in-place - xboxNameInfo->FileNameLength = convertedFileNameLength; - - // Check if the new file name fits within the given struct size and copy as many chars as possible if not - int maxFileNameLength = Length - sizeof(xboxkrnl::FILE_NAME_INFORMATION); - size_t convertedChars; - wcstombs_s(&convertedChars, xboxNameInfo->FileName, maxFileNameLength, ntNameInfo->FileName, ntNameInfo->FileNameLength); - - // Return the appropriate result depending on whether the string was fully copied - return convertedChars == ntNameInfo->FileNameLength / sizeof(NtDll::WCHAR) - ? STATUS_SUCCESS - : STATUS_BUFFER_OVERFLOW; -} - -// ---------------------------------------------------------------------------- -// NT to Xbox converters -// ---------------------------------------------------------------------------- - -NTSTATUS _NTToXboxNetOpenInfo(NtDll::FILE_NETWORK_OPEN_INFORMATION *ntNetOpenInfo, xboxkrnl::FILE_NETWORK_OPEN_INFORMATION *xboxNetOpenInfo, ULONG Length) -{ - // Copy everything from the NT struct - memcpy_s(xboxNetOpenInfo, Length, ntNetOpenInfo, sizeof(NtDll::FILE_NETWORK_OPEN_INFORMATION)); - - // Fix up attributes - xboxNetOpenInfo->FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS; - - return STATUS_SUCCESS; -} - -NTSTATUS _NTToXboxBasicInfo(NtDll::FILE_BASIC_INFORMATION *ntBasicInfo, xboxkrnl::FILE_BASIC_INFORMATION *xboxBasicInfo, ULONG Length) -{ - // Copy everything from the NT struct - memcpy_s(xboxBasicInfo, sizeof(xboxkrnl::FILE_BASIC_INFORMATION), ntBasicInfo, sizeof(NtDll::FILE_BASIC_INFORMATION)); - - _ConvertXboxBasicInfo(xboxBasicInfo); - - return STATUS_SUCCESS; -} - -NTSTATUS _NTToXboxNameInfo(NtDll::FILE_NAME_INFORMATION *ntNameInfo, xboxkrnl::FILE_NAME_INFORMATION *xboxNameInfo, ULONG Length) -{ - // TODO: the FileName probably needs to be converted back to an Xbox path in some cases - // Determine new file name length - size_t convertedFileNameLength = ntNameInfo->FileNameLength / sizeof(NtDll::WCHAR); - - // Clean up the Xbox FILE_NAME_INFORMATION struct - ZeroMemory(xboxNameInfo, Length); - - // Convert file name and return the result - return _ConvertXboxNameInfo(ntNameInfo, xboxNameInfo, convertedFileNameLength, Length); -} - -NTSTATUS _NTToXboxAllInfo(NtDll::FILE_ALL_INFORMATION *ntAllInfo, xboxkrnl::FILE_ALL_INFORMATION *xboxAllInfo, ULONG Length) -{ - // TODO: the FileName probably needs to be converted back to an Xbox path in some cases - // Determine new file name length - size_t convertedFileNameLength = ntAllInfo->NameInformation.FileNameLength / sizeof(NtDll::WCHAR); - - // Copy everything from the NT struct - memcpy_s(xboxAllInfo, Length, ntAllInfo, sizeof(NtDll::FILE_ALL_INFORMATION)); - - // Convert NT structs to Xbox where needed and return the result - _ConvertXboxBasicInfo(&xboxAllInfo->BasicInformation); - return _ConvertXboxNameInfo(&ntAllInfo->NameInformation, &xboxAllInfo->NameInformation, convertedFileNameLength, Length); -} - -// ---------------------------------------------------------------------------- -// File information struct converters -// ---------------------------------------------------------------------------- - -PVOID _XboxToNTFileInformation -( - IN PVOID xboxFileInformation, - IN ULONG FileInformationClass, - OUT ULONG *Length -) -{ - // The following classes of file information structs are identical between platforms: - // FileBasicInformation - // FileDispositionInformation - // FileEndOfFileInformation - // FileLinkInformation - // FilePositionInformation - - PVOID result = NULL; - - switch (FileInformationClass) - { - case xboxkrnl::FileLinkInformation: - { - xboxkrnl::FILE_LINK_INFORMATION *xboxLinkInfo = reinterpret_cast(xboxFileInformation); - result = _XboxToNTLinkInfo(xboxLinkInfo, Length); - break; - } - case xboxkrnl::FileRenameInformation: - { - xboxkrnl::FILE_RENAME_INFORMATION *xboxRenameInfo = reinterpret_cast(xboxFileInformation); - result = _XboxToNTRenameInfo(xboxRenameInfo, Length); - break; - } - default: - { - result = NULL; - break; - } - } - - return result; -} - -NTSTATUS NTToXboxFileInformation -( - IN PVOID nativeFileInformation, - OUT PVOID xboxFileInformation, - IN ULONG FileInformationClass, - IN ULONG Length -) -{ - // The following classes of file information structs are identical between platforms: - // FileAccessInformation - // FileAlignmentInformation - // FileEaInformation - // FileInternalInformation - // FileModeInformation - // FilePositionInformation - // FileStandardInformation - // FileReparsePointInformation - - NTSTATUS result = STATUS_SUCCESS; - - switch (FileInformationClass) - { - case NtDll::FileAllInformation: - { - NtDll::FILE_ALL_INFORMATION *ntAllInfo = reinterpret_cast(nativeFileInformation); - xboxkrnl::FILE_ALL_INFORMATION *xboxAllInfo = reinterpret_cast(xboxFileInformation); - result = _NTToXboxAllInfo(ntAllInfo, xboxAllInfo, Length); - break; - } - case NtDll::FileBasicInformation: - { - NtDll::FILE_BASIC_INFORMATION *ntBasicInfo = reinterpret_cast(nativeFileInformation); - xboxkrnl::FILE_BASIC_INFORMATION *xboxBasicInfo = reinterpret_cast(xboxFileInformation); - result = _NTToXboxBasicInfo(ntBasicInfo, xboxBasicInfo, Length); - break; - } - case NtDll::FileNameInformation: - { - NtDll::FILE_NAME_INFORMATION *ntNameInfo = reinterpret_cast(nativeFileInformation); - xboxkrnl::FILE_NAME_INFORMATION *xboxNameInfo = reinterpret_cast(xboxFileInformation); - result = _NTToXboxNameInfo(ntNameInfo, xboxNameInfo, Length); - break; - } - case NtDll::FileNetworkOpenInformation: - { - NtDll::FILE_NETWORK_OPEN_INFORMATION *ntNetOpenInfo = reinterpret_cast(nativeFileInformation); - xboxkrnl::FILE_NETWORK_OPEN_INFORMATION *xboxNetOpenInfo = reinterpret_cast(xboxFileInformation); - result = _NTToXboxNetOpenInfo(ntNetOpenInfo, xboxNetOpenInfo, Length); - break; - } - case NtDll::FileBothDirectoryInformation: - { - // TODO: handle differences - // - Xbox reuses the FILE_DIRECTORY_INFORMATION struct, which is mostly identical to FILE_BOTH_DIR_INFORMATION - // - NextEntryOffset might be a problem - // - NT has extra fields before FileName: - // - ULONG EaSize - // - CCHAR ShortNameLength - // - WCHAR ShortName[12] - // - FileName on Xbox uses single-byte chars, NT uses wide chars - - //break; - } - case NtDll::FileDirectoryInformation: - { - // TODO: handle differences - // - NextEntryOffset might be a problem - // - FileName on Xbox uses single-byte chars, NT uses wide chars - - //break; - } - case NtDll::FileFullDirectoryInformation: - { - // TODO: handle differences - // - Xbox reuses the FILE_DIRECTORY_INFORMATION struct, which is mostly identical to FILE_FULL_DIR_INFORMATION - // - NextEntryOffset might be a problem - // - NT has one extra field before FileName: - // - ULONG EaSize - // - FileName on Xbox uses single-byte chars, NT uses wide chars - - //break; - } - case NtDll::FileNamesInformation: - { - // TODO: handle differences - // - NextEntryOffset might be a problem - // - FileName on Xbox uses single-byte chars, NT uses wide chars - - //break; - } - case NtDll::FileObjectIdInformation: - { - // TODO: handle differences - // - The LONGLONG FileReference field from NT can be ignored - // - ExtendedInfo is an union on NT, but is otherwise identical - - //break; - } - default: - { - // No differences between structs; a simple copy should suffice - memcpy_s(xboxFileInformation, Length, nativeFileInformation, Length); - result = STATUS_SUCCESS; - break; - } - } - - return result; -} - -// TODO: FS_INFORMATION_CLASS and its related structs most likely need to be converted too - -// TODO : Move to a better suited file -/* TODO : Also, fix C2593: "'operator <<' is ambiguous" for this -std::ostream& operator<<(std::ostream& os, const NtDll::NTSTATUS& value) -{ - os << hex4((uint32_t)value) << " = " << NtStatusToString(value); - - return os; -} -*/ - -// TODO : Create (and use) an Xbox version of this too -CHAR* NtStatusToString(IN NTSTATUS Status) -{ -#define _CASE(s) case s: return #s; - - switch (Status) - { - // Note : Keep all cases sorted, for easier maintenance - _CASE(DBG_APP_NOT_IDLE); - _CASE(DBG_CONTINUE); - _CASE(DBG_CONTROL_BREAK); - _CASE(DBG_CONTROL_C); - _CASE(DBG_EXCEPTION_HANDLED); - _CASE(DBG_EXCEPTION_NOT_HANDLED); - _CASE(DBG_NO_STATE_CHANGE); - _CASE(DBG_PRINTEXCEPTION_C); - _CASE(DBG_REPLY_LATER); - _CASE(DBG_RIPEXCEPTION); - _CASE(DBG_TERMINATE_PROCESS); - _CASE(DBG_TERMINATE_THREAD); - _CASE(DBG_UNABLE_TO_PROVIDE_HANDLE); - _CASE(EPT_NT_CANT_CREATE); - _CASE(EPT_NT_CANT_PERFORM_OP); - _CASE(EPT_NT_INVALID_ENTRY); - _CASE(EPT_NT_NOT_REGISTERED); - _CASE(RPC_NT_ADDRESS_ERROR); - _CASE(RPC_NT_ALREADY_LISTENING); - _CASE(RPC_NT_ALREADY_REGISTERED); - _CASE(RPC_NT_BAD_STUB_DATA); - _CASE(RPC_NT_BINDING_HAS_NO_AUTH); - _CASE(RPC_NT_BINDING_INCOMPLETE); - _CASE(RPC_NT_BYTE_COUNT_TOO_SMALL); - _CASE(RPC_NT_CALL_CANCELLED); - _CASE(RPC_NT_CALL_FAILED); - _CASE(RPC_NT_CALL_FAILED_DNE); - _CASE(RPC_NT_CALL_IN_PROGRESS); - _CASE(RPC_NT_CANNOT_SUPPORT); - _CASE(RPC_NT_CANT_CREATE_ENDPOINT); - _CASE(RPC_NT_COMM_FAILURE); - _CASE(RPC_NT_DUPLICATE_ENDPOINT); - _CASE(RPC_NT_ENTRY_ALREADY_EXISTS); - _CASE(RPC_NT_ENTRY_NOT_FOUND); - _CASE(RPC_NT_ENUM_VALUE_OUT_OF_RANGE); - _CASE(RPC_NT_FP_DIV_ZERO); - _CASE(RPC_NT_FP_OVERFLOW); - _CASE(RPC_NT_FP_UNDERFLOW); - _CASE(RPC_NT_GROUP_MEMBER_NOT_FOUND); - _CASE(RPC_NT_INCOMPLETE_NAME); - _CASE(RPC_NT_INTERFACE_NOT_FOUND); - _CASE(RPC_NT_INTERNAL_ERROR); - _CASE(RPC_NT_INVALID_ASYNC_CALL); - _CASE(RPC_NT_INVALID_ASYNC_HANDLE); - _CASE(RPC_NT_INVALID_AUTH_IDENTITY); - _CASE(RPC_NT_INVALID_BINDING); - _CASE(RPC_NT_INVALID_BOUND); - _CASE(RPC_NT_INVALID_ENDPOINT_FORMAT); - _CASE(RPC_NT_INVALID_ES_ACTION); - _CASE(RPC_NT_INVALID_NAF_ID); - _CASE(RPC_NT_INVALID_NAME_SYNTAX); - _CASE(RPC_NT_INVALID_NETWORK_OPTIONS); - _CASE(RPC_NT_INVALID_NET_ADDR); - _CASE(RPC_NT_INVALID_OBJECT); - _CASE(RPC_NT_INVALID_PIPE_OBJECT); - _CASE(RPC_NT_INVALID_PIPE_OPERATION); - _CASE(RPC_NT_INVALID_RPC_PROTSEQ); - _CASE(RPC_NT_INVALID_STRING_BINDING); - _CASE(RPC_NT_INVALID_STRING_UUID); - _CASE(RPC_NT_INVALID_TAG); - _CASE(RPC_NT_INVALID_TIMEOUT); - _CASE(RPC_NT_INVALID_VERS_OPTION); - _CASE(RPC_NT_MAX_CALLS_TOO_SMALL); - _CASE(RPC_NT_NAME_SERVICE_UNAVAILABLE); - _CASE(RPC_NT_NOTHING_TO_EXPORT); - _CASE(RPC_NT_NOT_ALL_OBJS_UNEXPORTED); - _CASE(RPC_NT_NOT_CANCELLED); - _CASE(RPC_NT_NOT_LISTENING); - _CASE(RPC_NT_NOT_RPC_ERROR); - _CASE(RPC_NT_NO_BINDINGS); - _CASE(RPC_NT_NO_CALL_ACTIVE); - _CASE(RPC_NT_NO_CONTEXT_AVAILABLE); - _CASE(RPC_NT_NO_ENDPOINT_FOUND); - _CASE(RPC_NT_NO_ENTRY_NAME); - _CASE(RPC_NT_NO_INTERFACES); - _CASE(RPC_NT_NO_MORE_BINDINGS); - _CASE(RPC_NT_NO_MORE_ENTRIES); - _CASE(RPC_NT_NO_MORE_MEMBERS); - _CASE(RPC_NT_NO_PRINC_NAME); - _CASE(RPC_NT_NO_PROTSEQS); - _CASE(RPC_NT_NO_PROTSEQS_REGISTERED); - _CASE(RPC_NT_NULL_REF_POINTER); - _CASE(RPC_NT_OBJECT_NOT_FOUND); - _CASE(RPC_NT_OUT_OF_RESOURCES); - _CASE(RPC_NT_PIPE_CLOSED); - _CASE(RPC_NT_PIPE_DISCIPLINE_ERROR); - _CASE(RPC_NT_PIPE_EMPTY); - _CASE(RPC_NT_PROCNUM_OUT_OF_RANGE); - _CASE(RPC_NT_PROTOCOL_ERROR); - _CASE(RPC_NT_PROTSEQ_NOT_FOUND); - _CASE(RPC_NT_PROTSEQ_NOT_SUPPORTED); - _CASE(RPC_NT_SEC_PKG_ERROR); - _CASE(RPC_NT_SEND_INCOMPLETE); - _CASE(RPC_NT_SERVER_TOO_BUSY); - _CASE(RPC_NT_SERVER_UNAVAILABLE); - _CASE(RPC_NT_SS_CANNOT_GET_CALL_HANDLE); - _CASE(RPC_NT_SS_CHAR_TRANS_OPEN_FAIL); - _CASE(RPC_NT_SS_CHAR_TRANS_SHORT_FILE); - _CASE(RPC_NT_SS_CONTEXT_DAMAGED); - _CASE(RPC_NT_SS_CONTEXT_MISMATCH); - _CASE(RPC_NT_SS_HANDLES_MISMATCH); - _CASE(RPC_NT_SS_IN_NULL_CONTEXT); - _CASE(RPC_NT_STRING_TOO_LONG); - _CASE(RPC_NT_TYPE_ALREADY_REGISTERED); - _CASE(RPC_NT_UNKNOWN_AUTHN_LEVEL); - _CASE(RPC_NT_UNKNOWN_AUTHN_SERVICE); - _CASE(RPC_NT_UNKNOWN_AUTHN_TYPE); - _CASE(RPC_NT_UNKNOWN_AUTHZ_SERVICE); - _CASE(RPC_NT_UNKNOWN_IF); - _CASE(RPC_NT_UNKNOWN_MGR_TYPE); - _CASE(RPC_NT_UNSUPPORTED_AUTHN_LEVEL); - _CASE(RPC_NT_UNSUPPORTED_NAME_SYNTAX); - _CASE(RPC_NT_UNSUPPORTED_TRANS_SYN); - _CASE(RPC_NT_UNSUPPORTED_TYPE); - _CASE(RPC_NT_UUID_LOCAL_ONLY); - _CASE(RPC_NT_UUID_NO_ADDRESS); - _CASE(RPC_NT_WRONG_ES_VERSION); - _CASE(RPC_NT_WRONG_KIND_OF_BINDING); - _CASE(RPC_NT_WRONG_PIPE_VERSION); - _CASE(RPC_NT_WRONG_STUB_VERSION); - _CASE(RPC_NT_ZERO_DIVIDE); - _CASE(STATUS_ABANDONED_WAIT_0); - _CASE(STATUS_ABANDONED_WAIT_63); - _CASE(STATUS_ABIOS_INVALID_COMMAND); - _CASE(STATUS_ABIOS_INVALID_LID); - _CASE(STATUS_ABIOS_INVALID_SELECTOR); - _CASE(STATUS_ABIOS_LID_ALREADY_OWNED); - _CASE(STATUS_ABIOS_LID_NOT_EXIST); - _CASE(STATUS_ABIOS_NOT_LID_OWNER); - _CASE(STATUS_ABIOS_NOT_PRESENT); - _CASE(STATUS_ABIOS_SELECTOR_NOT_AVAILABLE); - _CASE(STATUS_ACCESS_DENIED); - _CASE(STATUS_ACCESS_VIOLATION); - _CASE(STATUS_ACCOUNT_DISABLED); - _CASE(STATUS_ACCOUNT_EXPIRED); - _CASE(STATUS_ACCOUNT_LOCKED_OUT); - _CASE(STATUS_ACCOUNT_RESTRICTION); - _CASE(STATUS_ACPI_ACQUIRE_GLOBAL_LOCK); - _CASE(STATUS_ACPI_ADDRESS_NOT_MAPPED); - _CASE(STATUS_ACPI_ALREADY_INITIALIZED); - _CASE(STATUS_ACPI_ASSERT_FAILED); - _CASE(STATUS_ACPI_FATAL); - _CASE(STATUS_ACPI_HANDLER_COLLISION); - _CASE(STATUS_ACPI_INCORRECT_ARGUMENT_COUNT); - _CASE(STATUS_ACPI_INVALID_ACCESS_SIZE); - _CASE(STATUS_ACPI_INVALID_ARGTYPE); - _CASE(STATUS_ACPI_INVALID_ARGUMENT); - _CASE(STATUS_ACPI_INVALID_DATA); - _CASE(STATUS_ACPI_INVALID_EVENTTYPE); - _CASE(STATUS_ACPI_INVALID_INDEX); - _CASE(STATUS_ACPI_INVALID_MUTEX_LEVEL); - _CASE(STATUS_ACPI_INVALID_OBJTYPE); - _CASE(STATUS_ACPI_INVALID_OPCODE); - _CASE(STATUS_ACPI_INVALID_REGION); - _CASE(STATUS_ACPI_INVALID_SUPERNAME); - _CASE(STATUS_ACPI_INVALID_TABLE); - _CASE(STATUS_ACPI_INVALID_TARGETTYPE); - _CASE(STATUS_ACPI_MUTEX_NOT_OWNED); - _CASE(STATUS_ACPI_MUTEX_NOT_OWNER); - _CASE(STATUS_ACPI_NOT_INITIALIZED); - _CASE(STATUS_ACPI_POWER_REQUEST_FAILED); - _CASE(STATUS_ACPI_REG_HANDLER_FAILED); - _CASE(STATUS_ACPI_RS_ACCESS); - _CASE(STATUS_ACPI_STACK_OVERFLOW); - _CASE(STATUS_ADAPTER_HARDWARE_ERROR); - _CASE(STATUS_ADDRESS_ALREADY_ASSOCIATED); - _CASE(STATUS_ADDRESS_ALREADY_EXISTS); - _CASE(STATUS_ADDRESS_CLOSED); - _CASE(STATUS_ADDRESS_NOT_ASSOCIATED); - _CASE(STATUS_AGENTS_EXHAUSTED); - _CASE(STATUS_ALERTED); - _CASE(STATUS_ALIAS_EXISTS); - _CASE(STATUS_ALLOCATE_BUCKET); - _CASE(STATUS_ALLOTTED_SPACE_EXCEEDED); - _CASE(STATUS_ALREADY_COMMITTED); - _CASE(STATUS_ALREADY_DISCONNECTED); - _CASE(STATUS_ALREADY_WIN32); - _CASE(STATUS_APP_INIT_FAILURE); - _CASE(STATUS_ARBITRATION_UNHANDLED); - _CASE(STATUS_ARRAY_BOUNDS_EXCEEDED); - _CASE(STATUS_AUDIT_FAILED); - _CASE(STATUS_BACKUP_CONTROLLER); - _CASE(STATUS_BAD_COMPRESSION_BUFFER); - _CASE(STATUS_BAD_CURRENT_DIRECTORY); - _CASE(STATUS_BAD_DESCRIPTOR_FORMAT); - _CASE(STATUS_BAD_DEVICE_TYPE); - _CASE(STATUS_BAD_DLL_ENTRYPOINT); - _CASE(STATUS_BAD_FUNCTION_TABLE); - _CASE(STATUS_BAD_IMPERSONATION_LEVEL); - _CASE(STATUS_BAD_INHERITANCE_ACL); - _CASE(STATUS_BAD_INITIAL_PC); - _CASE(STATUS_BAD_INITIAL_STACK); - _CASE(STATUS_BAD_LOGON_SESSION_STATE); - _CASE(STATUS_BAD_MASTER_BOOT_RECORD); - _CASE(STATUS_BAD_NETWORK_NAME); - _CASE(STATUS_BAD_NETWORK_PATH); - _CASE(STATUS_BAD_REMOTE_ADAPTER); - _CASE(STATUS_BAD_SERVICE_ENTRYPOINT); - _CASE(STATUS_BAD_STACK); - _CASE(STATUS_BAD_TOKEN_TYPE); - _CASE(STATUS_BAD_VALIDATION_CLASS); - _CASE(STATUS_BAD_WORKING_SET_LIMIT); - _CASE(STATUS_BEGINNING_OF_MEDIA); - _CASE(STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT); - _CASE(STATUS_BREAKPOINT); - _CASE(STATUS_BUFFER_ALL_ZEROS); - _CASE(STATUS_BUFFER_OVERFLOW); - _CASE(STATUS_BUFFER_TOO_SMALL); - _CASE(STATUS_BUS_RESET); - _CASE(STATUS_CACHE_PAGE_LOCKED); - _CASE(STATUS_CANCELLED); - _CASE(STATUS_CANNOT_DELETE); - _CASE(STATUS_CANNOT_IMPERSONATE); - _CASE(STATUS_CANNOT_LOAD_REGISTRY_FILE); - _CASE(STATUS_CANT_ACCESS_DOMAIN_INFO); - _CASE(STATUS_CANT_DISABLE_MANDATORY); - _CASE(STATUS_CANT_ENABLE_DENY_ONLY); - _CASE(STATUS_CANT_OPEN_ANONYMOUS); - _CASE(STATUS_CANT_TERMINATE_SELF); - _CASE(STATUS_CANT_WAIT); - _CASE(STATUS_CARDBUS_NOT_SUPPORTED); - _CASE(STATUS_CHECKING_FILE_SYSTEM); - _CASE(STATUS_CHILD_MUST_BE_VOLATILE); - _CASE(STATUS_CLIENT_SERVER_PARAMETERS_INVALID); - _CASE(STATUS_COMMITMENT_LIMIT); - _CASE(STATUS_COMMITMENT_MINIMUM); - _CASE(STATUS_CONFLICTING_ADDRESSES); - _CASE(STATUS_CONNECTION_ABORTED); - _CASE(STATUS_CONNECTION_ACTIVE); - _CASE(STATUS_CONNECTION_COUNT_LIMIT); - _CASE(STATUS_CONNECTION_DISCONNECTED); - _CASE(STATUS_CONNECTION_INVALID); - _CASE(STATUS_CONNECTION_IN_USE); - _CASE(STATUS_CONNECTION_REFUSED); - _CASE(STATUS_CONNECTION_RESET); - _CASE(STATUS_CONTROL_C_EXIT); - _CASE(STATUS_CONVERT_TO_LARGE); - _CASE(STATUS_CORRUPT_SYSTEM_FILE); - _CASE(STATUS_COULD_NOT_INTERPRET); - _CASE(STATUS_CRASH_DUMP); - _CASE(STATUS_CRC_ERROR); - _CASE(STATUS_CTL_FILE_NOT_SUPPORTED); - _CASE(STATUS_CTX_BAD_VIDEO_MODE); - _CASE(STATUS_CTX_CDM_CONNECT); - _CASE(STATUS_CTX_CDM_DISCONNECT); - _CASE(STATUS_CTX_CLIENT_LICENSE_IN_USE); - _CASE(STATUS_CTX_CLIENT_LICENSE_NOT_SET); - _CASE(STATUS_CTX_CLIENT_QUERY_TIMEOUT); - _CASE(STATUS_CTX_CLOSE_PENDING); - _CASE(STATUS_CTX_CONSOLE_CONNECT); - _CASE(STATUS_CTX_CONSOLE_DISCONNECT); - _CASE(STATUS_CTX_GRAPHICS_INVALID); - _CASE(STATUS_CTX_INVALID_MODEMNAME); - _CASE(STATUS_CTX_INVALID_PD); - _CASE(STATUS_CTX_INVALID_WD); - _CASE(STATUS_CTX_LICENSE_CLIENT_INVALID); - _CASE(STATUS_CTX_LICENSE_EXPIRED); - _CASE(STATUS_CTX_LICENSE_NOT_AVAILABLE); - _CASE(STATUS_CTX_MODEM_INF_NOT_FOUND); - _CASE(STATUS_CTX_MODEM_RESPONSE_BUSY); - _CASE(STATUS_CTX_MODEM_RESPONSE_NO_CARRIER); - _CASE(STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE); - _CASE(STATUS_CTX_MODEM_RESPONSE_TIMEOUT); - _CASE(STATUS_CTX_MODEM_RESPONSE_VOICE); - _CASE(STATUS_CTX_NOT_CONSOLE); - _CASE(STATUS_CTX_NO_OUTBUF); - _CASE(STATUS_CTX_PD_NOT_FOUND); - _CASE(STATUS_CTX_RESPONSE_ERROR); - _CASE(STATUS_CTX_SHADOW_DENIED); - _CASE(STATUS_CTX_SHADOW_DISABLED); - _CASE(STATUS_CTX_SHADOW_INVALID); - _CASE(STATUS_CTX_TD_ERROR); - _CASE(STATUS_CTX_WD_NOT_FOUND); - _CASE(STATUS_CTX_WINSTATION_ACCESS_DENIED); - _CASE(STATUS_CTX_WINSTATION_BUSY); - _CASE(STATUS_CTX_WINSTATION_NAME_COLLISION); - _CASE(STATUS_CTX_WINSTATION_NAME_INVALID); - _CASE(STATUS_CTX_WINSTATION_NOT_FOUND); - _CASE(STATUS_DATATYPE_MISALIGNMENT); - _CASE(STATUS_DATATYPE_MISALIGNMENT_ERROR); - _CASE(STATUS_DATA_ERROR); - _CASE(STATUS_DATA_LATE_ERROR); - _CASE(STATUS_DATA_NOT_ACCEPTED); - _CASE(STATUS_DATA_OVERRUN); - _CASE(STATUS_DEBUG_ATTACH_FAILED); - _CASE(STATUS_DECRYPTION_FAILED); - _CASE(STATUS_DELETE_PENDING); - _CASE(STATUS_DESTINATION_ELEMENT_FULL); - _CASE(STATUS_DEVICE_ALREADY_ATTACHED); - _CASE(STATUS_DEVICE_BUSY); - _CASE(STATUS_DEVICE_CONFIGURATION_ERROR); - _CASE(STATUS_DEVICE_DATA_ERROR); - _CASE(STATUS_DEVICE_DOES_NOT_EXIST); - _CASE(STATUS_DEVICE_DOOR_OPEN); - _CASE(STATUS_DEVICE_NOT_CONNECTED); - _CASE(STATUS_DEVICE_NOT_PARTITIONED); - _CASE(STATUS_DEVICE_NOT_READY); - _CASE(STATUS_DEVICE_OFF_LINE); - _CASE(STATUS_DEVICE_PAPER_EMPTY); - _CASE(STATUS_DEVICE_POWERED_OFF); - _CASE(STATUS_DEVICE_POWER_FAILURE); - _CASE(STATUS_DEVICE_PROTOCOL_ERROR); - _CASE(STATUS_DEVICE_REMOVED); - _CASE(STATUS_DEVICE_REQUIRES_CLEANING); - _CASE(STATUS_DFS_EXIT_PATH_FOUND); - _CASE(STATUS_DFS_UNAVAILABLE); - _CASE(STATUS_DIRECTORY_IS_A_REPARSE_POINT); - _CASE(STATUS_DIRECTORY_NOT_EMPTY); - _CASE(STATUS_DIRECTORY_SERVICE_REQUIRED); - _CASE(STATUS_DISK_CORRUPT_ERROR); - _CASE(STATUS_DISK_FULL); - _CASE(STATUS_DISK_OPERATION_FAILED); - _CASE(STATUS_DISK_RECALIBRATE_FAILED); - _CASE(STATUS_DISK_RESET_FAILED); - _CASE(STATUS_DLL_INIT_FAILED); - _CASE(STATUS_DLL_INIT_FAILED_LOGOFF); - _CASE(STATUS_DLL_NOT_FOUND); - _CASE(STATUS_DOMAIN_CONTROLLER_NOT_FOUND); - _CASE(STATUS_DOMAIN_CTRLR_CONFIG_ERROR); - _CASE(STATUS_DOMAIN_EXISTS); - _CASE(STATUS_DOMAIN_LIMIT_EXCEEDED); - _CASE(STATUS_DOMAIN_TRUST_INCONSISTENT); - _CASE(STATUS_DRIVER_CANCEL_TIMEOUT); - _CASE(STATUS_DRIVER_ENTRYPOINT_NOT_FOUND); - _CASE(STATUS_DRIVER_FAILED_SLEEP); - _CASE(STATUS_DRIVER_INTERNAL_ERROR); - _CASE(STATUS_DRIVER_ORDINAL_NOT_FOUND); - _CASE(STATUS_DRIVER_UNABLE_TO_LOAD); - _CASE(STATUS_DS_ADMIN_LIMIT_EXCEEDED); - _CASE(STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS); - _CASE(STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED); - _CASE(STATUS_DS_BUSY); - _CASE(STATUS_DS_CANT_MOD_OBJ_CLASS); - _CASE(STATUS_DS_CANT_MOD_PRIMARYGROUPID); - _CASE(STATUS_DS_CANT_ON_NON_LEAF); - _CASE(STATUS_DS_CANT_ON_RDN); - _CASE(STATUS_DS_CANT_START); - _CASE(STATUS_DS_CROSS_DOM_MOVE_FAILED); - _CASE(STATUS_DS_GC_NOT_AVAILABLE); - _CASE(STATUS_DS_GC_REQUIRED); - _CASE(STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER); - _CASE(STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER); - _CASE(STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER); - _CASE(STATUS_DS_HAVE_PRIMARY_MEMBERS); - _CASE(STATUS_DS_INCORRECT_ROLE_OWNER); - _CASE(STATUS_DS_INIT_FAILURE); - _CASE(STATUS_DS_INVALID_ATTRIBUTE_SYNTAX); - _CASE(STATUS_DS_INVALID_GROUP_TYPE); - _CASE(STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER); - _CASE(STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY); - _CASE(STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED); - _CASE(STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY); - _CASE(STATUS_DS_NO_ATTRIBUTE_OR_VALUE); - _CASE(STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS); - _CASE(STATUS_DS_NO_MORE_RIDS); - _CASE(STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN); - _CASE(STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN); - _CASE(STATUS_DS_NO_RIDS_ALLOCATED); - _CASE(STATUS_DS_OBJ_CLASS_VIOLATION); - _CASE(STATUS_DS_RIDMGR_INIT_ERROR); - _CASE(STATUS_DS_SAM_INIT_FAILURE); - _CASE(STATUS_DS_SENSITIVE_GROUP_VIOLATION); - _CASE(STATUS_DS_UNAVAILABLE); - _CASE(STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER); - _CASE(STATUS_DUPLICATE_NAME); - _CASE(STATUS_DUPLICATE_OBJECTID); - _CASE(STATUS_EAS_NOT_SUPPORTED); - _CASE(STATUS_EA_CORRUPT_ERROR); - _CASE(STATUS_EA_LIST_INCONSISTENT); - _CASE(STATUS_EA_TOO_LARGE); - _CASE(STATUS_ENCRYPTION_FAILED); - _CASE(STATUS_END_OF_FILE); - _CASE(STATUS_END_OF_MEDIA); - _CASE(STATUS_ENTRYPOINT_NOT_FOUND); - _CASE(STATUS_EOM_OVERFLOW); - _CASE(STATUS_EVALUATION_EXPIRATION); - _CASE(STATUS_EVENTLOG_CANT_START); - _CASE(STATUS_EVENTLOG_FILE_CHANGED); - _CASE(STATUS_EVENTLOG_FILE_CORRUPT); - _CASE(STATUS_EVENT_DONE); - _CASE(STATUS_EVENT_PENDING); - _CASE(STATUS_EXTRANEOUS_INFORMATION); - _CASE(STATUS_FAIL_CHECK); - _CASE(STATUS_FATAL_APP_EXIT); - _CASE(STATUS_FILEMARK_DETECTED); - _CASE(STATUS_FILES_OPEN); - _CASE(STATUS_FILE_CLOSED); - _CASE(STATUS_FILE_CORRUPT_ERROR); - _CASE(STATUS_FILE_DELETED); - _CASE(STATUS_FILE_ENCRYPTED); - _CASE(STATUS_FILE_FORCED_CLOSED); - _CASE(STATUS_FILE_INVALID); - _CASE(STATUS_FILE_IS_A_DIRECTORY); - _CASE(STATUS_FILE_IS_OFFLINE); - _CASE(STATUS_FILE_LOCK_CONFLICT); - _CASE(STATUS_FILE_NOT_ENCRYPTED); - _CASE(STATUS_FILE_RENAMED); - _CASE(STATUS_FLOAT_DENORMAL_OPERAND); - _CASE(STATUS_FLOAT_DIVIDE_BY_ZERO); - _CASE(STATUS_FLOAT_INEXACT_RESULT); - _CASE(STATUS_FLOAT_INVALID_OPERATION); - _CASE(STATUS_FLOAT_MULTIPLE_FAULTS); - _CASE(STATUS_FLOAT_MULTIPLE_TRAPS); - _CASE(STATUS_FLOAT_OVERFLOW); - _CASE(STATUS_FLOAT_STACK_CHECK); - _CASE(STATUS_FLOAT_UNDERFLOW); - _CASE(STATUS_FLOPPY_BAD_REGISTERS); - _CASE(STATUS_FLOPPY_ID_MARK_NOT_FOUND); - _CASE(STATUS_FLOPPY_UNKNOWN_ERROR); - _CASE(STATUS_FLOPPY_VOLUME); - _CASE(STATUS_FLOPPY_WRONG_CYLINDER); - _CASE(STATUS_FOUND_OUT_OF_SCOPE); - _CASE(STATUS_FREE_VM_NOT_AT_BASE); - _CASE(STATUS_FS_DRIVER_REQUIRED); - _CASE(STATUS_FT_MISSING_MEMBER); - _CASE(STATUS_FT_ORPHANING); - _CASE(STATUS_FT_READ_RECOVERY_FROM_BACKUP); - _CASE(STATUS_FT_WRITE_RECOVERY); - _CASE(STATUS_FULLSCREEN_MODE); - _CASE(STATUS_GENERIC_NOT_MAPPED); - _CASE(STATUS_GRACEFUL_DISCONNECT); - _CASE(STATUS_GROUP_EXISTS); - _CASE(STATUS_GUARD_PAGE_VIOLATION); - _CASE(STATUS_GUIDS_EXHAUSTED); - _CASE(STATUS_GUID_SUBSTITUTION_MADE); - _CASE(STATUS_HANDLES_CLOSED); - _CASE(STATUS_HANDLE_NOT_CLOSABLE); - _CASE(STATUS_HOST_UNREACHABLE); - _CASE(STATUS_ILLEGAL_CHARACTER); - _CASE(STATUS_ILLEGAL_DLL_RELOCATION); - _CASE(STATUS_ILLEGAL_ELEMENT_ADDRESS); - _CASE(STATUS_ILLEGAL_FLOAT_CONTEXT); - _CASE(STATUS_ILLEGAL_FUNCTION); - _CASE(STATUS_ILLEGAL_INSTRUCTION); - _CASE(STATUS_ILL_FORMED_PASSWORD); - _CASE(STATUS_ILL_FORMED_SERVICE_ENTRY); - _CASE(STATUS_IMAGE_ALREADY_LOADED); - _CASE(STATUS_IMAGE_CHECKSUM_MISMATCH); - _CASE(STATUS_IMAGE_MACHINE_TYPE_MISMATCH); - _CASE(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE); - _CASE(STATUS_IMAGE_MP_UP_MISMATCH); - _CASE(STATUS_IMAGE_NOT_AT_BASE); - _CASE(STATUS_INCOMPATIBLE_FILE_MAP); - _CASE(STATUS_INFO_LENGTH_MISMATCH); - _CASE(STATUS_INSTANCE_NOT_AVAILABLE); - _CASE(STATUS_INSTRUCTION_MISALIGNMENT); - _CASE(STATUS_INSUFFICIENT_LOGON_INFO); - _CASE(STATUS_INSUFFICIENT_POWER); - _CASE(STATUS_INSUFFICIENT_RESOURCES); - _CASE(STATUS_INSUFF_SERVER_RESOURCES); - _CASE(STATUS_INTEGER_DIVIDE_BY_ZERO); - _CASE(STATUS_INTEGER_OVERFLOW); - _CASE(STATUS_INTERNAL_DB_CORRUPTION); - _CASE(STATUS_INTERNAL_DB_ERROR); - _CASE(STATUS_INTERNAL_ERROR); - _CASE(STATUS_INVALID_ACCOUNT_NAME); - _CASE(STATUS_INVALID_ACL); - _CASE(STATUS_INVALID_ADDRESS); - _CASE(STATUS_INVALID_ADDRESS_COMPONENT); - _CASE(STATUS_INVALID_ADDRESS_WILDCARD); - _CASE(STATUS_INVALID_BLOCK_LENGTH); - _CASE(STATUS_INVALID_BUFFER_SIZE); - _CASE(STATUS_INVALID_CID); - _CASE(STATUS_INVALID_COMPUTER_NAME); - _CASE(STATUS_INVALID_CONNECTION); - _CASE(STATUS_INVALID_DEVICE_REQUEST); - _CASE(STATUS_INVALID_DEVICE_STATE); - _CASE(STATUS_INVALID_DISPOSITION); - _CASE(STATUS_INVALID_DOMAIN_ROLE); - _CASE(STATUS_INVALID_DOMAIN_STATE); - _CASE(STATUS_INVALID_EA_FLAG); - _CASE(STATUS_INVALID_EA_NAME); - _CASE(STATUS_INVALID_FILE_FOR_SECTION); - _CASE(STATUS_INVALID_GROUP_ATTRIBUTES); - _CASE(STATUS_INVALID_HANDLE); - _CASE(STATUS_INVALID_HW_PROFILE); - _CASE(STATUS_INVALID_ID_AUTHORITY); - _CASE(STATUS_INVALID_IMAGE_FORMAT); - _CASE(STATUS_INVALID_IMAGE_LE_FORMAT); - _CASE(STATUS_INVALID_IMAGE_NE_FORMAT); - _CASE(STATUS_INVALID_IMAGE_NOT_MZ); - _CASE(STATUS_INVALID_IMAGE_PROTECT); - _CASE(STATUS_INVALID_IMAGE_WIN_16); - _CASE(STATUS_INVALID_INFO_CLASS); - _CASE(STATUS_INVALID_LDT_DESCRIPTOR); - _CASE(STATUS_INVALID_LDT_OFFSET); - _CASE(STATUS_INVALID_LDT_SIZE); - _CASE(STATUS_INVALID_LEVEL); - _CASE(STATUS_INVALID_LOCK_SEQUENCE); - _CASE(STATUS_INVALID_LOGON_HOURS); - _CASE(STATUS_INVALID_LOGON_TYPE); - _CASE(STATUS_INVALID_MEMBER); - _CASE(STATUS_INVALID_NETWORK_RESPONSE); - _CASE(STATUS_INVALID_OPLOCK_PROTOCOL); - _CASE(STATUS_INVALID_OWNER); - _CASE(STATUS_INVALID_PAGE_PROTECTION); - _CASE(STATUS_INVALID_PARAMETER); - _CASE(STATUS_INVALID_PARAMETER_1); - _CASE(STATUS_INVALID_PARAMETER_10); - _CASE(STATUS_INVALID_PARAMETER_11); - _CASE(STATUS_INVALID_PARAMETER_12); - _CASE(STATUS_INVALID_PARAMETER_2); - _CASE(STATUS_INVALID_PARAMETER_3); - _CASE(STATUS_INVALID_PARAMETER_4); - _CASE(STATUS_INVALID_PARAMETER_5); - _CASE(STATUS_INVALID_PARAMETER_6); - _CASE(STATUS_INVALID_PARAMETER_7); - _CASE(STATUS_INVALID_PARAMETER_8); - _CASE(STATUS_INVALID_PARAMETER_9); - _CASE(STATUS_INVALID_PARAMETER_MIX); - _CASE(STATUS_INVALID_PIPE_STATE); - _CASE(STATUS_INVALID_PLUGPLAY_DEVICE_PATH); - _CASE(STATUS_INVALID_PORT_ATTRIBUTES); - _CASE(STATUS_INVALID_PORT_HANDLE); - _CASE(STATUS_INVALID_PRIMARY_GROUP); - _CASE(STATUS_INVALID_QUOTA_LOWER); - _CASE(STATUS_INVALID_READ_MODE); - _CASE(STATUS_INVALID_SECURITY_DESCR); - _CASE(STATUS_INVALID_SERVER_STATE); - _CASE(STATUS_INVALID_SID); - _CASE(STATUS_INVALID_SUB_AUTHORITY); - _CASE(STATUS_INVALID_SYSTEM_SERVICE); - _CASE(STATUS_INVALID_UNWIND_TARGET); - _CASE(STATUS_INVALID_USER_BUFFER); - _CASE(STATUS_INVALID_VARIANT); - _CASE(STATUS_INVALID_VIEW_SIZE); - _CASE(STATUS_INVALID_VOLUME_LABEL); - _CASE(STATUS_INVALID_WORKSTATION); - _CASE(STATUS_IN_PAGE_ERROR); - _CASE(STATUS_IO_DEVICE_ERROR); - _CASE(STATUS_IO_PRIVILEGE_FAILED); - _CASE(STATUS_IO_REPARSE_DATA_INVALID); - _CASE(STATUS_IO_REPARSE_TAG_INVALID); - _CASE(STATUS_IO_REPARSE_TAG_MISMATCH); - _CASE(STATUS_IO_REPARSE_TAG_NOT_HANDLED); - _CASE(STATUS_IO_TIMEOUT); - _CASE(STATUS_IP_ADDRESS_CONFLICT1); - _CASE(STATUS_IP_ADDRESS_CONFLICT2); - _CASE(STATUS_JOURNAL_DELETE_IN_PROGRESS); - _CASE(STATUS_JOURNAL_ENTRY_DELETED); - _CASE(STATUS_JOURNAL_NOT_ACTIVE); - _CASE(STATUS_KERNEL_APC); - _CASE(STATUS_KEY_DELETED); - _CASE(STATUS_KEY_HAS_CHILDREN); - _CASE(STATUS_LAST_ADMIN); - _CASE(STATUS_LICENSE_QUOTA_EXCEEDED); - _CASE(STATUS_LICENSE_VIOLATION); - _CASE(STATUS_LINK_FAILED); - _CASE(STATUS_LINK_TIMEOUT); - _CASE(STATUS_LM_CROSS_ENCRYPTION_REQUIRED); - _CASE(STATUS_LOCAL_DISCONNECT); - _CASE(STATUS_LOCAL_USER_SESSION_KEY); - _CASE(STATUS_LOCK_NOT_GRANTED); - _CASE(STATUS_LOGIN_TIME_RESTRICTION); - _CASE(STATUS_LOGIN_WKSTA_RESTRICTION); - _CASE(STATUS_LOGON_FAILURE); - _CASE(STATUS_LOGON_NOT_GRANTED); - _CASE(STATUS_LOGON_SERVER_CONFLICT); - _CASE(STATUS_LOGON_SESSION_COLLISION); - _CASE(STATUS_LOGON_SESSION_EXISTS); - _CASE(STATUS_LOGON_TYPE_NOT_GRANTED); - _CASE(STATUS_LOG_FILE_FULL); - _CASE(STATUS_LOG_HARD_ERROR); - _CASE(STATUS_LONGJUMP); - _CASE(STATUS_LOST_WRITEBEHIND_DATA); - _CASE(STATUS_LPC_REPLY_LOST); - _CASE(STATUS_LUIDS_EXHAUSTED); - _CASE(STATUS_MAGAZINE_NOT_PRESENT); - _CASE(STATUS_MAPPED_ALIGNMENT); - _CASE(STATUS_MAPPED_FILE_SIZE_ZERO); - _CASE(STATUS_MARSHALL_OVERFLOW); - _CASE(STATUS_MEDIA_CHANGED); - _CASE(STATUS_MEDIA_CHECK); - _CASE(STATUS_MEDIA_WRITE_PROTECTED); - _CASE(STATUS_MEMBERS_PRIMARY_GROUP); - _CASE(STATUS_MEMBER_IN_ALIAS); - _CASE(STATUS_MEMBER_IN_GROUP); - _CASE(STATUS_MEMBER_NOT_IN_ALIAS); - _CASE(STATUS_MEMBER_NOT_IN_GROUP); - _CASE(STATUS_MEMORY_NOT_ALLOCATED); - _CASE(STATUS_MESSAGE_NOT_FOUND); - _CASE(STATUS_MISSING_SYSTEMFILE); - _CASE(STATUS_MORE_ENTRIES); - _CASE(STATUS_MORE_PROCESSING_REQUIRED); - _CASE(STATUS_MP_PROCESSOR_MISMATCH); - _CASE(STATUS_MULTIPLE_FAULT_VIOLATION); - _CASE(STATUS_MUTANT_LIMIT_EXCEEDED); - _CASE(STATUS_MUTANT_NOT_OWNED); - _CASE(STATUS_MUTUAL_AUTHENTICATION_FAILED); - _CASE(STATUS_NAME_TOO_LONG); - _CASE(STATUS_NETLOGON_NOT_STARTED); - _CASE(STATUS_NETWORK_ACCESS_DENIED); - _CASE(STATUS_NETWORK_BUSY); - _CASE(STATUS_NETWORK_CREDENTIAL_CONFLICT); - _CASE(STATUS_NETWORK_NAME_DELETED); - _CASE(STATUS_NETWORK_UNREACHABLE); - _CASE(STATUS_NET_WRITE_FAULT); - _CASE(STATUS_NOINTERFACE); - _CASE(STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT); - _CASE(STATUS_NOLOGON_SERVER_TRUST_ACCOUNT); - _CASE(STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT); - _CASE(STATUS_NONCONTINUABLE_EXCEPTION); - _CASE(STATUS_NONEXISTENT_EA_ENTRY); - _CASE(STATUS_NONEXISTENT_SECTOR); - _CASE(STATUS_NONE_MAPPED); - _CASE(STATUS_NOTIFY_CLEANUP); - _CASE(STATUS_NOTIFY_ENUM_DIR); - _CASE(STATUS_NOT_ALL_ASSIGNED); - _CASE(STATUS_NOT_A_DIRECTORY); - _CASE(STATUS_NOT_A_REPARSE_POINT); - _CASE(STATUS_NOT_CLIENT_SESSION); - _CASE(STATUS_NOT_COMMITTED); - _CASE(STATUS_NOT_EXPORT_FORMAT); - _CASE(STATUS_NOT_FOUND); - _CASE(STATUS_NOT_IMPLEMENTED); - _CASE(STATUS_NOT_LOCKED); - _CASE(STATUS_NOT_LOGON_PROCESS); - _CASE(STATUS_NOT_MAPPED_DATA); - _CASE(STATUS_NOT_MAPPED_VIEW); - _CASE(STATUS_NOT_REGISTRY_FILE); - _CASE(STATUS_NOT_SAME_DEVICE); - _CASE(STATUS_NOT_SERVER_SESSION); - _CASE(STATUS_NOT_SUPPORTED); - _CASE(STATUS_NOT_SUPPORTED_ON_SBS); - _CASE(STATUS_NOT_TINY_STREAM); - _CASE(STATUS_NO_BROWSER_SERVERS_FOUND); - _CASE(STATUS_NO_CALLBACK_ACTIVE); - _CASE(STATUS_NO_DATA_DETECTED); - _CASE(STATUS_NO_EAS_ON_FILE); - _CASE(STATUS_NO_EFS); - _CASE(STATUS_NO_EVENT_PAIR); - _CASE(STATUS_NO_GUID_TRANSLATION); - _CASE(STATUS_NO_IMPERSONATION_TOKEN); - _CASE(STATUS_NO_INHERITANCE); - _CASE(STATUS_NO_LDT); - _CASE(STATUS_NO_LOGON_SERVERS); - _CASE(STATUS_NO_LOG_SPACE); - _CASE(STATUS_NO_MATCH); - _CASE(STATUS_NO_MEDIA); - _CASE(STATUS_NO_MEDIA_IN_DEVICE); - _CASE(STATUS_NO_MEMORY); - _CASE(STATUS_NO_MORE_EAS); - _CASE(STATUS_NO_MORE_ENTRIES); - _CASE(STATUS_NO_MORE_FILES); - _CASE(STATUS_NO_MORE_MATCHES); - _CASE(STATUS_NO_PAGEFILE); - _CASE(STATUS_NO_QUOTAS_FOR_ACCOUNT); - _CASE(STATUS_NO_RECOVERY_POLICY); - _CASE(STATUS_NO_SECURITY_ON_OBJECT); - _CASE(STATUS_NO_SPOOL_SPACE); - _CASE(STATUS_NO_SUCH_ALIAS); - _CASE(STATUS_NO_SUCH_DEVICE); - _CASE(STATUS_NO_SUCH_DOMAIN); - _CASE(STATUS_NO_SUCH_FILE); - _CASE(STATUS_NO_SUCH_GROUP); - _CASE(STATUS_NO_SUCH_LOGON_SESSION); - _CASE(STATUS_NO_SUCH_MEMBER); - _CASE(STATUS_NO_SUCH_PACKAGE); - _CASE(STATUS_NO_SUCH_PRIVILEGE); - _CASE(STATUS_NO_SUCH_USER); - _CASE(STATUS_NO_TOKEN); - _CASE(STATUS_NO_TRACKING_SERVICE); - _CASE(STATUS_NO_TRUST_LSA_SECRET); - _CASE(STATUS_NO_TRUST_SAM_ACCOUNT); - _CASE(STATUS_NO_USER_KEYS); - _CASE(STATUS_NO_USER_SESSION_KEY); - _CASE(STATUS_NO_YIELD_PERFORMED); - _CASE(STATUS_NT_CROSS_ENCRYPTION_REQUIRED); - _CASE(STATUS_NULL_LM_PASSWORD); - _CASE(STATUS_OBJECTID_EXISTS); - _CASE(STATUS_OBJECT_NAME_COLLISION); - _CASE(STATUS_OBJECT_NAME_EXISTS); - _CASE(STATUS_OBJECT_NAME_INVALID); - _CASE(STATUS_OBJECT_NAME_NOT_FOUND); - _CASE(STATUS_OBJECT_PATH_INVALID); - _CASE(STATUS_OBJECT_PATH_NOT_FOUND); - _CASE(STATUS_OBJECT_PATH_SYNTAX_BAD); - _CASE(STATUS_OBJECT_TYPE_MISMATCH); - _CASE(STATUS_ONLY_IF_CONNECTED); - _CASE(STATUS_OPEN_FAILED); - _CASE(STATUS_OPLOCK_BREAK_IN_PROGRESS); - _CASE(STATUS_OPLOCK_NOT_GRANTED); - _CASE(STATUS_ORDINAL_NOT_FOUND); - _CASE(STATUS_PAGEFILE_CREATE_FAILED); - _CASE(STATUS_PAGEFILE_QUOTA); - _CASE(STATUS_PAGEFILE_QUOTA_EXCEEDED); - _CASE(STATUS_PAGE_FAULT_COPY_ON_WRITE); - _CASE(STATUS_PAGE_FAULT_DEMAND_ZERO); - _CASE(STATUS_PAGE_FAULT_GUARD_PAGE); - _CASE(STATUS_PAGE_FAULT_PAGING_FILE); - _CASE(STATUS_PAGE_FAULT_TRANSITION); - _CASE(STATUS_PARITY_ERROR); - _CASE(STATUS_PARTIAL_COPY); - _CASE(STATUS_PARTITION_FAILURE); - _CASE(STATUS_PASSWORD_EXPIRED); - _CASE(STATUS_PASSWORD_MUST_CHANGE); - _CASE(STATUS_PASSWORD_RESTRICTION); - _CASE(STATUS_PATH_NOT_COVERED); - _CASE(STATUS_PENDING); - _CASE(STATUS_PIPE_BROKEN); - _CASE(STATUS_PIPE_BUSY); - _CASE(STATUS_PIPE_CLOSING); - _CASE(STATUS_PIPE_CONNECTED); - _CASE(STATUS_PIPE_DISCONNECTED); - _CASE(STATUS_PIPE_EMPTY); - _CASE(STATUS_PIPE_LISTENING); - _CASE(STATUS_PIPE_NOT_AVAILABLE); - _CASE(STATUS_PLUGPLAY_NO_DEVICE); - _CASE(STATUS_PNP_BAD_MPS_TABLE); - _CASE(STATUS_PNP_IRQ_TRANSLATION_FAILED); - _CASE(STATUS_PNP_REBOOT_REQUIRED); - _CASE(STATUS_PNP_RESTART_ENUMERATION); - _CASE(STATUS_PNP_TRANSLATION_FAILED); - _CASE(STATUS_POLICY_OBJECT_NOT_FOUND); - _CASE(STATUS_POLICY_ONLY_IN_DS); - _CASE(STATUS_PORT_ALREADY_SET); - _CASE(STATUS_PORT_CONNECTION_REFUSED); - _CASE(STATUS_PORT_DISCONNECTED); - _CASE(STATUS_PORT_MESSAGE_TOO_LONG); - _CASE(STATUS_PORT_UNREACHABLE); - _CASE(STATUS_POSSIBLE_DEADLOCK); - _CASE(STATUS_POWER_STATE_INVALID); - _CASE(STATUS_PREDEFINED_HANDLE); - _CASE(STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED); - _CASE(STATUS_PRINT_CANCELLED); - _CASE(STATUS_PRINT_QUEUE_FULL); - _CASE(STATUS_PRIVILEGED_INSTRUCTION); - _CASE(STATUS_PRIVILEGE_NOT_HELD); - _CASE(STATUS_PROCEDURE_NOT_FOUND); - _CASE(STATUS_PROCESS_IS_TERMINATING); - _CASE(STATUS_PROFILING_AT_LIMIT); - _CASE(STATUS_PROFILING_NOT_STARTED); - _CASE(STATUS_PROFILING_NOT_STOPPED); - _CASE(STATUS_PROPSET_NOT_FOUND); - _CASE(STATUS_PROTOCOL_UNREACHABLE); - _CASE(STATUS_PWD_HISTORY_CONFLICT); - _CASE(STATUS_PWD_TOO_RECENT); - _CASE(STATUS_PWD_TOO_SHORT); - _CASE(STATUS_QUOTA_EXCEEDED); - _CASE(STATUS_QUOTA_LIST_INCONSISTENT); - _CASE(STATUS_RANGE_LIST_CONFLICT); - _CASE(STATUS_RANGE_NOT_FOUND); - _CASE(STATUS_RANGE_NOT_LOCKED); - _CASE(STATUS_RDP_PROTOCOL_ERROR); - _CASE(STATUS_RECEIVE_EXPEDITED); - _CASE(STATUS_RECEIVE_PARTIAL); - _CASE(STATUS_RECEIVE_PARTIAL_EXPEDITED); - _CASE(STATUS_RECOVERY_FAILURE); - _CASE(STATUS_REDIRECTOR_HAS_OPEN_HANDLES); - _CASE(STATUS_REDIRECTOR_NOT_STARTED); - _CASE(STATUS_REDIRECTOR_PAUSED); - _CASE(STATUS_REDIRECTOR_STARTED); - _CASE(STATUS_REGISTRY_CORRUPT); - _CASE(STATUS_REGISTRY_IO_FAILED); - _CASE(STATUS_REGISTRY_QUOTA_LIMIT); - _CASE(STATUS_REGISTRY_RECOVERED); - _CASE(STATUS_REG_NAT_CONSUMPTION); - _CASE(STATUS_REINITIALIZATION_NEEDED); - _CASE(STATUS_REMOTE_DISCONNECT); - _CASE(STATUS_REMOTE_NOT_LISTENING); - _CASE(STATUS_REMOTE_RESOURCES); - _CASE(STATUS_REMOTE_SESSION_LIMIT); - _CASE(STATUS_REMOTE_STORAGE_MEDIA_ERROR); - _CASE(STATUS_REMOTE_STORAGE_NOT_ACTIVE); - _CASE(STATUS_REPARSE); - _CASE(STATUS_REPARSE_ATTRIBUTE_CONFLICT); - _CASE(STATUS_REPARSE_OBJECT); - _CASE(STATUS_REPARSE_POINT_NOT_RESOLVED); - _CASE(STATUS_REPLY_MESSAGE_MISMATCH); - _CASE(STATUS_REQUEST_ABORTED); - _CASE(STATUS_REQUEST_NOT_ACCEPTED); - _CASE(STATUS_RESOURCE_DATA_NOT_FOUND); - _CASE(STATUS_RESOURCE_LANG_NOT_FOUND); - _CASE(STATUS_RESOURCE_NAME_NOT_FOUND); - _CASE(STATUS_RESOURCE_NOT_OWNED); - _CASE(STATUS_RESOURCE_REQUIREMENTS_CHANGED); - _CASE(STATUS_RESOURCE_TYPE_NOT_FOUND); - _CASE(STATUS_RETRY); - _CASE(STATUS_REVISION_MISMATCH); - _CASE(STATUS_RXACT_COMMITTED); - _CASE(STATUS_RXACT_COMMIT_FAILURE); - _CASE(STATUS_RXACT_COMMIT_NECESSARY); - _CASE(STATUS_RXACT_INVALID_STATE); - _CASE(STATUS_RXACT_STATE_CREATED); - _CASE(STATUS_SAM_INIT_FAILURE); - _CASE(STATUS_SAM_NEED_BOOTKEY_FLOPPY); - _CASE(STATUS_SAM_NEED_BOOTKEY_PASSWORD); - _CASE(STATUS_SECRET_TOO_LONG); - _CASE(STATUS_SECTION_NOT_EXTENDED); - _CASE(STATUS_SECTION_NOT_IMAGE); - _CASE(STATUS_SECTION_PROTECTION); - _CASE(STATUS_SECTION_TOO_BIG); - _CASE(STATUS_SEGMENT_NOTIFICATION); - _CASE(STATUS_SEMAPHORE_LIMIT_EXCEEDED); - _CASE(STATUS_SERIAL_COUNTER_TIMEOUT); - _CASE(STATUS_SERIAL_MORE_WRITES); - _CASE(STATUS_SERIAL_NO_DEVICE_INITED); - _CASE(STATUS_SERVER_DISABLED); - _CASE(STATUS_SERVER_HAS_OPEN_HANDLES); - _CASE(STATUS_SERVER_NOT_DISABLED); - _CASE(STATUS_SERVER_SID_MISMATCH); - _CASE(STATUS_SERVICE_NOTIFICATION); - _CASE(STATUS_SETMARK_DETECTED); - _CASE(STATUS_SHARED_IRQ_BUSY); - _CASE(STATUS_SHARED_POLICY); - _CASE(STATUS_SHARING_PAUSED); - _CASE(STATUS_SHARING_VIOLATION); - _CASE(STATUS_SINGLE_STEP); - _CASE(STATUS_SOME_NOT_MAPPED); - _CASE(STATUS_SOURCE_ELEMENT_EMPTY); - _CASE(STATUS_SPECIAL_ACCOUNT); - _CASE(STATUS_SPECIAL_GROUP); - _CASE(STATUS_SPECIAL_USER); - _CASE(STATUS_STACK_OVERFLOW); - _CASE(STATUS_STACK_OVERFLOW_READ); - _CASE(STATUS_SUCCESS); - _CASE(STATUS_SUSPEND_COUNT_EXCEEDED); - _CASE(STATUS_SYNCHRONIZATION_REQUIRED); - _CASE(STATUS_SYSTEM_IMAGE_BAD_SIGNATURE); - _CASE(STATUS_SYSTEM_PROCESS_TERMINATED); - _CASE(STATUS_THREAD_IS_TERMINATING); - _CASE(STATUS_THREAD_NOT_IN_PROCESS); - _CASE(STATUS_THREAD_WAS_SUSPENDED); - _CASE(STATUS_TIMEOUT); - _CASE(STATUS_TIMER_NOT_CANCELED); - _CASE(STATUS_TIMER_RESOLUTION_NOT_SET); - _CASE(STATUS_TIMER_RESUME_IGNORED); - _CASE(STATUS_TIME_DIFFERENCE_AT_DC); - _CASE(STATUS_TOKEN_ALREADY_IN_USE); - _CASE(STATUS_TOO_LATE); - _CASE(STATUS_TOO_MANY_ADDRESSES); - _CASE(STATUS_TOO_MANY_COMMANDS); - _CASE(STATUS_TOO_MANY_CONTEXT_IDS); - _CASE(STATUS_TOO_MANY_GUIDS_REQUESTED); - _CASE(STATUS_TOO_MANY_LINKS); - _CASE(STATUS_TOO_MANY_LUIDS_REQUESTED); - _CASE(STATUS_TOO_MANY_NAMES); - _CASE(STATUS_TOO_MANY_NODES); - _CASE(STATUS_TOO_MANY_OPENED_FILES); - _CASE(STATUS_TOO_MANY_PAGING_FILES); - _CASE(STATUS_TOO_MANY_SECRETS); - _CASE(STATUS_TOO_MANY_SESSIONS); - _CASE(STATUS_TOO_MANY_SIDS); - _CASE(STATUS_TOO_MANY_THREADS); - _CASE(STATUS_TRANSACTION_ABORTED); - _CASE(STATUS_TRANSACTION_INVALID_ID); - _CASE(STATUS_TRANSACTION_INVALID_TYPE); - _CASE(STATUS_TRANSACTION_NO_MATCH); - _CASE(STATUS_TRANSACTION_NO_RELEASE); - _CASE(STATUS_TRANSACTION_RESPONDED); - _CASE(STATUS_TRANSACTION_TIMED_OUT); - _CASE(STATUS_TRANSLATION_COMPLETE); - _CASE(STATUS_TRANSPORT_FULL); - _CASE(STATUS_TRUSTED_DOMAIN_FAILURE); - _CASE(STATUS_TRUSTED_RELATIONSHIP_FAILURE); - _CASE(STATUS_TRUST_FAILURE); - _CASE(STATUS_UNABLE_TO_DECOMMIT_VM); - _CASE(STATUS_UNABLE_TO_DELETE_SECTION); - _CASE(STATUS_UNABLE_TO_FREE_VM); - _CASE(STATUS_UNABLE_TO_LOCK_MEDIA); - _CASE(STATUS_UNABLE_TO_UNLOAD_MEDIA); - _CASE(STATUS_UNDEFINED_CHARACTER); - _CASE(STATUS_UNEXPECTED_IO_ERROR); - _CASE(STATUS_UNEXPECTED_MM_CREATE_ERR); - _CASE(STATUS_UNEXPECTED_MM_EXTEND_ERR); - _CASE(STATUS_UNEXPECTED_MM_MAP_ERROR); - _CASE(STATUS_UNEXPECTED_NETWORK_ERROR); - _CASE(STATUS_UNHANDLED_EXCEPTION); - _CASE(STATUS_UNKNOWN_REVISION); - _CASE(STATUS_UNMAPPABLE_CHARACTER); - _CASE(STATUS_UNRECOGNIZED_MEDIA); - _CASE(STATUS_UNRECOGNIZED_VOLUME); - _CASE(STATUS_UNSUCCESSFUL); - _CASE(STATUS_UNSUPPORTED_COMPRESSION); - _CASE(STATUS_UNWIND); - _CASE(STATUS_USER_APC); - _CASE(STATUS_USER_EXISTS); - _CASE(STATUS_USER_MAPPED_FILE); - _CASE(STATUS_USER_SESSION_DELETED); - _CASE(STATUS_VALIDATE_CONTINUE); - _CASE(STATUS_VARIABLE_NOT_FOUND); - _CASE(STATUS_VDM_HARD_ERROR); - _CASE(STATUS_VERIFY_REQUIRED); - _CASE(STATUS_VIRTUAL_CIRCUIT_CLOSED); - _CASE(STATUS_VOLUME_DISMOUNTED); - _CASE(STATUS_VOLUME_MOUNTED); - _CASE(STATUS_VOLUME_NOT_UPGRADED); - _CASE(STATUS_WAIT_1); - _CASE(STATUS_WAIT_2); - _CASE(STATUS_WAIT_3); - _CASE(STATUS_WAIT_63); - _CASE(STATUS_WAKE_SYSTEM); - _CASE(STATUS_WAKE_SYSTEM_DEBUGGER); - _CASE(STATUS_WAS_LOCKED); - _CASE(STATUS_WAS_UNLOCKED); - _CASE(STATUS_WMI_GUID_NOT_FOUND); - _CASE(STATUS_WMI_INSTANCE_NOT_FOUND); - _CASE(STATUS_WMI_ITEMID_NOT_FOUND); - _CASE(STATUS_WMI_NOT_SUPPORTED); - _CASE(STATUS_WMI_READ_ONLY); - _CASE(STATUS_WMI_SET_FAILURE); - _CASE(STATUS_WMI_TRY_AGAIN); - _CASE(STATUS_WORKING_SET_LIMIT_RANGE); - _CASE(STATUS_WORKING_SET_QUOTA); - _CASE(STATUS_WOW_ASSERTION); - _CASE(STATUS_WRONG_EFS); - _CASE(STATUS_WRONG_PASSWORD); - _CASE(STATUS_WRONG_PASSWORD_CORE); - _CASE(STATUS_WRONG_VOLUME); - _CASE(STATUS_WX86_BREAKPOINT); - _CASE(STATUS_WX86_CONTINUE); - _CASE(STATUS_WX86_CREATEWX86TIB); - _CASE(STATUS_WX86_EXCEPTION_CHAIN); - _CASE(STATUS_WX86_EXCEPTION_CONTINUE); - _CASE(STATUS_WX86_EXCEPTION_LASTCHANCE); - _CASE(STATUS_WX86_FLOAT_STACK_CHECK); - _CASE(STATUS_WX86_INTERNAL_ERROR); - _CASE(STATUS_WX86_SINGLE_STEP); - _CASE(STATUS_WX86_UNSIMULATE); - default: return "STATUS_UNKNOWN"; - } - -#undef _CASE -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::FILE + +#include "EmuFile.h" +#include +#include +#include +#include +#include +#include +#pragma warning(disable:4005) // Ignore redefined status values +#include +#pragma warning(default:4005) +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\memory-manager\VMManager.h" +#include "Logging.h" + +#include + +// Default Xbox Partition Table +#define PE_PARTFLAGS_IN_USE 0x80000000 +#define XBOX_SWAPPART1_LBA_START 0x400 +#define XBOX_SWAPPART_LBA_SIZE 0x177000 +#define XBOX_SWAPPART2_LBA_START (XBOX_SWAPPART1_LBA_START + XBOX_SWAPPART_LBA_SIZE) +#define XBOX_SWAPPART3_LBA_START (XBOX_SWAPPART2_LBA_START + XBOX_SWAPPART_LBA_SIZE) + +#define XBOX_SYSPART_LBA_START (XBOX_SWAPPART3_LBA_START + XBOX_SWAPPART_LBA_SIZE) +#define XBOX_SYSPART_LBA_SIZE 0xfa000 + +#define XBOX_MUSICPART_LBA_START (XBOX_SYSPART_LBA_START + XBOX_SYSPART_LBA_SIZE) +#define XBOX_MUSICPART_LBA_SIZE 0x9896b0 + +XboxPartitionTable BackupPartTbl = +{ + {'*', '*', '*', '*', 'P', 'A', 'R', 'T', 'I', 'N', 'F', 'O', '*', '*', '*', '*'}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + { + {{'X', 'B', 'O', 'X', ' ', 'S', 'H', 'E', 'L', 'L', ' ', ' ', ' ', ' ', ' ', ' '}, PE_PARTFLAGS_IN_USE, XBOX_MUSICPART_LBA_START, XBOX_MUSICPART_LBA_SIZE, 0}, + {{'X', 'B', 'O', 'X', ' ', 'D', 'A', 'T', 'A', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, PE_PARTFLAGS_IN_USE, XBOX_SYSPART_LBA_START, XBOX_SYSPART_LBA_SIZE, 0}, + {{'X', 'B', 'O', 'X', ' ', 'G', 'A', 'M', 'E', ' ', 'S', 'W', 'A', 'P', ' ', '1'}, PE_PARTFLAGS_IN_USE, XBOX_SWAPPART1_LBA_START, XBOX_SWAPPART_LBA_SIZE, 0}, + {{'X', 'B', 'O', 'X', ' ', 'G', 'A', 'M', 'E', ' ', 'S', 'W', 'A', 'P', ' ', '2'}, PE_PARTFLAGS_IN_USE, XBOX_SWAPPART2_LBA_START, XBOX_SWAPPART_LBA_SIZE, 0}, + {{'X', 'B', 'O', 'X', ' ', 'G', 'A', 'M', 'E', ' ', 'S', 'W', 'A', 'P', ' ', '3'}, PE_PARTFLAGS_IN_USE, XBOX_SWAPPART3_LBA_START, XBOX_SWAPPART_LBA_SIZE, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 0, 0, 0, 0}, + } +}; + +void CxbxCreatePartitionHeaderFile(std::string filename, bool partition0 = false) +{ + HANDLE hf = CreateFile(filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if (!hf) { + CxbxKrnlCleanup("CxbxCreatePartitionHeaderFile Failed\nUnable to create file: %s (%s)", filename.c_str()); + return; + } + + // If this is partition 0, install the partiton table + if (partition0) { + DWORD NumberOfBytesWritten = 0; + WriteFile(hf, &BackupPartTbl, sizeof(XboxPartitionTable), &NumberOfBytesWritten, 0); + } + + SetFilePointer(hf, 512 * ONE_KB, 0, FILE_BEGIN); + SetEndOfFile(hf); + CloseHandle(hf); +} + +XboxPartitionTable CxbxGetPartitionTable() +{ + XboxPartitionTable table; + FILE* fp = fopen((CxbxBasePath + "Partition0.bin").c_str(), "rb"); + if (fp == nullptr) { + CxbxKrnlCleanup("CxbxGetPartitionTable Failed:\nUnable to open file: %s", (CxbxBasePath + "Partition0.bin").c_str()); + } + + fread(&table, sizeof(XboxPartitionTable), 1, fp); + fclose(fp); + + // If the partition table is not valid, format it + // This allows recovery from corrupted partition tables + // Or invalid partition tables left behind from previous versions + // of Cxbx-Reloaded + if (memcmp(table.Magic, BackupPartTbl.Magic, 16) != 0) { + DeleteFile((CxbxBasePath + "Partition0.bin").c_str()); + CxbxCreatePartitionHeaderFile(CxbxBasePath + "Partition0.bin", true); + memcpy(&table, &BackupPartTbl, sizeof(XboxPartitionTable)); + } + + return table; +} + +FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber) +{ + FATX_SUPERBLOCK superblock; + std::stringstream ss; + + ss << CxbxBasePath << "Partition" << partitionNumber << ".bin"; + FILE* fp = fopen(ss.str().c_str(), "rb"); + fread(&superblock, sizeof(FATX_SUPERBLOCK), 1, fp); + fclose(fp); + return superblock; +} + +int CxbxGetPartitionNumberFromHandle(HANDLE hFile) +{ + // Get which partition number is being accessed, by parsing the filename and extracting the last portion + char buffer[MAX_PATH] = {0}; + if (!GetFinalPathNameByHandle(hFile, buffer, MAX_PATH, VOLUME_NAME_DOS)) { + CxbxKrnlCleanup("CxbxGetPartitionNumberFromHandle Failed:\nUnable to determine path for HANDLE 0x%08X", hFile); + } + + std::string bufferString(buffer); + std::string partitionString = "\\Partition"; + std::string partitionNumberString = bufferString.substr(bufferString.find(partitionString) + partitionString.length(), 1); + + // atoi returns 0 on non-numeric characters, so we don't need to error check here + return atoi(partitionNumberString.c_str()); +} + +std::string CxbxGetPartitionDataPathFromHandle(HANDLE hFile) +{ + // Get which partition number is being accessed, by parsing the filename and extracting the last portion + char buffer[MAX_PATH] = {0}; + if (!GetFinalPathNameByHandle(hFile, buffer, MAX_PATH, VOLUME_NAME_DOS)) { + CxbxKrnlCleanup("CxbxGetPartitionDataPathFromHandle Failed:\nUnable to determine path for HANDLE 0x%08X", hFile); + } + + std::string bufferString(buffer); + std::string partitionString = "\\Partition"; + std::string partitionPath = bufferString.substr(0, bufferString.find(partitionString) + partitionString.length() + 1); + return partitionPath; +} + +void CxbxFormatPartitionByHandle(HANDLE hFile) +{ + std::string partitionPath = CxbxGetPartitionDataPathFromHandle(hFile); + + // Sanity check, make sure we are actually deleting something within the Cxbx-Reloaded folder + if (partitionPath.find("Cxbx-Reloaded") == std::string::npos) { + EmuLog(LOG_LEVEL::WARNING, "Attempting to format a path that is not within a Cxbx-Reloaded data folder... Ignoring!\n"); + return; + } + + + // Format the partition, by iterating through the contents and removing all files/folders within + // Previously, we deleted and re-created the folder, but that caused permission issues for some users + try + { + for (auto& directoryEntry : std::filesystem::recursive_directory_iterator(partitionPath)) { + std::filesystem::remove_all(directoryEntry); + } + } + catch (std::filesystem::filesystem_error fsException) + { + printf("std::filesystem failed with message: %s\n", fsException.what()); + } + + + printf("Formatted EmuDisk Partition%d\n", CxbxGetPartitionNumberFromHandle(hFile)); +} + +const std::string MediaBoardRomFile = "Chihiro\\fpr21042_m29w160et.bin"; +const std::string DrivePrefix = "\\??\\"; +const std::string DriveSerial = DrivePrefix + "serial:"; +const std::string DriveCdRom0 = DrivePrefix + "CdRom0:"; // CD-ROM device +const std::string DriveMbfs = "mbfs:"; // media board's file system area device +const std::string DriveMbcom = "mbcom:"; // media board's communication area device +const std::string DriveMbrom0 = "mbrom0:"; // media board's boot ROM device (first image) +const std::string DriveMbrom1 = "mbrom1:"; // media board's boot ROM device (second image) +const std::string DriveA = DrivePrefix + "A:"; // A: could be CDROM +const std::string DriveC = DrivePrefix + "C:"; // C: is HDD0 +const std::string DriveD = DrivePrefix + "D:"; // D: is DVD Player +const std::string DriveE = DrivePrefix + "E:"; +const std::string DriveF = DrivePrefix + "F:"; +const std::string DriveS = DrivePrefix + "S:"; +const std::string DriveT = DrivePrefix + "T:"; // T: is Title persistent data region +const std::string DriveU = DrivePrefix + "U:"; // U: is User persistent data region +const std::string DriveV = DrivePrefix + "V:"; +const std::string DriveW = DrivePrefix + "W:"; +const std::string DriveX = DrivePrefix + "X:"; +const std::string DriveY = DrivePrefix + "Y:"; // Y: is Dashboard volume (contains "xboxdash.xbe" and "XDASH" folder + contents) +const std::string DriveZ = DrivePrefix + "Z:"; // Z: is Title utility data region +const std::string DevicePrefix = "\\Device"; +const std::string DeviceCdrom0 = DevicePrefix + "\\CdRom0"; +const std::string DeviceHarddisk0 = DevicePrefix + "\\Harddisk0"; +const std::string DeviceHarddisk0PartitionPrefix = DevicePrefix + "\\Harddisk0\\partition"; +const std::string DeviceHarddisk0Partition0 = DeviceHarddisk0PartitionPrefix + "0"; // Contains raw config sectors (like XBOX_REFURB_INFO) + entire hard disk +const std::string DeviceHarddisk0Partition1 = DeviceHarddisk0PartitionPrefix + "1"; // Data partition. Contains TDATA and UDATA folders. +const std::string DeviceHarddisk0Partition2 = DeviceHarddisk0PartitionPrefix + "2"; // Shell partition. Contains Dashboard (cpxdash.xbe, evoxdash.xbe or xboxdash.xbe) +const std::string DeviceHarddisk0Partition3 = DeviceHarddisk0PartitionPrefix + "3"; // First cache partition. Contains cache data (from here up to largest number) +const std::string DeviceHarddisk0Partition4 = DeviceHarddisk0PartitionPrefix + "4"; +const std::string DeviceHarddisk0Partition5 = DeviceHarddisk0PartitionPrefix + "5"; +const std::string DeviceHarddisk0Partition6 = DeviceHarddisk0PartitionPrefix + "6"; +const std::string DeviceHarddisk0Partition7 = DeviceHarddisk0PartitionPrefix + "7"; +const std::string DeviceHarddisk0Partition8 = DeviceHarddisk0PartitionPrefix + "8"; +const std::string DeviceHarddisk0Partition9 = DeviceHarddisk0PartitionPrefix + "9"; +const std::string DeviceHarddisk0Partition10 = DeviceHarddisk0PartitionPrefix + "10"; +const std::string DeviceHarddisk0Partition11 = DeviceHarddisk0PartitionPrefix + "11"; +const std::string DeviceHarddisk0Partition12 = DeviceHarddisk0PartitionPrefix + "12"; +const std::string DeviceHarddisk0Partition13 = DeviceHarddisk0PartitionPrefix + "13"; +const std::string DeviceHarddisk0Partition14 = DeviceHarddisk0PartitionPrefix + "14"; +const std::string DeviceHarddisk0Partition15 = DeviceHarddisk0PartitionPrefix + "15"; +const std::string DeviceHarddisk0Partition16 = DeviceHarddisk0PartitionPrefix + "16"; +const std::string DeviceHarddisk0Partition17 = DeviceHarddisk0PartitionPrefix + "17"; +const std::string DeviceHarddisk0Partition18 = DeviceHarddisk0PartitionPrefix + "18"; +const std::string DeviceHarddisk0Partition19 = DeviceHarddisk0PartitionPrefix + "19"; +const std::string DeviceHarddisk0Partition20 = DeviceHarddisk0PartitionPrefix + "20"; // 20 = Largest possible partition number +const char CxbxDefaultXbeDriveLetter = 'D'; + +int CxbxDefaultXbeDriveIndex = -1; +EmuNtSymbolicLinkObject* NtSymbolicLinkObjects[26]; +std::vector Devices; + +EmuHandle::EmuHandle(EmuNtObject* ntObject) +{ + NtObject = ntObject; +} + +NTSTATUS EmuHandle::NtClose() +{ + return NtObject->NtClose(); +} + +NTSTATUS EmuHandle::NtDuplicateObject(PHANDLE TargetHandle, DWORD Options) +{ + *TargetHandle = NtObject->NtDuplicateObject(Options)->NewHandle(); + return STATUS_SUCCESS; +} + +EmuNtObject::EmuNtObject() +{ + RefCount = 1; +} + +HANDLE EmuNtObject::NewHandle() +{ + RefCount++; + return EmuHandleToHandle(new EmuHandle(this)); +} + +NTSTATUS EmuNtObject::NtClose() +{ + if (--RefCount <= 0) { + delete this; + } + + return STATUS_SUCCESS; +} + +EmuNtObject* EmuNtObject::NtDuplicateObject(DWORD Options) +{ + RefCount++; + return this; +} + +bool IsEmuHandle(HANDLE Handle) +{ + return ((uint32_t)Handle > 0x80000000) && ((uint32_t)Handle < 0xFFFFFFFE); +} + +EmuHandle* HandleToEmuHandle(HANDLE Handle) +{ + return (EmuHandle*)((uint32_t)Handle & 0x7FFFFFFF); +} + +HANDLE EmuHandleToHandle(EmuHandle* emuHandle) +{ + return (HANDLE)((uint32_t)emuHandle | 0x80000000); +} + +std::wstring string_to_wstring(std::string const & src) +{ + std::wstring result = std::wstring(src.length(), L' '); + std::copy(src.begin(), src.end(), result.begin()); + return result; +} + +std::wstring PUNICODE_STRING_to_wstring(NtDll::PUNICODE_STRING const & src) +{ +return std::wstring(src->Buffer, src->Length / sizeof(NtDll::WCHAR)); +} + +std::string PSTRING_to_string(xboxkrnl::PSTRING const & src) +{ + return std::string(src->Buffer, src->Length); +} + +void copy_string_to_PSTRING_to(std::string const & src, const xboxkrnl::PSTRING & dest) +{ + dest->Length = (USHORT)src.size(); + memcpy(dest->Buffer, src.c_str(), src.size()); +} + +void replace_all(std::string& str, const std::string& from, const std::string& to) { + if (from.empty()) + return; + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } +} + +NTSTATUS CxbxConvertFilePath( + std::string RelativeXboxPath, + OUT std::wstring &RelativeHostPath, + IN OUT NtDll::HANDLE *RootDirectory, + std::string aFileAPIName, + bool partitionHeader) +{ + std::string OriginalPath = RelativeXboxPath; + std::string RelativePath = RelativeXboxPath; + std::string XboxFullPath; + std::string HostPath; + EmuNtSymbolicLinkObject* NtSymbolicLinkObject = NULL; + + // Always trim '\??\' off : + if (RelativePath.compare(0, DrivePrefix.length(), DrivePrefix.c_str()) == 0) + RelativePath.erase(0, 4); + + // Check if we where called from a File-handling API : + if (!aFileAPIName.empty()) { + if (RelativePath.compare(DriveMbrom0) == 0 || RelativePath.compare(DriveMbrom1) == 0) { + *RootDirectory = CxbxBasePathHandle; + HostPath = CxbxBasePath; + RelativePath = MediaBoardRomFile; + } + else if (!partitionHeader) { + // Check if the path starts with a volume indicator : + if ((RelativePath.length() >= 2) && (RelativePath[1] == ':')) { + // Look up the symbolic link information using the drive letter : + NtSymbolicLinkObject = FindNtSymbolicLinkObjectByDriveLetter(RelativePath[0]); + RelativePath.erase(0, 2); // Remove 'C:' + + // If the remaining path starts with a ':', remove it (to prevent errors) : + if ((RelativePath.length() > 0) && (RelativePath[0] == ':')) + RelativePath.erase(0, 1); // xbmp needs this, as it accesses 'e::\' + } + else if (RelativePath[0] == '$') { + if (RelativePath.compare(0, 5, "$HOME") == 0) // "xbmp" needs this + { + NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); + RelativePath.erase(0, 5); // Remove '$HOME' + } + else + CxbxKrnlCleanup(("Unsupported path macro : " + OriginalPath).c_str()); + } + // Check if the path starts with a relative path indicator : + else if (RelativePath[0] == '.') {// "4x4 Evo 2" needs this + NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); + RelativePath.erase(0, 1); // Remove the '.' + } + else { + // TODO : How should we handle accesses to the serial: (?semi-)volume? + if (RelativePath.compare(0, 7, "serial:") == 0) + return STATUS_UNRECOGNIZED_VOLUME; + + // The path seems to be a device path, look it up : + NtSymbolicLinkObject = FindNtSymbolicLinkObjectByDevice(RelativePath); + // Fixup RelativePath path here + if (NtSymbolicLinkObject != NULL) + RelativePath.erase(0, NtSymbolicLinkObject->XboxSymbolicLinkPath.length()); // Remove '\Device\Harddisk0\Partition2' + } + + if (NtSymbolicLinkObject == NULL) { + // Check if the path accesses a partition from Harddisk0 : + if (_strnicmp(RelativePath.c_str(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { + XboxFullPath = RelativePath; + // Remove Harddisk0 prefix, in the hope that the remaining path might work : + RelativePath.erase(0, DeviceHarddisk0.length() + 1); + // And set Root to the folder containing the partition-folders : + *RootDirectory = CxbxBasePathHandle; + HostPath = CxbxBasePath; + } + // NOTE: RootDirectory cannot be ignored. + // Any special handling for it should be done below. + else if (*RootDirectory == nullptr) { + // Assume relative to Xbe path + NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); + } + else if (*RootDirectory == (NtDll::HANDLE)-3) { + // This is a special handle that tells the API that this is a DOS device + // We can safely remove it and forward to the Xbe directory. + // Test case GTA3 + NtSymbolicLinkObject = FindNtSymbolicLinkObjectByRootHandle(g_hCurDir); + } + else if (*RootDirectory == (NtDll::HANDLE)-4) { + // NOTE: A handle of -4 on the Xbox signifies the path should be in the BaseNamedObjects namespace. + // This handle doesn't exist on Windows, so we prefix the name instead. (note from LukeUsher) + // Handle special root directory constants + *RootDirectory = NULL; + + if (OriginalPath.size() == 0){ + RelativePath = "\\BaseNamedObjects"; + } else { + RelativePath = "\\BaseNamedObjects\\" + OriginalPath; + } + } + // else {} // NOTE: Allow RootDirectory handle to take control of relative path. + // Test-case: Turok Evolution + } + + if (NtSymbolicLinkObject != NULL) { + HostPath = NtSymbolicLinkObject->HostSymbolicLinkPath; + + XboxFullPath = NtSymbolicLinkObject->XboxSymbolicLinkPath; + + // If accessing a partition as a directly, set the root directory handle and keep relative path as is + *RootDirectory = NtSymbolicLinkObject->RootDirectoryHandle; + } + } else { + *RootDirectory = CxbxBasePathHandle; + HostPath = CxbxBasePath; + RelativePath = RelativeXboxPath.substr(DeviceHarddisk0.length()) + ".bin"; + } + + // If the remaining path starts with a '\', remove it (to prevent working in a native root) : + if ((RelativePath.length() > 0) && (RelativePath[0] == '\\')) { + RelativePath.erase(0, 1); + // And if needed, add it to the host path instead : + if (HostPath.back() != '\\') + HostPath.append(1, '\\'); + } + + // Lastly, remove any '\\' sequences in the string (this should fix the problem with Azurik game saves) + replace_all( RelativePath, "\\\\", "\\" ); + + if (g_bPrintfOn) { + EmuLog(LOG_LEVEL::DEBUG, "%s Corrected path...", aFileAPIName.c_str()); + EmuLog(LOG_LEVEL::DEBUG, " Org:\"%s\"", OriginalPath.c_str()); + if (_strnicmp(HostPath.c_str(), CxbxBasePath.c_str(), CxbxBasePath.length()) == 0) { + EmuLog(LOG_LEVEL::DEBUG, " New:\"$CxbxPath\\%s%s\"", (HostPath.substr(CxbxBasePath.length(), std::string::npos)).c_str(), RelativePath.c_str()); + } + else + EmuLog(LOG_LEVEL::DEBUG, " New:\"$XbePath\\%s\"", RelativePath.c_str()); + } + } + else + { + // For non-file API calls, prefix with '\??\' again : + RelativePath = DrivePrefix + RelativePath; + *RootDirectory = 0; + } + + // Convert the relative path to unicode + RelativeHostPath = string_to_wstring(RelativePath); + + return STATUS_SUCCESS; +} + +NTSTATUS CxbxObjectAttributesToNT( + xboxkrnl::POBJECT_ATTRIBUTES ObjectAttributes, + OUT NativeObjectAttributes& nativeObjectAttributes, + const std::string aFileAPIName, + bool partitionHeader) +{ + if (ObjectAttributes == NULL) + { + // When the pointer is nil, make sure we pass nil to Windows too : + nativeObjectAttributes.NtObjAttrPtr = nullptr; + return STATUS_SUCCESS; + } + + // Pick up the ObjectName, and let's see what to make of it : + std::string ObjectName = ""; + if (ObjectAttributes->ObjectName != NULL) { + ObjectName = PSTRING_to_string(ObjectAttributes->ObjectName); + } + std::wstring RelativeHostPath; + NtDll::HANDLE RootDirectory = ObjectAttributes->RootDirectory; + + // Is there a filename API given? + if (aFileAPIName.size() > 0) { + // Then interpret the ObjectName as a filename, and update it to host relative : + NTSTATUS result = CxbxConvertFilePath(ObjectName, /*OUT*/RelativeHostPath, /*IN OUT*/&RootDirectory, aFileAPIName, partitionHeader); + if (FAILED(result)) { + return result; + } + } + else { + // When not called from a file-handling API, just convert the ObjectName to a wide string : + RelativeHostPath = string_to_wstring(ObjectName); + } + + // Copy the wide string to the unicode string + wcscpy_s(nativeObjectAttributes.wszObjectName, RelativeHostPath.c_str()); + NtDll::RtlInitUnicodeString(&nativeObjectAttributes.NtUnicodeString, nativeObjectAttributes.wszObjectName); + // And initialize the NT ObjectAttributes with that : + InitializeObjectAttributes(&nativeObjectAttributes.NtObjAttr, &nativeObjectAttributes.NtUnicodeString, ObjectAttributes->Attributes, RootDirectory, NULL); + // ObjectAttributes are given, so make sure the pointer we're going to pass to Windows is assigned : + nativeObjectAttributes.NtObjAttrPtr = &nativeObjectAttributes.NtObjAttr; + + return STATUS_SUCCESS; +} + +int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath) +{ + for (size_t i = 0; i < Devices.size(); i++) + if (_strnicmp(XboxDevicePath, Devices[i].XboxDevicePath.c_str(), Devices[i].XboxDevicePath.length()) == 0) + return(i); + + return -1; +} + +XboxDevice *CxbxDeviceByDevicePath(const std::string XboxDevicePath) +{ + int DeviceIndex = CxbxDeviceIndexByDevicePath(XboxDevicePath.c_str()); + if (DeviceIndex >= 0) + return &Devices[DeviceIndex]; + + return nullptr; +} + +int CxbxRegisterDeviceHostPath(std::string XboxDevicePath, std::string HostDevicePath, bool IsFile) +{ + int result = -1; + NTSTATUS status = (NTSTATUS)-1; + + XboxDevice newDevice; + newDevice.XboxDevicePath = XboxDevicePath; + newDevice.HostDevicePath = HostDevicePath; + + // All HDD partitions have a .bin file to allow direct file io on the partition info + if (_strnicmp(XboxDevicePath.c_str(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { + std::string partitionHeaderPath = (HostDevicePath + ".bin").c_str(); + if (!PathFileExists(partitionHeaderPath.c_str())) { + CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0); + } + + status = STATUS_SUCCESS; + } + + // If this path is not a raw file partition, create the directory for it + if (!IsFile) { + status = SHCreateDirectoryEx(NULL, HostDevicePath.c_str(), NULL); + } + + if (status == STATUS_SUCCESS || status == ERROR_ALREADY_EXISTS) { + Devices.push_back(newDevice); + result = Devices.size() - 1; + } + + return result; +} + + +NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath) +{ + NTSTATUS result = 0; + EmuNtSymbolicLinkObject* SymbolicLinkObject = FindNtSymbolicLinkObjectByName(SymbolicLinkName); + + if (SymbolicLinkObject != NULL) + // In that case, close it (will also delete if reference count drops to zero) + SymbolicLinkObject->NtClose(); + + // Now (re)create a symbolic link object, and initialize it with the new definition : + SymbolicLinkObject = new EmuNtSymbolicLinkObject(); + result = SymbolicLinkObject->Init(SymbolicLinkName, FullPath); + + if (result != STATUS_SUCCESS) + SymbolicLinkObject->NtClose(); + + return result; +} + + +NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::string aFullPath) +{ + NTSTATUS result = STATUS_OBJECT_NAME_INVALID; + int i = 0; + int DeviceIndex = 0; + + // If aFullPath is an empty string, set it to the CD-ROM drive + // This should work for all titles, as CD-ROM is mapped to the current working directory + // This fixes the issue where titles crash after being launched from the update.xbe + if (aFullPath.length() == 0) { + aFullPath = DeviceCdrom0; + } + + DriveLetter = SymbolicLinkToDriveLetter(aSymbolicLinkName); + if (DriveLetter >= 'A' && DriveLetter <= 'Z') + { + result = STATUS_OBJECT_NAME_COLLISION; + if (FindNtSymbolicLinkObjectByDriveLetter(DriveLetter) == NULL) + { + // Look up the partition in the list of pre-registered devices : + result = STATUS_DEVICE_DOES_NOT_EXIST; // TODO : Is this the correct error? + + // If aFullPath starts with a Drive letter, find the originating path and substitute that + if (aFullPath[1] == ':' && aFullPath[0] >= 'A' && aFullPath[0] <= 'Z') { + EmuNtSymbolicLinkObject* DriveLetterLink = FindNtSymbolicLinkObjectByDriveLetter(aFullPath[0]); + if (DriveLetterLink != NULL) { + aFullPath = DriveLetterLink->XboxSymbolicLinkPath; + } + } + + // Make a distinction between Xbox paths (starting with '\Device'...) and host paths : + IsHostBasedPath = _strnicmp(aFullPath.c_str(), DevicePrefix.c_str(), DevicePrefix.length()) != 0; + if (IsHostBasedPath) + DeviceIndex = CxbxDefaultXbeDriveIndex; + else + DeviceIndex = CxbxDeviceIndexByDevicePath(aFullPath.c_str()); + + if (DeviceIndex >= 0) + { + result = STATUS_SUCCESS; + SymbolicLinkName = aSymbolicLinkName; + if (IsHostBasedPath) + { + XboxSymbolicLinkPath = ""; + HostSymbolicLinkPath = aFullPath; + } + else + { + XboxSymbolicLinkPath = aFullPath; + HostSymbolicLinkPath = Devices[DeviceIndex].HostDevicePath; + // Handle the case where a sub folder of the partition is mounted (instead of it's root) : + std::string ExtraPath = aFullPath.substr(Devices[DeviceIndex].XboxDevicePath.length(), std::string::npos); + + if (!ExtraPath.empty()) + HostSymbolicLinkPath = HostSymbolicLinkPath + ExtraPath; + } + + SHCreateDirectoryEx(NULL, HostSymbolicLinkPath.c_str(), NULL); + RootDirectoryHandle = CreateFile(HostSymbolicLinkPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (RootDirectoryHandle == INVALID_HANDLE_VALUE) + { + result = STATUS_DEVICE_DOES_NOT_EXIST; // TODO : Is this the correct error? + CxbxKrnlCleanup((std::string("Could not map ") + HostSymbolicLinkPath).c_str()); + } + else + { + NtSymbolicLinkObjects[DriveLetter - 'A'] = this; + EmuLog(LOG_LEVEL::DEBUG, "Linked \"%s\" to \"%s\" (residing at \"%s\")", aSymbolicLinkName.c_str(), aFullPath.c_str(), HostSymbolicLinkPath.c_str()); + } + } + } + } + + return result; +} + + EmuNtSymbolicLinkObject::~EmuNtSymbolicLinkObject() +{ + if (DriveLetter >= 'A' && DriveLetter <= 'Z') { + NtSymbolicLinkObjects[DriveLetter - 'A'] = NULL; + NtDll::NtClose(RootDirectoryHandle); + } +} + +char SymbolicLinkToDriveLetter(std::string SymbolicLinkName) +{ + char result = '\0'; + // SymbolicLinkName must look like this : "\??\D:" + if ((SymbolicLinkName.size() == 6) && (SymbolicLinkName[0] == '\\') && (SymbolicLinkName[1] == '?') && (SymbolicLinkName[2] == '?') && (SymbolicLinkName[3] == '\\') && (SymbolicLinkName[5] == ':')) + { + result = SymbolicLinkName[4]; + if (result >= 'A' && result <= 'Z') + return result; + + if (result >= 'a' && result <= 'z') { + return result + 'A' - 'a'; + } + } + + return NULL; +} + +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveLetter) +{ + if (DriveLetter >= 'A' && DriveLetter <= 'Z') + return NtSymbolicLinkObjects[DriveLetter - 'A']; + + if (DriveLetter >= 'a' && DriveLetter <= 'z') + return NtSymbolicLinkObjects[DriveLetter - 'a']; + + return NULL; +} + +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName) +{ + return FindNtSymbolicLinkObjectByDriveLetter(SymbolicLinkToDriveLetter(SymbolicLinkName)); +} + + +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDevice(std::string DeviceName) +{ + for (char DriveLetter = 'A'; DriveLetter <= 'Z'; DriveLetter++) + { + EmuNtSymbolicLinkObject* result = NtSymbolicLinkObjects[DriveLetter - 'A']; + if ((result != NULL) && _strnicmp(DeviceName.c_str(), result->XboxSymbolicLinkPath.c_str(), result->XboxSymbolicLinkPath.length()) == 0) + return result; + } + + return NULL; +} + + +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(const HANDLE Handle) +{ + for (char DriveLetter = 'A'; DriveLetter <= 'Z'; DriveLetter++) + { + EmuNtSymbolicLinkObject* result = NtSymbolicLinkObjects[DriveLetter - 'A']; + if ((result != NULL) && (Handle == result->RootDirectoryHandle)) + return result; + } + + return NULL; +} + + +void _CxbxPVOIDDeleter(PVOID *ptr) +{ + if (*ptr) + g_VMManager.Deallocate((VAddr)*ptr); +} + +// ---------------------------------------------------------------------------- +// Xbox to NT converters +// ---------------------------------------------------------------------------- + +NtDll::FILE_LINK_INFORMATION * _XboxToNTLinkInfo(xboxkrnl::FILE_LINK_INFORMATION *xboxLinkInfo, ULONG *Length) +{ + // Convert the path from Xbox to native + std::string originalFileName(xboxLinkInfo->FileName, xboxLinkInfo->FileNameLength); + std::wstring convertedFileName; + NtDll::HANDLE RootDirectory = nullptr; + NTSTATUS res = CxbxConvertFilePath(originalFileName, /*OUT*/convertedFileName, /*IN OUT*/&RootDirectory, "NtSetInformationFile"); + // TODO : handle if(FAILED(res)) + + // Build the native FILE_LINK_INFORMATION struct + *Length = sizeof(NtDll::FILE_LINK_INFORMATION) + convertedFileName.size() * sizeof(wchar_t); + NtDll::FILE_LINK_INFORMATION *ntLinkInfo = (NtDll::FILE_LINK_INFORMATION *) g_VMManager.AllocateZeroed(*Length); + ntLinkInfo->ReplaceIfExists = xboxLinkInfo->ReplaceIfExists; + ntLinkInfo->RootDirectory = RootDirectory; + ntLinkInfo->FileNameLength = convertedFileName.size() * sizeof(wchar_t); + wmemcpy_s(ntLinkInfo->FileName, convertedFileName.size(), convertedFileName.c_str(), convertedFileName.size()); + + return ntLinkInfo; +} + +NtDll::FILE_RENAME_INFORMATION * _XboxToNTRenameInfo(xboxkrnl::FILE_RENAME_INFORMATION *xboxRenameInfo, ULONG *Length) +{ + // Convert the path from Xbox to native + std::string originalFileName(xboxRenameInfo->FileName.Buffer, xboxRenameInfo->FileName.Length); + std::wstring convertedFileName; + NtDll::HANDLE RootDirectory = nullptr; + NTSTATUS res = CxbxConvertFilePath(originalFileName, /*OUT*/convertedFileName, /*IN OUT*/&RootDirectory, "NtSetInformationFile"); + // TODO : handle if(FAILED(res)) + + // Build the native FILE_RENAME_INFORMATION struct + *Length = sizeof(NtDll::FILE_RENAME_INFORMATION) + convertedFileName.size() * sizeof(wchar_t); + NtDll::FILE_RENAME_INFORMATION *ntRenameInfo = (NtDll::FILE_RENAME_INFORMATION *) g_VMManager.AllocateZeroed(*Length); + ntRenameInfo->ReplaceIfExists = xboxRenameInfo->ReplaceIfExists; + ntRenameInfo->RootDirectory = RootDirectory; + ntRenameInfo->FileNameLength = convertedFileName.size() * sizeof(wchar_t); + wmemcpy_s(ntRenameInfo->FileName, convertedFileName.size(), convertedFileName.c_str(), convertedFileName.size()); + + return ntRenameInfo; +} + +// ---------------------------------------------------------------------------- +// NT to Xbox converters - common functions +// ---------------------------------------------------------------------------- + +void _ConvertXboxBasicInfo(xboxkrnl::FILE_BASIC_INFORMATION *xboxBasicInfo) +{ + // Fix up attributes + xboxBasicInfo->FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS; +} + +NTSTATUS _ConvertXboxNameInfo(NtDll::FILE_NAME_INFORMATION *ntNameInfo, xboxkrnl::FILE_NAME_INFORMATION *xboxNameInfo, int convertedFileNameLength, ULONG Length) +{ + // Convert the file name to ANSI in-place + xboxNameInfo->FileNameLength = convertedFileNameLength; + + // Check if the new file name fits within the given struct size and copy as many chars as possible if not + int maxFileNameLength = Length - sizeof(xboxkrnl::FILE_NAME_INFORMATION); + size_t convertedChars; + wcstombs_s(&convertedChars, xboxNameInfo->FileName, maxFileNameLength, ntNameInfo->FileName, ntNameInfo->FileNameLength); + + // Return the appropriate result depending on whether the string was fully copied + return convertedChars == ntNameInfo->FileNameLength / sizeof(NtDll::WCHAR) + ? STATUS_SUCCESS + : STATUS_BUFFER_OVERFLOW; +} + +// ---------------------------------------------------------------------------- +// NT to Xbox converters +// ---------------------------------------------------------------------------- + +NTSTATUS _NTToXboxNetOpenInfo(NtDll::FILE_NETWORK_OPEN_INFORMATION *ntNetOpenInfo, xboxkrnl::FILE_NETWORK_OPEN_INFORMATION *xboxNetOpenInfo, ULONG Length) +{ + // Copy everything from the NT struct + memcpy_s(xboxNetOpenInfo, Length, ntNetOpenInfo, sizeof(NtDll::FILE_NETWORK_OPEN_INFORMATION)); + + // Fix up attributes + xboxNetOpenInfo->FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS; + + return STATUS_SUCCESS; +} + +NTSTATUS _NTToXboxBasicInfo(NtDll::FILE_BASIC_INFORMATION *ntBasicInfo, xboxkrnl::FILE_BASIC_INFORMATION *xboxBasicInfo, ULONG Length) +{ + // Copy everything from the NT struct + memcpy_s(xboxBasicInfo, sizeof(xboxkrnl::FILE_BASIC_INFORMATION), ntBasicInfo, sizeof(NtDll::FILE_BASIC_INFORMATION)); + + _ConvertXboxBasicInfo(xboxBasicInfo); + + return STATUS_SUCCESS; +} + +NTSTATUS _NTToXboxNameInfo(NtDll::FILE_NAME_INFORMATION *ntNameInfo, xboxkrnl::FILE_NAME_INFORMATION *xboxNameInfo, ULONG Length) +{ + // TODO: the FileName probably needs to be converted back to an Xbox path in some cases + // Determine new file name length + size_t convertedFileNameLength = ntNameInfo->FileNameLength / sizeof(NtDll::WCHAR); + + // Clean up the Xbox FILE_NAME_INFORMATION struct + ZeroMemory(xboxNameInfo, Length); + + // Convert file name and return the result + return _ConvertXboxNameInfo(ntNameInfo, xboxNameInfo, convertedFileNameLength, Length); +} + +NTSTATUS _NTToXboxAllInfo(NtDll::FILE_ALL_INFORMATION *ntAllInfo, xboxkrnl::FILE_ALL_INFORMATION *xboxAllInfo, ULONG Length) +{ + // TODO: the FileName probably needs to be converted back to an Xbox path in some cases + // Determine new file name length + size_t convertedFileNameLength = ntAllInfo->NameInformation.FileNameLength / sizeof(NtDll::WCHAR); + + // Copy everything from the NT struct + memcpy_s(xboxAllInfo, Length, ntAllInfo, sizeof(NtDll::FILE_ALL_INFORMATION)); + + // Convert NT structs to Xbox where needed and return the result + _ConvertXboxBasicInfo(&xboxAllInfo->BasicInformation); + return _ConvertXboxNameInfo(&ntAllInfo->NameInformation, &xboxAllInfo->NameInformation, convertedFileNameLength, Length); +} + +// ---------------------------------------------------------------------------- +// File information struct converters +// ---------------------------------------------------------------------------- + +PVOID _XboxToNTFileInformation +( + IN PVOID xboxFileInformation, + IN ULONG FileInformationClass, + OUT ULONG *Length +) +{ + // The following classes of file information structs are identical between platforms: + // FileBasicInformation + // FileDispositionInformation + // FileEndOfFileInformation + // FileLinkInformation + // FilePositionInformation + + PVOID result = NULL; + + switch (FileInformationClass) + { + case xboxkrnl::FileLinkInformation: + { + xboxkrnl::FILE_LINK_INFORMATION *xboxLinkInfo = reinterpret_cast(xboxFileInformation); + result = _XboxToNTLinkInfo(xboxLinkInfo, Length); + break; + } + case xboxkrnl::FileRenameInformation: + { + xboxkrnl::FILE_RENAME_INFORMATION *xboxRenameInfo = reinterpret_cast(xboxFileInformation); + result = _XboxToNTRenameInfo(xboxRenameInfo, Length); + break; + } + default: + { + result = NULL; + break; + } + } + + return result; +} + +NTSTATUS NTToXboxFileInformation +( + IN PVOID nativeFileInformation, + OUT PVOID xboxFileInformation, + IN ULONG FileInformationClass, + IN ULONG Length +) +{ + // The following classes of file information structs are identical between platforms: + // FileAccessInformation + // FileAlignmentInformation + // FileEaInformation + // FileInternalInformation + // FileModeInformation + // FilePositionInformation + // FileStandardInformation + // FileReparsePointInformation + + NTSTATUS result = STATUS_SUCCESS; + + switch (FileInformationClass) + { + case NtDll::FileAllInformation: + { + NtDll::FILE_ALL_INFORMATION *ntAllInfo = reinterpret_cast(nativeFileInformation); + xboxkrnl::FILE_ALL_INFORMATION *xboxAllInfo = reinterpret_cast(xboxFileInformation); + result = _NTToXboxAllInfo(ntAllInfo, xboxAllInfo, Length); + break; + } + case NtDll::FileBasicInformation: + { + NtDll::FILE_BASIC_INFORMATION *ntBasicInfo = reinterpret_cast(nativeFileInformation); + xboxkrnl::FILE_BASIC_INFORMATION *xboxBasicInfo = reinterpret_cast(xboxFileInformation); + result = _NTToXboxBasicInfo(ntBasicInfo, xboxBasicInfo, Length); + break; + } + case NtDll::FileNameInformation: + { + NtDll::FILE_NAME_INFORMATION *ntNameInfo = reinterpret_cast(nativeFileInformation); + xboxkrnl::FILE_NAME_INFORMATION *xboxNameInfo = reinterpret_cast(xboxFileInformation); + result = _NTToXboxNameInfo(ntNameInfo, xboxNameInfo, Length); + break; + } + case NtDll::FileNetworkOpenInformation: + { + NtDll::FILE_NETWORK_OPEN_INFORMATION *ntNetOpenInfo = reinterpret_cast(nativeFileInformation); + xboxkrnl::FILE_NETWORK_OPEN_INFORMATION *xboxNetOpenInfo = reinterpret_cast(xboxFileInformation); + result = _NTToXboxNetOpenInfo(ntNetOpenInfo, xboxNetOpenInfo, Length); + break; + } + case NtDll::FileBothDirectoryInformation: + { + // TODO: handle differences + // - Xbox reuses the FILE_DIRECTORY_INFORMATION struct, which is mostly identical to FILE_BOTH_DIR_INFORMATION + // - NextEntryOffset might be a problem + // - NT has extra fields before FileName: + // - ULONG EaSize + // - CCHAR ShortNameLength + // - WCHAR ShortName[12] + // - FileName on Xbox uses single-byte chars, NT uses wide chars + + //break; + } + case NtDll::FileDirectoryInformation: + { + // TODO: handle differences + // - NextEntryOffset might be a problem + // - FileName on Xbox uses single-byte chars, NT uses wide chars + + //break; + } + case NtDll::FileFullDirectoryInformation: + { + // TODO: handle differences + // - Xbox reuses the FILE_DIRECTORY_INFORMATION struct, which is mostly identical to FILE_FULL_DIR_INFORMATION + // - NextEntryOffset might be a problem + // - NT has one extra field before FileName: + // - ULONG EaSize + // - FileName on Xbox uses single-byte chars, NT uses wide chars + + //break; + } + case NtDll::FileNamesInformation: + { + // TODO: handle differences + // - NextEntryOffset might be a problem + // - FileName on Xbox uses single-byte chars, NT uses wide chars + + //break; + } + case NtDll::FileObjectIdInformation: + { + // TODO: handle differences + // - The LONGLONG FileReference field from NT can be ignored + // - ExtendedInfo is an union on NT, but is otherwise identical + + //break; + } + default: + { + // No differences between structs; a simple copy should suffice + memcpy_s(xboxFileInformation, Length, nativeFileInformation, Length); + result = STATUS_SUCCESS; + break; + } + } + + return result; +} + +// TODO: FS_INFORMATION_CLASS and its related structs most likely need to be converted too + +// TODO : Move to a better suited file +/* TODO : Also, fix C2593: "'operator <<' is ambiguous" for this +std::ostream& operator<<(std::ostream& os, const NtDll::NTSTATUS& value) +{ + os << hex4((uint32_t)value) << " = " << NtStatusToString(value); + + return os; +} +*/ + +// TODO : Create (and use) an Xbox version of this too +CHAR* NtStatusToString(IN NTSTATUS Status) +{ +#define _CASE(s) case s: return #s; + + switch (Status) + { + // Note : Keep all cases sorted, for easier maintenance + _CASE(DBG_APP_NOT_IDLE); + _CASE(DBG_CONTINUE); + _CASE(DBG_CONTROL_BREAK); + _CASE(DBG_CONTROL_C); + _CASE(DBG_EXCEPTION_HANDLED); + _CASE(DBG_EXCEPTION_NOT_HANDLED); + _CASE(DBG_NO_STATE_CHANGE); + _CASE(DBG_PRINTEXCEPTION_C); + _CASE(DBG_REPLY_LATER); + _CASE(DBG_RIPEXCEPTION); + _CASE(DBG_TERMINATE_PROCESS); + _CASE(DBG_TERMINATE_THREAD); + _CASE(DBG_UNABLE_TO_PROVIDE_HANDLE); + _CASE(EPT_NT_CANT_CREATE); + _CASE(EPT_NT_CANT_PERFORM_OP); + _CASE(EPT_NT_INVALID_ENTRY); + _CASE(EPT_NT_NOT_REGISTERED); + _CASE(RPC_NT_ADDRESS_ERROR); + _CASE(RPC_NT_ALREADY_LISTENING); + _CASE(RPC_NT_ALREADY_REGISTERED); + _CASE(RPC_NT_BAD_STUB_DATA); + _CASE(RPC_NT_BINDING_HAS_NO_AUTH); + _CASE(RPC_NT_BINDING_INCOMPLETE); + _CASE(RPC_NT_BYTE_COUNT_TOO_SMALL); + _CASE(RPC_NT_CALL_CANCELLED); + _CASE(RPC_NT_CALL_FAILED); + _CASE(RPC_NT_CALL_FAILED_DNE); + _CASE(RPC_NT_CALL_IN_PROGRESS); + _CASE(RPC_NT_CANNOT_SUPPORT); + _CASE(RPC_NT_CANT_CREATE_ENDPOINT); + _CASE(RPC_NT_COMM_FAILURE); + _CASE(RPC_NT_DUPLICATE_ENDPOINT); + _CASE(RPC_NT_ENTRY_ALREADY_EXISTS); + _CASE(RPC_NT_ENTRY_NOT_FOUND); + _CASE(RPC_NT_ENUM_VALUE_OUT_OF_RANGE); + _CASE(RPC_NT_FP_DIV_ZERO); + _CASE(RPC_NT_FP_OVERFLOW); + _CASE(RPC_NT_FP_UNDERFLOW); + _CASE(RPC_NT_GROUP_MEMBER_NOT_FOUND); + _CASE(RPC_NT_INCOMPLETE_NAME); + _CASE(RPC_NT_INTERFACE_NOT_FOUND); + _CASE(RPC_NT_INTERNAL_ERROR); + _CASE(RPC_NT_INVALID_ASYNC_CALL); + _CASE(RPC_NT_INVALID_ASYNC_HANDLE); + _CASE(RPC_NT_INVALID_AUTH_IDENTITY); + _CASE(RPC_NT_INVALID_BINDING); + _CASE(RPC_NT_INVALID_BOUND); + _CASE(RPC_NT_INVALID_ENDPOINT_FORMAT); + _CASE(RPC_NT_INVALID_ES_ACTION); + _CASE(RPC_NT_INVALID_NAF_ID); + _CASE(RPC_NT_INVALID_NAME_SYNTAX); + _CASE(RPC_NT_INVALID_NETWORK_OPTIONS); + _CASE(RPC_NT_INVALID_NET_ADDR); + _CASE(RPC_NT_INVALID_OBJECT); + _CASE(RPC_NT_INVALID_PIPE_OBJECT); + _CASE(RPC_NT_INVALID_PIPE_OPERATION); + _CASE(RPC_NT_INVALID_RPC_PROTSEQ); + _CASE(RPC_NT_INVALID_STRING_BINDING); + _CASE(RPC_NT_INVALID_STRING_UUID); + _CASE(RPC_NT_INVALID_TAG); + _CASE(RPC_NT_INVALID_TIMEOUT); + _CASE(RPC_NT_INVALID_VERS_OPTION); + _CASE(RPC_NT_MAX_CALLS_TOO_SMALL); + _CASE(RPC_NT_NAME_SERVICE_UNAVAILABLE); + _CASE(RPC_NT_NOTHING_TO_EXPORT); + _CASE(RPC_NT_NOT_ALL_OBJS_UNEXPORTED); + _CASE(RPC_NT_NOT_CANCELLED); + _CASE(RPC_NT_NOT_LISTENING); + _CASE(RPC_NT_NOT_RPC_ERROR); + _CASE(RPC_NT_NO_BINDINGS); + _CASE(RPC_NT_NO_CALL_ACTIVE); + _CASE(RPC_NT_NO_CONTEXT_AVAILABLE); + _CASE(RPC_NT_NO_ENDPOINT_FOUND); + _CASE(RPC_NT_NO_ENTRY_NAME); + _CASE(RPC_NT_NO_INTERFACES); + _CASE(RPC_NT_NO_MORE_BINDINGS); + _CASE(RPC_NT_NO_MORE_ENTRIES); + _CASE(RPC_NT_NO_MORE_MEMBERS); + _CASE(RPC_NT_NO_PRINC_NAME); + _CASE(RPC_NT_NO_PROTSEQS); + _CASE(RPC_NT_NO_PROTSEQS_REGISTERED); + _CASE(RPC_NT_NULL_REF_POINTER); + _CASE(RPC_NT_OBJECT_NOT_FOUND); + _CASE(RPC_NT_OUT_OF_RESOURCES); + _CASE(RPC_NT_PIPE_CLOSED); + _CASE(RPC_NT_PIPE_DISCIPLINE_ERROR); + _CASE(RPC_NT_PIPE_EMPTY); + _CASE(RPC_NT_PROCNUM_OUT_OF_RANGE); + _CASE(RPC_NT_PROTOCOL_ERROR); + _CASE(RPC_NT_PROTSEQ_NOT_FOUND); + _CASE(RPC_NT_PROTSEQ_NOT_SUPPORTED); + _CASE(RPC_NT_SEC_PKG_ERROR); + _CASE(RPC_NT_SEND_INCOMPLETE); + _CASE(RPC_NT_SERVER_TOO_BUSY); + _CASE(RPC_NT_SERVER_UNAVAILABLE); + _CASE(RPC_NT_SS_CANNOT_GET_CALL_HANDLE); + _CASE(RPC_NT_SS_CHAR_TRANS_OPEN_FAIL); + _CASE(RPC_NT_SS_CHAR_TRANS_SHORT_FILE); + _CASE(RPC_NT_SS_CONTEXT_DAMAGED); + _CASE(RPC_NT_SS_CONTEXT_MISMATCH); + _CASE(RPC_NT_SS_HANDLES_MISMATCH); + _CASE(RPC_NT_SS_IN_NULL_CONTEXT); + _CASE(RPC_NT_STRING_TOO_LONG); + _CASE(RPC_NT_TYPE_ALREADY_REGISTERED); + _CASE(RPC_NT_UNKNOWN_AUTHN_LEVEL); + _CASE(RPC_NT_UNKNOWN_AUTHN_SERVICE); + _CASE(RPC_NT_UNKNOWN_AUTHN_TYPE); + _CASE(RPC_NT_UNKNOWN_AUTHZ_SERVICE); + _CASE(RPC_NT_UNKNOWN_IF); + _CASE(RPC_NT_UNKNOWN_MGR_TYPE); + _CASE(RPC_NT_UNSUPPORTED_AUTHN_LEVEL); + _CASE(RPC_NT_UNSUPPORTED_NAME_SYNTAX); + _CASE(RPC_NT_UNSUPPORTED_TRANS_SYN); + _CASE(RPC_NT_UNSUPPORTED_TYPE); + _CASE(RPC_NT_UUID_LOCAL_ONLY); + _CASE(RPC_NT_UUID_NO_ADDRESS); + _CASE(RPC_NT_WRONG_ES_VERSION); + _CASE(RPC_NT_WRONG_KIND_OF_BINDING); + _CASE(RPC_NT_WRONG_PIPE_VERSION); + _CASE(RPC_NT_WRONG_STUB_VERSION); + _CASE(RPC_NT_ZERO_DIVIDE); + _CASE(STATUS_ABANDONED_WAIT_0); + _CASE(STATUS_ABANDONED_WAIT_63); + _CASE(STATUS_ABIOS_INVALID_COMMAND); + _CASE(STATUS_ABIOS_INVALID_LID); + _CASE(STATUS_ABIOS_INVALID_SELECTOR); + _CASE(STATUS_ABIOS_LID_ALREADY_OWNED); + _CASE(STATUS_ABIOS_LID_NOT_EXIST); + _CASE(STATUS_ABIOS_NOT_LID_OWNER); + _CASE(STATUS_ABIOS_NOT_PRESENT); + _CASE(STATUS_ABIOS_SELECTOR_NOT_AVAILABLE); + _CASE(STATUS_ACCESS_DENIED); + _CASE(STATUS_ACCESS_VIOLATION); + _CASE(STATUS_ACCOUNT_DISABLED); + _CASE(STATUS_ACCOUNT_EXPIRED); + _CASE(STATUS_ACCOUNT_LOCKED_OUT); + _CASE(STATUS_ACCOUNT_RESTRICTION); + _CASE(STATUS_ACPI_ACQUIRE_GLOBAL_LOCK); + _CASE(STATUS_ACPI_ADDRESS_NOT_MAPPED); + _CASE(STATUS_ACPI_ALREADY_INITIALIZED); + _CASE(STATUS_ACPI_ASSERT_FAILED); + _CASE(STATUS_ACPI_FATAL); + _CASE(STATUS_ACPI_HANDLER_COLLISION); + _CASE(STATUS_ACPI_INCORRECT_ARGUMENT_COUNT); + _CASE(STATUS_ACPI_INVALID_ACCESS_SIZE); + _CASE(STATUS_ACPI_INVALID_ARGTYPE); + _CASE(STATUS_ACPI_INVALID_ARGUMENT); + _CASE(STATUS_ACPI_INVALID_DATA); + _CASE(STATUS_ACPI_INVALID_EVENTTYPE); + _CASE(STATUS_ACPI_INVALID_INDEX); + _CASE(STATUS_ACPI_INVALID_MUTEX_LEVEL); + _CASE(STATUS_ACPI_INVALID_OBJTYPE); + _CASE(STATUS_ACPI_INVALID_OPCODE); + _CASE(STATUS_ACPI_INVALID_REGION); + _CASE(STATUS_ACPI_INVALID_SUPERNAME); + _CASE(STATUS_ACPI_INVALID_TABLE); + _CASE(STATUS_ACPI_INVALID_TARGETTYPE); + _CASE(STATUS_ACPI_MUTEX_NOT_OWNED); + _CASE(STATUS_ACPI_MUTEX_NOT_OWNER); + _CASE(STATUS_ACPI_NOT_INITIALIZED); + _CASE(STATUS_ACPI_POWER_REQUEST_FAILED); + _CASE(STATUS_ACPI_REG_HANDLER_FAILED); + _CASE(STATUS_ACPI_RS_ACCESS); + _CASE(STATUS_ACPI_STACK_OVERFLOW); + _CASE(STATUS_ADAPTER_HARDWARE_ERROR); + _CASE(STATUS_ADDRESS_ALREADY_ASSOCIATED); + _CASE(STATUS_ADDRESS_ALREADY_EXISTS); + _CASE(STATUS_ADDRESS_CLOSED); + _CASE(STATUS_ADDRESS_NOT_ASSOCIATED); + _CASE(STATUS_AGENTS_EXHAUSTED); + _CASE(STATUS_ALERTED); + _CASE(STATUS_ALIAS_EXISTS); + _CASE(STATUS_ALLOCATE_BUCKET); + _CASE(STATUS_ALLOTTED_SPACE_EXCEEDED); + _CASE(STATUS_ALREADY_COMMITTED); + _CASE(STATUS_ALREADY_DISCONNECTED); + _CASE(STATUS_ALREADY_WIN32); + _CASE(STATUS_APP_INIT_FAILURE); + _CASE(STATUS_ARBITRATION_UNHANDLED); + _CASE(STATUS_ARRAY_BOUNDS_EXCEEDED); + _CASE(STATUS_AUDIT_FAILED); + _CASE(STATUS_BACKUP_CONTROLLER); + _CASE(STATUS_BAD_COMPRESSION_BUFFER); + _CASE(STATUS_BAD_CURRENT_DIRECTORY); + _CASE(STATUS_BAD_DESCRIPTOR_FORMAT); + _CASE(STATUS_BAD_DEVICE_TYPE); + _CASE(STATUS_BAD_DLL_ENTRYPOINT); + _CASE(STATUS_BAD_FUNCTION_TABLE); + _CASE(STATUS_BAD_IMPERSONATION_LEVEL); + _CASE(STATUS_BAD_INHERITANCE_ACL); + _CASE(STATUS_BAD_INITIAL_PC); + _CASE(STATUS_BAD_INITIAL_STACK); + _CASE(STATUS_BAD_LOGON_SESSION_STATE); + _CASE(STATUS_BAD_MASTER_BOOT_RECORD); + _CASE(STATUS_BAD_NETWORK_NAME); + _CASE(STATUS_BAD_NETWORK_PATH); + _CASE(STATUS_BAD_REMOTE_ADAPTER); + _CASE(STATUS_BAD_SERVICE_ENTRYPOINT); + _CASE(STATUS_BAD_STACK); + _CASE(STATUS_BAD_TOKEN_TYPE); + _CASE(STATUS_BAD_VALIDATION_CLASS); + _CASE(STATUS_BAD_WORKING_SET_LIMIT); + _CASE(STATUS_BEGINNING_OF_MEDIA); + _CASE(STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT); + _CASE(STATUS_BREAKPOINT); + _CASE(STATUS_BUFFER_ALL_ZEROS); + _CASE(STATUS_BUFFER_OVERFLOW); + _CASE(STATUS_BUFFER_TOO_SMALL); + _CASE(STATUS_BUS_RESET); + _CASE(STATUS_CACHE_PAGE_LOCKED); + _CASE(STATUS_CANCELLED); + _CASE(STATUS_CANNOT_DELETE); + _CASE(STATUS_CANNOT_IMPERSONATE); + _CASE(STATUS_CANNOT_LOAD_REGISTRY_FILE); + _CASE(STATUS_CANT_ACCESS_DOMAIN_INFO); + _CASE(STATUS_CANT_DISABLE_MANDATORY); + _CASE(STATUS_CANT_ENABLE_DENY_ONLY); + _CASE(STATUS_CANT_OPEN_ANONYMOUS); + _CASE(STATUS_CANT_TERMINATE_SELF); + _CASE(STATUS_CANT_WAIT); + _CASE(STATUS_CARDBUS_NOT_SUPPORTED); + _CASE(STATUS_CHECKING_FILE_SYSTEM); + _CASE(STATUS_CHILD_MUST_BE_VOLATILE); + _CASE(STATUS_CLIENT_SERVER_PARAMETERS_INVALID); + _CASE(STATUS_COMMITMENT_LIMIT); + _CASE(STATUS_COMMITMENT_MINIMUM); + _CASE(STATUS_CONFLICTING_ADDRESSES); + _CASE(STATUS_CONNECTION_ABORTED); + _CASE(STATUS_CONNECTION_ACTIVE); + _CASE(STATUS_CONNECTION_COUNT_LIMIT); + _CASE(STATUS_CONNECTION_DISCONNECTED); + _CASE(STATUS_CONNECTION_INVALID); + _CASE(STATUS_CONNECTION_IN_USE); + _CASE(STATUS_CONNECTION_REFUSED); + _CASE(STATUS_CONNECTION_RESET); + _CASE(STATUS_CONTROL_C_EXIT); + _CASE(STATUS_CONVERT_TO_LARGE); + _CASE(STATUS_CORRUPT_SYSTEM_FILE); + _CASE(STATUS_COULD_NOT_INTERPRET); + _CASE(STATUS_CRASH_DUMP); + _CASE(STATUS_CRC_ERROR); + _CASE(STATUS_CTL_FILE_NOT_SUPPORTED); + _CASE(STATUS_CTX_BAD_VIDEO_MODE); + _CASE(STATUS_CTX_CDM_CONNECT); + _CASE(STATUS_CTX_CDM_DISCONNECT); + _CASE(STATUS_CTX_CLIENT_LICENSE_IN_USE); + _CASE(STATUS_CTX_CLIENT_LICENSE_NOT_SET); + _CASE(STATUS_CTX_CLIENT_QUERY_TIMEOUT); + _CASE(STATUS_CTX_CLOSE_PENDING); + _CASE(STATUS_CTX_CONSOLE_CONNECT); + _CASE(STATUS_CTX_CONSOLE_DISCONNECT); + _CASE(STATUS_CTX_GRAPHICS_INVALID); + _CASE(STATUS_CTX_INVALID_MODEMNAME); + _CASE(STATUS_CTX_INVALID_PD); + _CASE(STATUS_CTX_INVALID_WD); + _CASE(STATUS_CTX_LICENSE_CLIENT_INVALID); + _CASE(STATUS_CTX_LICENSE_EXPIRED); + _CASE(STATUS_CTX_LICENSE_NOT_AVAILABLE); + _CASE(STATUS_CTX_MODEM_INF_NOT_FOUND); + _CASE(STATUS_CTX_MODEM_RESPONSE_BUSY); + _CASE(STATUS_CTX_MODEM_RESPONSE_NO_CARRIER); + _CASE(STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE); + _CASE(STATUS_CTX_MODEM_RESPONSE_TIMEOUT); + _CASE(STATUS_CTX_MODEM_RESPONSE_VOICE); + _CASE(STATUS_CTX_NOT_CONSOLE); + _CASE(STATUS_CTX_NO_OUTBUF); + _CASE(STATUS_CTX_PD_NOT_FOUND); + _CASE(STATUS_CTX_RESPONSE_ERROR); + _CASE(STATUS_CTX_SHADOW_DENIED); + _CASE(STATUS_CTX_SHADOW_DISABLED); + _CASE(STATUS_CTX_SHADOW_INVALID); + _CASE(STATUS_CTX_TD_ERROR); + _CASE(STATUS_CTX_WD_NOT_FOUND); + _CASE(STATUS_CTX_WINSTATION_ACCESS_DENIED); + _CASE(STATUS_CTX_WINSTATION_BUSY); + _CASE(STATUS_CTX_WINSTATION_NAME_COLLISION); + _CASE(STATUS_CTX_WINSTATION_NAME_INVALID); + _CASE(STATUS_CTX_WINSTATION_NOT_FOUND); + _CASE(STATUS_DATATYPE_MISALIGNMENT); + _CASE(STATUS_DATATYPE_MISALIGNMENT_ERROR); + _CASE(STATUS_DATA_ERROR); + _CASE(STATUS_DATA_LATE_ERROR); + _CASE(STATUS_DATA_NOT_ACCEPTED); + _CASE(STATUS_DATA_OVERRUN); + _CASE(STATUS_DEBUG_ATTACH_FAILED); + _CASE(STATUS_DECRYPTION_FAILED); + _CASE(STATUS_DELETE_PENDING); + _CASE(STATUS_DESTINATION_ELEMENT_FULL); + _CASE(STATUS_DEVICE_ALREADY_ATTACHED); + _CASE(STATUS_DEVICE_BUSY); + _CASE(STATUS_DEVICE_CONFIGURATION_ERROR); + _CASE(STATUS_DEVICE_DATA_ERROR); + _CASE(STATUS_DEVICE_DOES_NOT_EXIST); + _CASE(STATUS_DEVICE_DOOR_OPEN); + _CASE(STATUS_DEVICE_NOT_CONNECTED); + _CASE(STATUS_DEVICE_NOT_PARTITIONED); + _CASE(STATUS_DEVICE_NOT_READY); + _CASE(STATUS_DEVICE_OFF_LINE); + _CASE(STATUS_DEVICE_PAPER_EMPTY); + _CASE(STATUS_DEVICE_POWERED_OFF); + _CASE(STATUS_DEVICE_POWER_FAILURE); + _CASE(STATUS_DEVICE_PROTOCOL_ERROR); + _CASE(STATUS_DEVICE_REMOVED); + _CASE(STATUS_DEVICE_REQUIRES_CLEANING); + _CASE(STATUS_DFS_EXIT_PATH_FOUND); + _CASE(STATUS_DFS_UNAVAILABLE); + _CASE(STATUS_DIRECTORY_IS_A_REPARSE_POINT); + _CASE(STATUS_DIRECTORY_NOT_EMPTY); + _CASE(STATUS_DIRECTORY_SERVICE_REQUIRED); + _CASE(STATUS_DISK_CORRUPT_ERROR); + _CASE(STATUS_DISK_FULL); + _CASE(STATUS_DISK_OPERATION_FAILED); + _CASE(STATUS_DISK_RECALIBRATE_FAILED); + _CASE(STATUS_DISK_RESET_FAILED); + _CASE(STATUS_DLL_INIT_FAILED); + _CASE(STATUS_DLL_INIT_FAILED_LOGOFF); + _CASE(STATUS_DLL_NOT_FOUND); + _CASE(STATUS_DOMAIN_CONTROLLER_NOT_FOUND); + _CASE(STATUS_DOMAIN_CTRLR_CONFIG_ERROR); + _CASE(STATUS_DOMAIN_EXISTS); + _CASE(STATUS_DOMAIN_LIMIT_EXCEEDED); + _CASE(STATUS_DOMAIN_TRUST_INCONSISTENT); + _CASE(STATUS_DRIVER_CANCEL_TIMEOUT); + _CASE(STATUS_DRIVER_ENTRYPOINT_NOT_FOUND); + _CASE(STATUS_DRIVER_FAILED_SLEEP); + _CASE(STATUS_DRIVER_INTERNAL_ERROR); + _CASE(STATUS_DRIVER_ORDINAL_NOT_FOUND); + _CASE(STATUS_DRIVER_UNABLE_TO_LOAD); + _CASE(STATUS_DS_ADMIN_LIMIT_EXCEEDED); + _CASE(STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS); + _CASE(STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED); + _CASE(STATUS_DS_BUSY); + _CASE(STATUS_DS_CANT_MOD_OBJ_CLASS); + _CASE(STATUS_DS_CANT_MOD_PRIMARYGROUPID); + _CASE(STATUS_DS_CANT_ON_NON_LEAF); + _CASE(STATUS_DS_CANT_ON_RDN); + _CASE(STATUS_DS_CANT_START); + _CASE(STATUS_DS_CROSS_DOM_MOVE_FAILED); + _CASE(STATUS_DS_GC_NOT_AVAILABLE); + _CASE(STATUS_DS_GC_REQUIRED); + _CASE(STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER); + _CASE(STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER); + _CASE(STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER); + _CASE(STATUS_DS_HAVE_PRIMARY_MEMBERS); + _CASE(STATUS_DS_INCORRECT_ROLE_OWNER); + _CASE(STATUS_DS_INIT_FAILURE); + _CASE(STATUS_DS_INVALID_ATTRIBUTE_SYNTAX); + _CASE(STATUS_DS_INVALID_GROUP_TYPE); + _CASE(STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER); + _CASE(STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY); + _CASE(STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED); + _CASE(STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY); + _CASE(STATUS_DS_NO_ATTRIBUTE_OR_VALUE); + _CASE(STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS); + _CASE(STATUS_DS_NO_MORE_RIDS); + _CASE(STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN); + _CASE(STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN); + _CASE(STATUS_DS_NO_RIDS_ALLOCATED); + _CASE(STATUS_DS_OBJ_CLASS_VIOLATION); + _CASE(STATUS_DS_RIDMGR_INIT_ERROR); + _CASE(STATUS_DS_SAM_INIT_FAILURE); + _CASE(STATUS_DS_SENSITIVE_GROUP_VIOLATION); + _CASE(STATUS_DS_UNAVAILABLE); + _CASE(STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER); + _CASE(STATUS_DUPLICATE_NAME); + _CASE(STATUS_DUPLICATE_OBJECTID); + _CASE(STATUS_EAS_NOT_SUPPORTED); + _CASE(STATUS_EA_CORRUPT_ERROR); + _CASE(STATUS_EA_LIST_INCONSISTENT); + _CASE(STATUS_EA_TOO_LARGE); + _CASE(STATUS_ENCRYPTION_FAILED); + _CASE(STATUS_END_OF_FILE); + _CASE(STATUS_END_OF_MEDIA); + _CASE(STATUS_ENTRYPOINT_NOT_FOUND); + _CASE(STATUS_EOM_OVERFLOW); + _CASE(STATUS_EVALUATION_EXPIRATION); + _CASE(STATUS_EVENTLOG_CANT_START); + _CASE(STATUS_EVENTLOG_FILE_CHANGED); + _CASE(STATUS_EVENTLOG_FILE_CORRUPT); + _CASE(STATUS_EVENT_DONE); + _CASE(STATUS_EVENT_PENDING); + _CASE(STATUS_EXTRANEOUS_INFORMATION); + _CASE(STATUS_FAIL_CHECK); + _CASE(STATUS_FATAL_APP_EXIT); + _CASE(STATUS_FILEMARK_DETECTED); + _CASE(STATUS_FILES_OPEN); + _CASE(STATUS_FILE_CLOSED); + _CASE(STATUS_FILE_CORRUPT_ERROR); + _CASE(STATUS_FILE_DELETED); + _CASE(STATUS_FILE_ENCRYPTED); + _CASE(STATUS_FILE_FORCED_CLOSED); + _CASE(STATUS_FILE_INVALID); + _CASE(STATUS_FILE_IS_A_DIRECTORY); + _CASE(STATUS_FILE_IS_OFFLINE); + _CASE(STATUS_FILE_LOCK_CONFLICT); + _CASE(STATUS_FILE_NOT_ENCRYPTED); + _CASE(STATUS_FILE_RENAMED); + _CASE(STATUS_FLOAT_DENORMAL_OPERAND); + _CASE(STATUS_FLOAT_DIVIDE_BY_ZERO); + _CASE(STATUS_FLOAT_INEXACT_RESULT); + _CASE(STATUS_FLOAT_INVALID_OPERATION); + _CASE(STATUS_FLOAT_MULTIPLE_FAULTS); + _CASE(STATUS_FLOAT_MULTIPLE_TRAPS); + _CASE(STATUS_FLOAT_OVERFLOW); + _CASE(STATUS_FLOAT_STACK_CHECK); + _CASE(STATUS_FLOAT_UNDERFLOW); + _CASE(STATUS_FLOPPY_BAD_REGISTERS); + _CASE(STATUS_FLOPPY_ID_MARK_NOT_FOUND); + _CASE(STATUS_FLOPPY_UNKNOWN_ERROR); + _CASE(STATUS_FLOPPY_VOLUME); + _CASE(STATUS_FLOPPY_WRONG_CYLINDER); + _CASE(STATUS_FOUND_OUT_OF_SCOPE); + _CASE(STATUS_FREE_VM_NOT_AT_BASE); + _CASE(STATUS_FS_DRIVER_REQUIRED); + _CASE(STATUS_FT_MISSING_MEMBER); + _CASE(STATUS_FT_ORPHANING); + _CASE(STATUS_FT_READ_RECOVERY_FROM_BACKUP); + _CASE(STATUS_FT_WRITE_RECOVERY); + _CASE(STATUS_FULLSCREEN_MODE); + _CASE(STATUS_GENERIC_NOT_MAPPED); + _CASE(STATUS_GRACEFUL_DISCONNECT); + _CASE(STATUS_GROUP_EXISTS); + _CASE(STATUS_GUARD_PAGE_VIOLATION); + _CASE(STATUS_GUIDS_EXHAUSTED); + _CASE(STATUS_GUID_SUBSTITUTION_MADE); + _CASE(STATUS_HANDLES_CLOSED); + _CASE(STATUS_HANDLE_NOT_CLOSABLE); + _CASE(STATUS_HOST_UNREACHABLE); + _CASE(STATUS_ILLEGAL_CHARACTER); + _CASE(STATUS_ILLEGAL_DLL_RELOCATION); + _CASE(STATUS_ILLEGAL_ELEMENT_ADDRESS); + _CASE(STATUS_ILLEGAL_FLOAT_CONTEXT); + _CASE(STATUS_ILLEGAL_FUNCTION); + _CASE(STATUS_ILLEGAL_INSTRUCTION); + _CASE(STATUS_ILL_FORMED_PASSWORD); + _CASE(STATUS_ILL_FORMED_SERVICE_ENTRY); + _CASE(STATUS_IMAGE_ALREADY_LOADED); + _CASE(STATUS_IMAGE_CHECKSUM_MISMATCH); + _CASE(STATUS_IMAGE_MACHINE_TYPE_MISMATCH); + _CASE(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE); + _CASE(STATUS_IMAGE_MP_UP_MISMATCH); + _CASE(STATUS_IMAGE_NOT_AT_BASE); + _CASE(STATUS_INCOMPATIBLE_FILE_MAP); + _CASE(STATUS_INFO_LENGTH_MISMATCH); + _CASE(STATUS_INSTANCE_NOT_AVAILABLE); + _CASE(STATUS_INSTRUCTION_MISALIGNMENT); + _CASE(STATUS_INSUFFICIENT_LOGON_INFO); + _CASE(STATUS_INSUFFICIENT_POWER); + _CASE(STATUS_INSUFFICIENT_RESOURCES); + _CASE(STATUS_INSUFF_SERVER_RESOURCES); + _CASE(STATUS_INTEGER_DIVIDE_BY_ZERO); + _CASE(STATUS_INTEGER_OVERFLOW); + _CASE(STATUS_INTERNAL_DB_CORRUPTION); + _CASE(STATUS_INTERNAL_DB_ERROR); + _CASE(STATUS_INTERNAL_ERROR); + _CASE(STATUS_INVALID_ACCOUNT_NAME); + _CASE(STATUS_INVALID_ACL); + _CASE(STATUS_INVALID_ADDRESS); + _CASE(STATUS_INVALID_ADDRESS_COMPONENT); + _CASE(STATUS_INVALID_ADDRESS_WILDCARD); + _CASE(STATUS_INVALID_BLOCK_LENGTH); + _CASE(STATUS_INVALID_BUFFER_SIZE); + _CASE(STATUS_INVALID_CID); + _CASE(STATUS_INVALID_COMPUTER_NAME); + _CASE(STATUS_INVALID_CONNECTION); + _CASE(STATUS_INVALID_DEVICE_REQUEST); + _CASE(STATUS_INVALID_DEVICE_STATE); + _CASE(STATUS_INVALID_DISPOSITION); + _CASE(STATUS_INVALID_DOMAIN_ROLE); + _CASE(STATUS_INVALID_DOMAIN_STATE); + _CASE(STATUS_INVALID_EA_FLAG); + _CASE(STATUS_INVALID_EA_NAME); + _CASE(STATUS_INVALID_FILE_FOR_SECTION); + _CASE(STATUS_INVALID_GROUP_ATTRIBUTES); + _CASE(STATUS_INVALID_HANDLE); + _CASE(STATUS_INVALID_HW_PROFILE); + _CASE(STATUS_INVALID_ID_AUTHORITY); + _CASE(STATUS_INVALID_IMAGE_FORMAT); + _CASE(STATUS_INVALID_IMAGE_LE_FORMAT); + _CASE(STATUS_INVALID_IMAGE_NE_FORMAT); + _CASE(STATUS_INVALID_IMAGE_NOT_MZ); + _CASE(STATUS_INVALID_IMAGE_PROTECT); + _CASE(STATUS_INVALID_IMAGE_WIN_16); + _CASE(STATUS_INVALID_INFO_CLASS); + _CASE(STATUS_INVALID_LDT_DESCRIPTOR); + _CASE(STATUS_INVALID_LDT_OFFSET); + _CASE(STATUS_INVALID_LDT_SIZE); + _CASE(STATUS_INVALID_LEVEL); + _CASE(STATUS_INVALID_LOCK_SEQUENCE); + _CASE(STATUS_INVALID_LOGON_HOURS); + _CASE(STATUS_INVALID_LOGON_TYPE); + _CASE(STATUS_INVALID_MEMBER); + _CASE(STATUS_INVALID_NETWORK_RESPONSE); + _CASE(STATUS_INVALID_OPLOCK_PROTOCOL); + _CASE(STATUS_INVALID_OWNER); + _CASE(STATUS_INVALID_PAGE_PROTECTION); + _CASE(STATUS_INVALID_PARAMETER); + _CASE(STATUS_INVALID_PARAMETER_1); + _CASE(STATUS_INVALID_PARAMETER_10); + _CASE(STATUS_INVALID_PARAMETER_11); + _CASE(STATUS_INVALID_PARAMETER_12); + _CASE(STATUS_INVALID_PARAMETER_2); + _CASE(STATUS_INVALID_PARAMETER_3); + _CASE(STATUS_INVALID_PARAMETER_4); + _CASE(STATUS_INVALID_PARAMETER_5); + _CASE(STATUS_INVALID_PARAMETER_6); + _CASE(STATUS_INVALID_PARAMETER_7); + _CASE(STATUS_INVALID_PARAMETER_8); + _CASE(STATUS_INVALID_PARAMETER_9); + _CASE(STATUS_INVALID_PARAMETER_MIX); + _CASE(STATUS_INVALID_PIPE_STATE); + _CASE(STATUS_INVALID_PLUGPLAY_DEVICE_PATH); + _CASE(STATUS_INVALID_PORT_ATTRIBUTES); + _CASE(STATUS_INVALID_PORT_HANDLE); + _CASE(STATUS_INVALID_PRIMARY_GROUP); + _CASE(STATUS_INVALID_QUOTA_LOWER); + _CASE(STATUS_INVALID_READ_MODE); + _CASE(STATUS_INVALID_SECURITY_DESCR); + _CASE(STATUS_INVALID_SERVER_STATE); + _CASE(STATUS_INVALID_SID); + _CASE(STATUS_INVALID_SUB_AUTHORITY); + _CASE(STATUS_INVALID_SYSTEM_SERVICE); + _CASE(STATUS_INVALID_UNWIND_TARGET); + _CASE(STATUS_INVALID_USER_BUFFER); + _CASE(STATUS_INVALID_VARIANT); + _CASE(STATUS_INVALID_VIEW_SIZE); + _CASE(STATUS_INVALID_VOLUME_LABEL); + _CASE(STATUS_INVALID_WORKSTATION); + _CASE(STATUS_IN_PAGE_ERROR); + _CASE(STATUS_IO_DEVICE_ERROR); + _CASE(STATUS_IO_PRIVILEGE_FAILED); + _CASE(STATUS_IO_REPARSE_DATA_INVALID); + _CASE(STATUS_IO_REPARSE_TAG_INVALID); + _CASE(STATUS_IO_REPARSE_TAG_MISMATCH); + _CASE(STATUS_IO_REPARSE_TAG_NOT_HANDLED); + _CASE(STATUS_IO_TIMEOUT); + _CASE(STATUS_IP_ADDRESS_CONFLICT1); + _CASE(STATUS_IP_ADDRESS_CONFLICT2); + _CASE(STATUS_JOURNAL_DELETE_IN_PROGRESS); + _CASE(STATUS_JOURNAL_ENTRY_DELETED); + _CASE(STATUS_JOURNAL_NOT_ACTIVE); + _CASE(STATUS_KERNEL_APC); + _CASE(STATUS_KEY_DELETED); + _CASE(STATUS_KEY_HAS_CHILDREN); + _CASE(STATUS_LAST_ADMIN); + _CASE(STATUS_LICENSE_QUOTA_EXCEEDED); + _CASE(STATUS_LICENSE_VIOLATION); + _CASE(STATUS_LINK_FAILED); + _CASE(STATUS_LINK_TIMEOUT); + _CASE(STATUS_LM_CROSS_ENCRYPTION_REQUIRED); + _CASE(STATUS_LOCAL_DISCONNECT); + _CASE(STATUS_LOCAL_USER_SESSION_KEY); + _CASE(STATUS_LOCK_NOT_GRANTED); + _CASE(STATUS_LOGIN_TIME_RESTRICTION); + _CASE(STATUS_LOGIN_WKSTA_RESTRICTION); + _CASE(STATUS_LOGON_FAILURE); + _CASE(STATUS_LOGON_NOT_GRANTED); + _CASE(STATUS_LOGON_SERVER_CONFLICT); + _CASE(STATUS_LOGON_SESSION_COLLISION); + _CASE(STATUS_LOGON_SESSION_EXISTS); + _CASE(STATUS_LOGON_TYPE_NOT_GRANTED); + _CASE(STATUS_LOG_FILE_FULL); + _CASE(STATUS_LOG_HARD_ERROR); + _CASE(STATUS_LONGJUMP); + _CASE(STATUS_LOST_WRITEBEHIND_DATA); + _CASE(STATUS_LPC_REPLY_LOST); + _CASE(STATUS_LUIDS_EXHAUSTED); + _CASE(STATUS_MAGAZINE_NOT_PRESENT); + _CASE(STATUS_MAPPED_ALIGNMENT); + _CASE(STATUS_MAPPED_FILE_SIZE_ZERO); + _CASE(STATUS_MARSHALL_OVERFLOW); + _CASE(STATUS_MEDIA_CHANGED); + _CASE(STATUS_MEDIA_CHECK); + _CASE(STATUS_MEDIA_WRITE_PROTECTED); + _CASE(STATUS_MEMBERS_PRIMARY_GROUP); + _CASE(STATUS_MEMBER_IN_ALIAS); + _CASE(STATUS_MEMBER_IN_GROUP); + _CASE(STATUS_MEMBER_NOT_IN_ALIAS); + _CASE(STATUS_MEMBER_NOT_IN_GROUP); + _CASE(STATUS_MEMORY_NOT_ALLOCATED); + _CASE(STATUS_MESSAGE_NOT_FOUND); + _CASE(STATUS_MISSING_SYSTEMFILE); + _CASE(STATUS_MORE_ENTRIES); + _CASE(STATUS_MORE_PROCESSING_REQUIRED); + _CASE(STATUS_MP_PROCESSOR_MISMATCH); + _CASE(STATUS_MULTIPLE_FAULT_VIOLATION); + _CASE(STATUS_MUTANT_LIMIT_EXCEEDED); + _CASE(STATUS_MUTANT_NOT_OWNED); + _CASE(STATUS_MUTUAL_AUTHENTICATION_FAILED); + _CASE(STATUS_NAME_TOO_LONG); + _CASE(STATUS_NETLOGON_NOT_STARTED); + _CASE(STATUS_NETWORK_ACCESS_DENIED); + _CASE(STATUS_NETWORK_BUSY); + _CASE(STATUS_NETWORK_CREDENTIAL_CONFLICT); + _CASE(STATUS_NETWORK_NAME_DELETED); + _CASE(STATUS_NETWORK_UNREACHABLE); + _CASE(STATUS_NET_WRITE_FAULT); + _CASE(STATUS_NOINTERFACE); + _CASE(STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT); + _CASE(STATUS_NOLOGON_SERVER_TRUST_ACCOUNT); + _CASE(STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT); + _CASE(STATUS_NONCONTINUABLE_EXCEPTION); + _CASE(STATUS_NONEXISTENT_EA_ENTRY); + _CASE(STATUS_NONEXISTENT_SECTOR); + _CASE(STATUS_NONE_MAPPED); + _CASE(STATUS_NOTIFY_CLEANUP); + _CASE(STATUS_NOTIFY_ENUM_DIR); + _CASE(STATUS_NOT_ALL_ASSIGNED); + _CASE(STATUS_NOT_A_DIRECTORY); + _CASE(STATUS_NOT_A_REPARSE_POINT); + _CASE(STATUS_NOT_CLIENT_SESSION); + _CASE(STATUS_NOT_COMMITTED); + _CASE(STATUS_NOT_EXPORT_FORMAT); + _CASE(STATUS_NOT_FOUND); + _CASE(STATUS_NOT_IMPLEMENTED); + _CASE(STATUS_NOT_LOCKED); + _CASE(STATUS_NOT_LOGON_PROCESS); + _CASE(STATUS_NOT_MAPPED_DATA); + _CASE(STATUS_NOT_MAPPED_VIEW); + _CASE(STATUS_NOT_REGISTRY_FILE); + _CASE(STATUS_NOT_SAME_DEVICE); + _CASE(STATUS_NOT_SERVER_SESSION); + _CASE(STATUS_NOT_SUPPORTED); + _CASE(STATUS_NOT_SUPPORTED_ON_SBS); + _CASE(STATUS_NOT_TINY_STREAM); + _CASE(STATUS_NO_BROWSER_SERVERS_FOUND); + _CASE(STATUS_NO_CALLBACK_ACTIVE); + _CASE(STATUS_NO_DATA_DETECTED); + _CASE(STATUS_NO_EAS_ON_FILE); + _CASE(STATUS_NO_EFS); + _CASE(STATUS_NO_EVENT_PAIR); + _CASE(STATUS_NO_GUID_TRANSLATION); + _CASE(STATUS_NO_IMPERSONATION_TOKEN); + _CASE(STATUS_NO_INHERITANCE); + _CASE(STATUS_NO_LDT); + _CASE(STATUS_NO_LOGON_SERVERS); + _CASE(STATUS_NO_LOG_SPACE); + _CASE(STATUS_NO_MATCH); + _CASE(STATUS_NO_MEDIA); + _CASE(STATUS_NO_MEDIA_IN_DEVICE); + _CASE(STATUS_NO_MEMORY); + _CASE(STATUS_NO_MORE_EAS); + _CASE(STATUS_NO_MORE_ENTRIES); + _CASE(STATUS_NO_MORE_FILES); + _CASE(STATUS_NO_MORE_MATCHES); + _CASE(STATUS_NO_PAGEFILE); + _CASE(STATUS_NO_QUOTAS_FOR_ACCOUNT); + _CASE(STATUS_NO_RECOVERY_POLICY); + _CASE(STATUS_NO_SECURITY_ON_OBJECT); + _CASE(STATUS_NO_SPOOL_SPACE); + _CASE(STATUS_NO_SUCH_ALIAS); + _CASE(STATUS_NO_SUCH_DEVICE); + _CASE(STATUS_NO_SUCH_DOMAIN); + _CASE(STATUS_NO_SUCH_FILE); + _CASE(STATUS_NO_SUCH_GROUP); + _CASE(STATUS_NO_SUCH_LOGON_SESSION); + _CASE(STATUS_NO_SUCH_MEMBER); + _CASE(STATUS_NO_SUCH_PACKAGE); + _CASE(STATUS_NO_SUCH_PRIVILEGE); + _CASE(STATUS_NO_SUCH_USER); + _CASE(STATUS_NO_TOKEN); + _CASE(STATUS_NO_TRACKING_SERVICE); + _CASE(STATUS_NO_TRUST_LSA_SECRET); + _CASE(STATUS_NO_TRUST_SAM_ACCOUNT); + _CASE(STATUS_NO_USER_KEYS); + _CASE(STATUS_NO_USER_SESSION_KEY); + _CASE(STATUS_NO_YIELD_PERFORMED); + _CASE(STATUS_NT_CROSS_ENCRYPTION_REQUIRED); + _CASE(STATUS_NULL_LM_PASSWORD); + _CASE(STATUS_OBJECTID_EXISTS); + _CASE(STATUS_OBJECT_NAME_COLLISION); + _CASE(STATUS_OBJECT_NAME_EXISTS); + _CASE(STATUS_OBJECT_NAME_INVALID); + _CASE(STATUS_OBJECT_NAME_NOT_FOUND); + _CASE(STATUS_OBJECT_PATH_INVALID); + _CASE(STATUS_OBJECT_PATH_NOT_FOUND); + _CASE(STATUS_OBJECT_PATH_SYNTAX_BAD); + _CASE(STATUS_OBJECT_TYPE_MISMATCH); + _CASE(STATUS_ONLY_IF_CONNECTED); + _CASE(STATUS_OPEN_FAILED); + _CASE(STATUS_OPLOCK_BREAK_IN_PROGRESS); + _CASE(STATUS_OPLOCK_NOT_GRANTED); + _CASE(STATUS_ORDINAL_NOT_FOUND); + _CASE(STATUS_PAGEFILE_CREATE_FAILED); + _CASE(STATUS_PAGEFILE_QUOTA); + _CASE(STATUS_PAGEFILE_QUOTA_EXCEEDED); + _CASE(STATUS_PAGE_FAULT_COPY_ON_WRITE); + _CASE(STATUS_PAGE_FAULT_DEMAND_ZERO); + _CASE(STATUS_PAGE_FAULT_GUARD_PAGE); + _CASE(STATUS_PAGE_FAULT_PAGING_FILE); + _CASE(STATUS_PAGE_FAULT_TRANSITION); + _CASE(STATUS_PARITY_ERROR); + _CASE(STATUS_PARTIAL_COPY); + _CASE(STATUS_PARTITION_FAILURE); + _CASE(STATUS_PASSWORD_EXPIRED); + _CASE(STATUS_PASSWORD_MUST_CHANGE); + _CASE(STATUS_PASSWORD_RESTRICTION); + _CASE(STATUS_PATH_NOT_COVERED); + _CASE(STATUS_PENDING); + _CASE(STATUS_PIPE_BROKEN); + _CASE(STATUS_PIPE_BUSY); + _CASE(STATUS_PIPE_CLOSING); + _CASE(STATUS_PIPE_CONNECTED); + _CASE(STATUS_PIPE_DISCONNECTED); + _CASE(STATUS_PIPE_EMPTY); + _CASE(STATUS_PIPE_LISTENING); + _CASE(STATUS_PIPE_NOT_AVAILABLE); + _CASE(STATUS_PLUGPLAY_NO_DEVICE); + _CASE(STATUS_PNP_BAD_MPS_TABLE); + _CASE(STATUS_PNP_IRQ_TRANSLATION_FAILED); + _CASE(STATUS_PNP_REBOOT_REQUIRED); + _CASE(STATUS_PNP_RESTART_ENUMERATION); + _CASE(STATUS_PNP_TRANSLATION_FAILED); + _CASE(STATUS_POLICY_OBJECT_NOT_FOUND); + _CASE(STATUS_POLICY_ONLY_IN_DS); + _CASE(STATUS_PORT_ALREADY_SET); + _CASE(STATUS_PORT_CONNECTION_REFUSED); + _CASE(STATUS_PORT_DISCONNECTED); + _CASE(STATUS_PORT_MESSAGE_TOO_LONG); + _CASE(STATUS_PORT_UNREACHABLE); + _CASE(STATUS_POSSIBLE_DEADLOCK); + _CASE(STATUS_POWER_STATE_INVALID); + _CASE(STATUS_PREDEFINED_HANDLE); + _CASE(STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED); + _CASE(STATUS_PRINT_CANCELLED); + _CASE(STATUS_PRINT_QUEUE_FULL); + _CASE(STATUS_PRIVILEGED_INSTRUCTION); + _CASE(STATUS_PRIVILEGE_NOT_HELD); + _CASE(STATUS_PROCEDURE_NOT_FOUND); + _CASE(STATUS_PROCESS_IS_TERMINATING); + _CASE(STATUS_PROFILING_AT_LIMIT); + _CASE(STATUS_PROFILING_NOT_STARTED); + _CASE(STATUS_PROFILING_NOT_STOPPED); + _CASE(STATUS_PROPSET_NOT_FOUND); + _CASE(STATUS_PROTOCOL_UNREACHABLE); + _CASE(STATUS_PWD_HISTORY_CONFLICT); + _CASE(STATUS_PWD_TOO_RECENT); + _CASE(STATUS_PWD_TOO_SHORT); + _CASE(STATUS_QUOTA_EXCEEDED); + _CASE(STATUS_QUOTA_LIST_INCONSISTENT); + _CASE(STATUS_RANGE_LIST_CONFLICT); + _CASE(STATUS_RANGE_NOT_FOUND); + _CASE(STATUS_RANGE_NOT_LOCKED); + _CASE(STATUS_RDP_PROTOCOL_ERROR); + _CASE(STATUS_RECEIVE_EXPEDITED); + _CASE(STATUS_RECEIVE_PARTIAL); + _CASE(STATUS_RECEIVE_PARTIAL_EXPEDITED); + _CASE(STATUS_RECOVERY_FAILURE); + _CASE(STATUS_REDIRECTOR_HAS_OPEN_HANDLES); + _CASE(STATUS_REDIRECTOR_NOT_STARTED); + _CASE(STATUS_REDIRECTOR_PAUSED); + _CASE(STATUS_REDIRECTOR_STARTED); + _CASE(STATUS_REGISTRY_CORRUPT); + _CASE(STATUS_REGISTRY_IO_FAILED); + _CASE(STATUS_REGISTRY_QUOTA_LIMIT); + _CASE(STATUS_REGISTRY_RECOVERED); + _CASE(STATUS_REG_NAT_CONSUMPTION); + _CASE(STATUS_REINITIALIZATION_NEEDED); + _CASE(STATUS_REMOTE_DISCONNECT); + _CASE(STATUS_REMOTE_NOT_LISTENING); + _CASE(STATUS_REMOTE_RESOURCES); + _CASE(STATUS_REMOTE_SESSION_LIMIT); + _CASE(STATUS_REMOTE_STORAGE_MEDIA_ERROR); + _CASE(STATUS_REMOTE_STORAGE_NOT_ACTIVE); + _CASE(STATUS_REPARSE); + _CASE(STATUS_REPARSE_ATTRIBUTE_CONFLICT); + _CASE(STATUS_REPARSE_OBJECT); + _CASE(STATUS_REPARSE_POINT_NOT_RESOLVED); + _CASE(STATUS_REPLY_MESSAGE_MISMATCH); + _CASE(STATUS_REQUEST_ABORTED); + _CASE(STATUS_REQUEST_NOT_ACCEPTED); + _CASE(STATUS_RESOURCE_DATA_NOT_FOUND); + _CASE(STATUS_RESOURCE_LANG_NOT_FOUND); + _CASE(STATUS_RESOURCE_NAME_NOT_FOUND); + _CASE(STATUS_RESOURCE_NOT_OWNED); + _CASE(STATUS_RESOURCE_REQUIREMENTS_CHANGED); + _CASE(STATUS_RESOURCE_TYPE_NOT_FOUND); + _CASE(STATUS_RETRY); + _CASE(STATUS_REVISION_MISMATCH); + _CASE(STATUS_RXACT_COMMITTED); + _CASE(STATUS_RXACT_COMMIT_FAILURE); + _CASE(STATUS_RXACT_COMMIT_NECESSARY); + _CASE(STATUS_RXACT_INVALID_STATE); + _CASE(STATUS_RXACT_STATE_CREATED); + _CASE(STATUS_SAM_INIT_FAILURE); + _CASE(STATUS_SAM_NEED_BOOTKEY_FLOPPY); + _CASE(STATUS_SAM_NEED_BOOTKEY_PASSWORD); + _CASE(STATUS_SECRET_TOO_LONG); + _CASE(STATUS_SECTION_NOT_EXTENDED); + _CASE(STATUS_SECTION_NOT_IMAGE); + _CASE(STATUS_SECTION_PROTECTION); + _CASE(STATUS_SECTION_TOO_BIG); + _CASE(STATUS_SEGMENT_NOTIFICATION); + _CASE(STATUS_SEMAPHORE_LIMIT_EXCEEDED); + _CASE(STATUS_SERIAL_COUNTER_TIMEOUT); + _CASE(STATUS_SERIAL_MORE_WRITES); + _CASE(STATUS_SERIAL_NO_DEVICE_INITED); + _CASE(STATUS_SERVER_DISABLED); + _CASE(STATUS_SERVER_HAS_OPEN_HANDLES); + _CASE(STATUS_SERVER_NOT_DISABLED); + _CASE(STATUS_SERVER_SID_MISMATCH); + _CASE(STATUS_SERVICE_NOTIFICATION); + _CASE(STATUS_SETMARK_DETECTED); + _CASE(STATUS_SHARED_IRQ_BUSY); + _CASE(STATUS_SHARED_POLICY); + _CASE(STATUS_SHARING_PAUSED); + _CASE(STATUS_SHARING_VIOLATION); + _CASE(STATUS_SINGLE_STEP); + _CASE(STATUS_SOME_NOT_MAPPED); + _CASE(STATUS_SOURCE_ELEMENT_EMPTY); + _CASE(STATUS_SPECIAL_ACCOUNT); + _CASE(STATUS_SPECIAL_GROUP); + _CASE(STATUS_SPECIAL_USER); + _CASE(STATUS_STACK_OVERFLOW); + _CASE(STATUS_STACK_OVERFLOW_READ); + _CASE(STATUS_SUCCESS); + _CASE(STATUS_SUSPEND_COUNT_EXCEEDED); + _CASE(STATUS_SYNCHRONIZATION_REQUIRED); + _CASE(STATUS_SYSTEM_IMAGE_BAD_SIGNATURE); + _CASE(STATUS_SYSTEM_PROCESS_TERMINATED); + _CASE(STATUS_THREAD_IS_TERMINATING); + _CASE(STATUS_THREAD_NOT_IN_PROCESS); + _CASE(STATUS_THREAD_WAS_SUSPENDED); + _CASE(STATUS_TIMEOUT); + _CASE(STATUS_TIMER_NOT_CANCELED); + _CASE(STATUS_TIMER_RESOLUTION_NOT_SET); + _CASE(STATUS_TIMER_RESUME_IGNORED); + _CASE(STATUS_TIME_DIFFERENCE_AT_DC); + _CASE(STATUS_TOKEN_ALREADY_IN_USE); + _CASE(STATUS_TOO_LATE); + _CASE(STATUS_TOO_MANY_ADDRESSES); + _CASE(STATUS_TOO_MANY_COMMANDS); + _CASE(STATUS_TOO_MANY_CONTEXT_IDS); + _CASE(STATUS_TOO_MANY_GUIDS_REQUESTED); + _CASE(STATUS_TOO_MANY_LINKS); + _CASE(STATUS_TOO_MANY_LUIDS_REQUESTED); + _CASE(STATUS_TOO_MANY_NAMES); + _CASE(STATUS_TOO_MANY_NODES); + _CASE(STATUS_TOO_MANY_OPENED_FILES); + _CASE(STATUS_TOO_MANY_PAGING_FILES); + _CASE(STATUS_TOO_MANY_SECRETS); + _CASE(STATUS_TOO_MANY_SESSIONS); + _CASE(STATUS_TOO_MANY_SIDS); + _CASE(STATUS_TOO_MANY_THREADS); + _CASE(STATUS_TRANSACTION_ABORTED); + _CASE(STATUS_TRANSACTION_INVALID_ID); + _CASE(STATUS_TRANSACTION_INVALID_TYPE); + _CASE(STATUS_TRANSACTION_NO_MATCH); + _CASE(STATUS_TRANSACTION_NO_RELEASE); + _CASE(STATUS_TRANSACTION_RESPONDED); + _CASE(STATUS_TRANSACTION_TIMED_OUT); + _CASE(STATUS_TRANSLATION_COMPLETE); + _CASE(STATUS_TRANSPORT_FULL); + _CASE(STATUS_TRUSTED_DOMAIN_FAILURE); + _CASE(STATUS_TRUSTED_RELATIONSHIP_FAILURE); + _CASE(STATUS_TRUST_FAILURE); + _CASE(STATUS_UNABLE_TO_DECOMMIT_VM); + _CASE(STATUS_UNABLE_TO_DELETE_SECTION); + _CASE(STATUS_UNABLE_TO_FREE_VM); + _CASE(STATUS_UNABLE_TO_LOCK_MEDIA); + _CASE(STATUS_UNABLE_TO_UNLOAD_MEDIA); + _CASE(STATUS_UNDEFINED_CHARACTER); + _CASE(STATUS_UNEXPECTED_IO_ERROR); + _CASE(STATUS_UNEXPECTED_MM_CREATE_ERR); + _CASE(STATUS_UNEXPECTED_MM_EXTEND_ERR); + _CASE(STATUS_UNEXPECTED_MM_MAP_ERROR); + _CASE(STATUS_UNEXPECTED_NETWORK_ERROR); + _CASE(STATUS_UNHANDLED_EXCEPTION); + _CASE(STATUS_UNKNOWN_REVISION); + _CASE(STATUS_UNMAPPABLE_CHARACTER); + _CASE(STATUS_UNRECOGNIZED_MEDIA); + _CASE(STATUS_UNRECOGNIZED_VOLUME); + _CASE(STATUS_UNSUCCESSFUL); + _CASE(STATUS_UNSUPPORTED_COMPRESSION); + _CASE(STATUS_UNWIND); + _CASE(STATUS_USER_APC); + _CASE(STATUS_USER_EXISTS); + _CASE(STATUS_USER_MAPPED_FILE); + _CASE(STATUS_USER_SESSION_DELETED); + _CASE(STATUS_VALIDATE_CONTINUE); + _CASE(STATUS_VARIABLE_NOT_FOUND); + _CASE(STATUS_VDM_HARD_ERROR); + _CASE(STATUS_VERIFY_REQUIRED); + _CASE(STATUS_VIRTUAL_CIRCUIT_CLOSED); + _CASE(STATUS_VOLUME_DISMOUNTED); + _CASE(STATUS_VOLUME_MOUNTED); + _CASE(STATUS_VOLUME_NOT_UPGRADED); + _CASE(STATUS_WAIT_1); + _CASE(STATUS_WAIT_2); + _CASE(STATUS_WAIT_3); + _CASE(STATUS_WAIT_63); + _CASE(STATUS_WAKE_SYSTEM); + _CASE(STATUS_WAKE_SYSTEM_DEBUGGER); + _CASE(STATUS_WAS_LOCKED); + _CASE(STATUS_WAS_UNLOCKED); + _CASE(STATUS_WMI_GUID_NOT_FOUND); + _CASE(STATUS_WMI_INSTANCE_NOT_FOUND); + _CASE(STATUS_WMI_ITEMID_NOT_FOUND); + _CASE(STATUS_WMI_NOT_SUPPORTED); + _CASE(STATUS_WMI_READ_ONLY); + _CASE(STATUS_WMI_SET_FAILURE); + _CASE(STATUS_WMI_TRY_AGAIN); + _CASE(STATUS_WORKING_SET_LIMIT_RANGE); + _CASE(STATUS_WORKING_SET_QUOTA); + _CASE(STATUS_WOW_ASSERTION); + _CASE(STATUS_WRONG_EFS); + _CASE(STATUS_WRONG_PASSWORD); + _CASE(STATUS_WRONG_PASSWORD_CORE); + _CASE(STATUS_WRONG_VOLUME); + _CASE(STATUS_WX86_BREAKPOINT); + _CASE(STATUS_WX86_CONTINUE); + _CASE(STATUS_WX86_CREATEWX86TIB); + _CASE(STATUS_WX86_EXCEPTION_CHAIN); + _CASE(STATUS_WX86_EXCEPTION_CONTINUE); + _CASE(STATUS_WX86_EXCEPTION_LASTCHANCE); + _CASE(STATUS_WX86_FLOAT_STACK_CHECK); + _CASE(STATUS_WX86_INTERNAL_ERROR); + _CASE(STATUS_WX86_SINGLE_STEP); + _CASE(STATUS_WX86_UNSIMULATE); + default: return "STATUS_UNKNOWN"; + } + +#undef _CASE +} diff --git a/src/core/kernel/support/EmuFile.h b/src/core/kernel/support/EmuFile.h index 147c9bd9a..fae6463dd 100644 --- a/src/core/kernel/support/EmuFile.h +++ b/src/core/kernel/support/EmuFile.h @@ -1,307 +1,307 @@ -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** -#ifndef EMUFILE_H -#define EMUFILE_H - - -#include -#include -#include -#include -#include - -// ****************************************************************** -// * prevent name collisions -// ****************************************************************** -namespace NtDll -{ - #include "EmuNtDll.h" -}; - -#include "Emu.h" - -// TODO : Move to a better suited file -//std::ostream& operator<<(std::ostream& os, const NtDll::NTSTATUS& value); - -extern const std::string MediaBoardRomFile; -extern const std::string DrivePrefix; -extern const std::string DriveSerial; -extern const std::string DriveCdRom0; -extern const std::string DriveMbfs; -extern const std::string DriveMbcom; -extern const std::string DriveMbrom; -extern const std::string DriveA; -extern const std::string DriveC; -extern const std::string DriveD; -extern const std::string DriveE; -extern const std::string DriveF; -extern const std::string DriveS; -extern const std::string DriveT; -extern const std::string DriveU; -extern const std::string DriveV; -extern const std::string DriveW; -extern const std::string DriveX; -extern const std::string DriveY; -extern const std::string DriveZ; -extern const std::string DevicePrefix; -extern const std::string DeviceCdrom0; -extern const std::string DeviceHarddisk0; -extern const std::string DeviceHarddisk0PartitionPrefix; -extern const std::string DeviceHarddisk0Partition0; -extern const std::string DeviceHarddisk0Partition1; -extern const std::string DeviceHarddisk0Partition2; -extern const std::string DeviceHarddisk0Partition3; -extern const std::string DeviceHarddisk0Partition4; -extern const std::string DeviceHarddisk0Partition5; -extern const std::string DeviceHarddisk0Partition6; -extern const std::string DeviceHarddisk0Partition7; -extern const std::string DeviceHarddisk0Partition8; -extern const std::string DeviceHarddisk0Partition9; -extern const std::string DeviceHarddisk0Partition10; -extern const std::string DeviceHarddisk0Partition11; -extern const std::string DeviceHarddisk0Partition12; -extern const std::string DeviceHarddisk0Partition13; -extern const std::string DeviceHarddisk0Partition14; -extern const std::string DeviceHarddisk0Partition15; -extern const std::string DeviceHarddisk0Partition16; -extern const std::string DeviceHarddisk0Partition17; -extern const std::string DeviceHarddisk0Partition18; -extern const std::string DeviceHarddisk0Partition19; -extern const std::string DeviceHarddisk0Partition20; -extern const char CxbxDefaultXbeDriveLetter; -extern int CxbxDefaultXbeDriveIndex; - -extern std::string CxbxBasePath; -extern HANDLE CxbxBasePathHandle; - -const size_t XboxFileInfoStructSizes[xboxkrnl::FileMaximumInformation] = { - 0, // (index 0) - sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileDirectoryInformation - sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileFullDirectoryInformation - sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileBothDirectoryInformation - sizeof(xboxkrnl::FILE_BASIC_INFORMATION), // FileBasicInformation - sizeof(xboxkrnl::FILE_STANDARD_INFORMATION), // FileStandardInformation - sizeof(xboxkrnl::FILE_INTERNAL_INFORMATION), // FileInternalInformation - sizeof(xboxkrnl::FILE_EA_INFORMATION), // FileEaInformation - sizeof(xboxkrnl::FILE_ACCESS_INFORMATION), // FileAccessInformation - sizeof(xboxkrnl::FILE_NAME_INFORMATION), // FileNameInformation - sizeof(xboxkrnl::FILE_RENAME_INFORMATION), // FileRenameInformation - sizeof(xboxkrnl::FILE_LINK_INFORMATION), // FileLinkInformation - sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileNamesInformation - sizeof(xboxkrnl::FILE_DISPOSITION_INFORMATION), // FileDispositionInformation - sizeof(xboxkrnl::FILE_POSITION_INFORMATION), // FilePositionInformation - sizeof(xboxkrnl::FILE_FULL_EA_INFORMATION), // FileFullEaInformation - sizeof(xboxkrnl::FILE_MODE_INFORMATION), // FileModeInformation - sizeof(xboxkrnl::FILE_ALIGNMENT_INFORMATION), // FileAlignmentInformation - sizeof(xboxkrnl::FILE_ALL_INFORMATION), // FileAllInformation - sizeof(xboxkrnl::FILE_ALLOCATION_INFORMATION), // FileAllocationInformation - sizeof(xboxkrnl::FILE_END_OF_FILE_INFORMATION), // FileEndOfFileInformation - sizeof(xboxkrnl::FILE_NAME_INFORMATION), // FileAlternateNameInformation - sizeof(xboxkrnl::FILE_STREAM_INFORMATION), // FileStreamInformation - sizeof(xboxkrnl::FILE_PIPE_INFORMATION), // FilePipeInformation - sizeof(xboxkrnl::FILE_PIPE_LOCAL_INFORMATION), // FilePipeLocalInformation - sizeof(xboxkrnl::FILE_PIPE_REMOTE_INFORMATION), // FilePipeRemoteInformation - sizeof(xboxkrnl::FILE_MAILSLOT_QUERY_INFORMATION), // FileMailslotQueryInformation - sizeof(xboxkrnl::FILE_MAILSLOT_SET_INFORMATION), // FileMailslotSetInformation - sizeof(xboxkrnl::FILE_COMPRESSION_INFORMATION), // FileCompressionInformation - 0, // FileCopyOnWriteInformation - sizeof(xboxkrnl::FILE_COMPLETION_INFORMATION), // FileCompletionInformation - sizeof(xboxkrnl::FILE_MOVE_CLUSTER_INFORMATION), // FileMoveClusterInformation - 0, // FileQuotaInformation - sizeof(xboxkrnl::FILE_REPARSE_POINT_INFORMATION), // FileReparsePointInformation - sizeof(xboxkrnl::FILE_NETWORK_OPEN_INFORMATION), // FileNetworkOpenInformation - sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileObjectIdInformation - sizeof(xboxkrnl::FILE_TRACKING_INFORMATION), // FileTrackingInformation - 0, // FileOleDirectoryInformation - 0, // FileContentIndexInformation - 0, // FileInheritContentIndexInformation - 0 // FileOleInformation -}; - - -class EmuNtObject; - -struct NativeObjectAttributes { - wchar_t wszObjectName[160]; - NtDll::UNICODE_STRING NtUnicodeString; - NtDll::OBJECT_ATTRIBUTES NtObjAttr; - // This is what should be passed on to Windows - // after CxbxObjectAttributesToNT() has been called : - NtDll::POBJECT_ATTRIBUTES NtObjAttrPtr; -}; - -NTSTATUS CxbxObjectAttributesToNT(xboxkrnl::POBJECT_ATTRIBUTES ObjectAttributes, NativeObjectAttributes& nativeObjectAttributes, std::string aFileAPIName = "", bool partitionHeader = false); -NTSTATUS CxbxConvertFilePath(std::string RelativeXboxPath, OUT std::wstring &RelativeHostPath, IN OUT NtDll::HANDLE *RootDirectory, std::string aFileAPIName = "", bool partitionHeader = false); - -// ****************************************************************** -// * Wrapper of a handle object -// ****************************************************************** -class EmuHandle -{ -public: - EmuHandle(EmuNtObject* ntObject); - NTSTATUS NtClose(); - NTSTATUS NtDuplicateObject(PHANDLE TargetHandle, DWORD Options); - EmuNtObject* NtObject; -}; - -// ****************************************************************** -// * An NT fake object -// ****************************************************************** -class EmuNtObject -{ -public: - EmuNtObject(); - HANDLE NewHandle(); - NTSTATUS NtClose(); - EmuNtObject* NtDuplicateObject(DWORD Options); -protected: - virtual ~EmuNtObject() {}; -private: - ULONG RefCount; - -}; - -// ****************************************************************** -// * Emulated symbolic link handle -// ****************************************************************** -class EmuNtSymbolicLinkObject : public EmuNtObject { -public: - char DriveLetter; - std::string SymbolicLinkName; - bool IsHostBasedPath; - std::string XboxSymbolicLinkPath; - std::string HostSymbolicLinkPath; - HANDLE RootDirectoryHandle; - NTSTATUS Init(std::string aSymbolicLinkName, std::string aFullPath); - ~EmuNtSymbolicLinkObject(); -}; - -struct XboxDevice { - std::string XboxDevicePath; - std::string HostDevicePath; - HANDLE HostRootHandle; -}; - -// ****************************************************************** -// * is Handle a 'special' emulated handle? -// ****************************************************************** -bool IsEmuHandle(HANDLE Handle); -EmuHandle* HandleToEmuHandle(HANDLE Handle); -HANDLE EmuHandleToHandle(EmuHandle* emuHandle); - -CHAR* NtStatusToString(IN NTSTATUS Status); - -int CxbxRegisterDeviceHostPath(std::string XboxFullPath, std::string HostDevicePath, bool IsFile = false); -int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath); -XboxDevice *CxbxDeviceByDevicePath(const std::string XboxDevicePath); - -char SymbolicLinkToDriveLetter(std::string aSymbolicLinkName); -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveLetter); -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName); -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDevice(std::string DeviceName); -EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle); -void CleanupSymbolicLinks(); - -HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath); -NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath); - -std::wstring string_to_wstring(std::string const & src); -std::wstring PUNICODE_STRING_to_wstring(NtDll::PUNICODE_STRING const & src); -std::string PSTRING_to_string(xboxkrnl::PSTRING const & src); -void copy_string_to_PSTRING_to(std::string const & src, const xboxkrnl::PSTRING & dest); - -static int NtFileDirectoryInformationSize = sizeof(NtDll::FILE_DIRECTORY_INFORMATION) - 1; -static int NtPathBufferSize = MAX_PATH * sizeof(wchar_t); - -// Deletes structs created by the converters -void _CxbxPVOIDDeleter(PVOID *ptr); - -// Creates a PVOID variable named var which takes the given value -// and is automatically deleted when it goes out of scope -#define SMART_PVOID(var, value, orig) \ - PVOID var = value; \ - std::shared_ptr __var_shared_ptr; \ - if (nullptr == var) \ - { \ - __var_shared_ptr = nullptr; \ - var = orig; \ - } \ - else \ - __var_shared_ptr = std::shared_ptr(&var, _CxbxPVOIDDeleter); - -// Converts an Xbox FileInformation struct to the NT equivalent. -// Used by NtSetInformationFile. -#define XboxToNTFileInformation(var, i, c, l) SMART_PVOID(var, _XboxToNTFileInformation(i, c, l), i) -PVOID _XboxToNTFileInformation -( - IN PVOID xboxFileInformation, - IN ULONG FileInformationClass, - OUT ULONG *Length -); - -// Converts an NT FileInformation struct to the Xbox equivalent. -// Used by NtQueryInformationFile and NtQueryDirectoryFile -NTSTATUS NTToXboxFileInformation -( - IN PVOID nativeFileInformation, - OUT PVOID xboxFileInformation, - IN ULONG FileInformationClass, - IN ULONG Length -); - -// Xbox Partition Information -typedef struct -{ - UCHAR Name[16]; - ULONG Flags; - ULONG LBAStart; - ULONG LBASize; - ULONG Reserved; -} XboxPartitionTableEntry; - -typedef struct -{ - UCHAR Magic[16]; - char Res0[32]; - XboxPartitionTableEntry TableEntries[14]; -} XboxPartitionTable; - -typedef struct _FATX_SUPERBLOCK -{ - char Tag[4]; - unsigned int VolumeID; - unsigned int ClusterSize; - USHORT FatCopies; - int Resvd; - char Unused[4078]; -} FATX_SUPERBLOCK; - -XboxPartitionTable CxbxGetPartitionTable(); -FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber); -int CxbxGetPartitionNumberFromHandle(HANDLE hFile); -std::string CxbxGetPartitionDataPathFromHandle(HANDLE hFile); -void CxbxFormatPartitionByHandle(HANDLE hFile); - -#endif +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** +#ifndef EMUFILE_H +#define EMUFILE_H + + +#include +#include +#include +#include +#include + +// ****************************************************************** +// * prevent name collisions +// ****************************************************************** +namespace NtDll +{ + #include "EmuNtDll.h" +}; + +#include "Emu.h" + +// TODO : Move to a better suited file +//std::ostream& operator<<(std::ostream& os, const NtDll::NTSTATUS& value); + +extern const std::string MediaBoardRomFile; +extern const std::string DrivePrefix; +extern const std::string DriveSerial; +extern const std::string DriveCdRom0; +extern const std::string DriveMbfs; +extern const std::string DriveMbcom; +extern const std::string DriveMbrom; +extern const std::string DriveA; +extern const std::string DriveC; +extern const std::string DriveD; +extern const std::string DriveE; +extern const std::string DriveF; +extern const std::string DriveS; +extern const std::string DriveT; +extern const std::string DriveU; +extern const std::string DriveV; +extern const std::string DriveW; +extern const std::string DriveX; +extern const std::string DriveY; +extern const std::string DriveZ; +extern const std::string DevicePrefix; +extern const std::string DeviceCdrom0; +extern const std::string DeviceHarddisk0; +extern const std::string DeviceHarddisk0PartitionPrefix; +extern const std::string DeviceHarddisk0Partition0; +extern const std::string DeviceHarddisk0Partition1; +extern const std::string DeviceHarddisk0Partition2; +extern const std::string DeviceHarddisk0Partition3; +extern const std::string DeviceHarddisk0Partition4; +extern const std::string DeviceHarddisk0Partition5; +extern const std::string DeviceHarddisk0Partition6; +extern const std::string DeviceHarddisk0Partition7; +extern const std::string DeviceHarddisk0Partition8; +extern const std::string DeviceHarddisk0Partition9; +extern const std::string DeviceHarddisk0Partition10; +extern const std::string DeviceHarddisk0Partition11; +extern const std::string DeviceHarddisk0Partition12; +extern const std::string DeviceHarddisk0Partition13; +extern const std::string DeviceHarddisk0Partition14; +extern const std::string DeviceHarddisk0Partition15; +extern const std::string DeviceHarddisk0Partition16; +extern const std::string DeviceHarddisk0Partition17; +extern const std::string DeviceHarddisk0Partition18; +extern const std::string DeviceHarddisk0Partition19; +extern const std::string DeviceHarddisk0Partition20; +extern const char CxbxDefaultXbeDriveLetter; +extern int CxbxDefaultXbeDriveIndex; + +extern std::string CxbxBasePath; +extern HANDLE CxbxBasePathHandle; + +const size_t XboxFileInfoStructSizes[xboxkrnl::FileMaximumInformation] = { + 0, // (index 0) + sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileDirectoryInformation + sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileFullDirectoryInformation + sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileBothDirectoryInformation + sizeof(xboxkrnl::FILE_BASIC_INFORMATION), // FileBasicInformation + sizeof(xboxkrnl::FILE_STANDARD_INFORMATION), // FileStandardInformation + sizeof(xboxkrnl::FILE_INTERNAL_INFORMATION), // FileInternalInformation + sizeof(xboxkrnl::FILE_EA_INFORMATION), // FileEaInformation + sizeof(xboxkrnl::FILE_ACCESS_INFORMATION), // FileAccessInformation + sizeof(xboxkrnl::FILE_NAME_INFORMATION), // FileNameInformation + sizeof(xboxkrnl::FILE_RENAME_INFORMATION), // FileRenameInformation + sizeof(xboxkrnl::FILE_LINK_INFORMATION), // FileLinkInformation + sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileNamesInformation + sizeof(xboxkrnl::FILE_DISPOSITION_INFORMATION), // FileDispositionInformation + sizeof(xboxkrnl::FILE_POSITION_INFORMATION), // FilePositionInformation + sizeof(xboxkrnl::FILE_FULL_EA_INFORMATION), // FileFullEaInformation + sizeof(xboxkrnl::FILE_MODE_INFORMATION), // FileModeInformation + sizeof(xboxkrnl::FILE_ALIGNMENT_INFORMATION), // FileAlignmentInformation + sizeof(xboxkrnl::FILE_ALL_INFORMATION), // FileAllInformation + sizeof(xboxkrnl::FILE_ALLOCATION_INFORMATION), // FileAllocationInformation + sizeof(xboxkrnl::FILE_END_OF_FILE_INFORMATION), // FileEndOfFileInformation + sizeof(xboxkrnl::FILE_NAME_INFORMATION), // FileAlternateNameInformation + sizeof(xboxkrnl::FILE_STREAM_INFORMATION), // FileStreamInformation + sizeof(xboxkrnl::FILE_PIPE_INFORMATION), // FilePipeInformation + sizeof(xboxkrnl::FILE_PIPE_LOCAL_INFORMATION), // FilePipeLocalInformation + sizeof(xboxkrnl::FILE_PIPE_REMOTE_INFORMATION), // FilePipeRemoteInformation + sizeof(xboxkrnl::FILE_MAILSLOT_QUERY_INFORMATION), // FileMailslotQueryInformation + sizeof(xboxkrnl::FILE_MAILSLOT_SET_INFORMATION), // FileMailslotSetInformation + sizeof(xboxkrnl::FILE_COMPRESSION_INFORMATION), // FileCompressionInformation + 0, // FileCopyOnWriteInformation + sizeof(xboxkrnl::FILE_COMPLETION_INFORMATION), // FileCompletionInformation + sizeof(xboxkrnl::FILE_MOVE_CLUSTER_INFORMATION), // FileMoveClusterInformation + 0, // FileQuotaInformation + sizeof(xboxkrnl::FILE_REPARSE_POINT_INFORMATION), // FileReparsePointInformation + sizeof(xboxkrnl::FILE_NETWORK_OPEN_INFORMATION), // FileNetworkOpenInformation + sizeof(xboxkrnl::FILE_DIRECTORY_INFORMATION), // FileObjectIdInformation + sizeof(xboxkrnl::FILE_TRACKING_INFORMATION), // FileTrackingInformation + 0, // FileOleDirectoryInformation + 0, // FileContentIndexInformation + 0, // FileInheritContentIndexInformation + 0 // FileOleInformation +}; + + +class EmuNtObject; + +struct NativeObjectAttributes { + wchar_t wszObjectName[160]; + NtDll::UNICODE_STRING NtUnicodeString; + NtDll::OBJECT_ATTRIBUTES NtObjAttr; + // This is what should be passed on to Windows + // after CxbxObjectAttributesToNT() has been called : + NtDll::POBJECT_ATTRIBUTES NtObjAttrPtr; +}; + +NTSTATUS CxbxObjectAttributesToNT(xboxkrnl::POBJECT_ATTRIBUTES ObjectAttributes, NativeObjectAttributes& nativeObjectAttributes, std::string aFileAPIName = "", bool partitionHeader = false); +NTSTATUS CxbxConvertFilePath(std::string RelativeXboxPath, OUT std::wstring &RelativeHostPath, IN OUT NtDll::HANDLE *RootDirectory, std::string aFileAPIName = "", bool partitionHeader = false); + +// ****************************************************************** +// * Wrapper of a handle object +// ****************************************************************** +class EmuHandle +{ +public: + EmuHandle(EmuNtObject* ntObject); + NTSTATUS NtClose(); + NTSTATUS NtDuplicateObject(PHANDLE TargetHandle, DWORD Options); + EmuNtObject* NtObject; +}; + +// ****************************************************************** +// * An NT fake object +// ****************************************************************** +class EmuNtObject +{ +public: + EmuNtObject(); + HANDLE NewHandle(); + NTSTATUS NtClose(); + EmuNtObject* NtDuplicateObject(DWORD Options); +protected: + virtual ~EmuNtObject() {}; +private: + ULONG RefCount; + +}; + +// ****************************************************************** +// * Emulated symbolic link handle +// ****************************************************************** +class EmuNtSymbolicLinkObject : public EmuNtObject { +public: + char DriveLetter; + std::string SymbolicLinkName; + bool IsHostBasedPath; + std::string XboxSymbolicLinkPath; + std::string HostSymbolicLinkPath; + HANDLE RootDirectoryHandle; + NTSTATUS Init(std::string aSymbolicLinkName, std::string aFullPath); + ~EmuNtSymbolicLinkObject(); +}; + +struct XboxDevice { + std::string XboxDevicePath; + std::string HostDevicePath; + HANDLE HostRootHandle; +}; + +// ****************************************************************** +// * is Handle a 'special' emulated handle? +// ****************************************************************** +bool IsEmuHandle(HANDLE Handle); +EmuHandle* HandleToEmuHandle(HANDLE Handle); +HANDLE EmuHandleToHandle(EmuHandle* emuHandle); + +CHAR* NtStatusToString(IN NTSTATUS Status); + +int CxbxRegisterDeviceHostPath(std::string XboxFullPath, std::string HostDevicePath, bool IsFile = false); +int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath); +XboxDevice *CxbxDeviceByDevicePath(const std::string XboxDevicePath); + +char SymbolicLinkToDriveLetter(std::string aSymbolicLinkName); +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveLetter); +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByName(std::string SymbolicLinkName); +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDevice(std::string DeviceName); +EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle); +void CleanupSymbolicLinks(); + +HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath); +NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath); + +std::wstring string_to_wstring(std::string const & src); +std::wstring PUNICODE_STRING_to_wstring(NtDll::PUNICODE_STRING const & src); +std::string PSTRING_to_string(xboxkrnl::PSTRING const & src); +void copy_string_to_PSTRING_to(std::string const & src, const xboxkrnl::PSTRING & dest); + +static int NtFileDirectoryInformationSize = sizeof(NtDll::FILE_DIRECTORY_INFORMATION) - 1; +static int NtPathBufferSize = MAX_PATH * sizeof(wchar_t); + +// Deletes structs created by the converters +void _CxbxPVOIDDeleter(PVOID *ptr); + +// Creates a PVOID variable named var which takes the given value +// and is automatically deleted when it goes out of scope +#define SMART_PVOID(var, value, orig) \ + PVOID var = value; \ + std::shared_ptr __var_shared_ptr; \ + if (nullptr == var) \ + { \ + __var_shared_ptr = nullptr; \ + var = orig; \ + } \ + else \ + __var_shared_ptr = std::shared_ptr(&var, _CxbxPVOIDDeleter); + +// Converts an Xbox FileInformation struct to the NT equivalent. +// Used by NtSetInformationFile. +#define XboxToNTFileInformation(var, i, c, l) SMART_PVOID(var, _XboxToNTFileInformation(i, c, l), i) +PVOID _XboxToNTFileInformation +( + IN PVOID xboxFileInformation, + IN ULONG FileInformationClass, + OUT ULONG *Length +); + +// Converts an NT FileInformation struct to the Xbox equivalent. +// Used by NtQueryInformationFile and NtQueryDirectoryFile +NTSTATUS NTToXboxFileInformation +( + IN PVOID nativeFileInformation, + OUT PVOID xboxFileInformation, + IN ULONG FileInformationClass, + IN ULONG Length +); + +// Xbox Partition Information +typedef struct +{ + UCHAR Name[16]; + ULONG Flags; + ULONG LBAStart; + ULONG LBASize; + ULONG Reserved; +} XboxPartitionTableEntry; + +typedef struct +{ + UCHAR Magic[16]; + char Res0[32]; + XboxPartitionTableEntry TableEntries[14]; +} XboxPartitionTable; + +typedef struct _FATX_SUPERBLOCK +{ + char Tag[4]; + unsigned int VolumeID; + unsigned int ClusterSize; + USHORT FatCopies; + int Resvd; + char Unused[4078]; +} FATX_SUPERBLOCK; + +XboxPartitionTable CxbxGetPartitionTable(); +FATX_SUPERBLOCK CxbxGetFatXSuperBlock(int partitionNumber); +int CxbxGetPartitionNumberFromHandle(HANDLE hFile); +std::string CxbxGetPartitionDataPathFromHandle(HANDLE hFile); +void CxbxFormatPartitionByHandle(HANDLE hFile); + +#endif diff --git a/src/core/kernel/support/EmuNtDll.cpp b/src/core/kernel/support/EmuNtDll.cpp index 0da77ab2b..ff589a77a 100644 --- a/src/core/kernel/support/EmuNtDll.cpp +++ b/src/core/kernel/support/EmuNtDll.cpp @@ -85,7 +85,7 @@ IMPORT(NtQueueApcThread); IMPORT(NtReadFile); IMPORT(NtReleaseMutant); IMPORT(NtReleaseSemaphore); -IMPORT(NtResumeThread); +IMPORT(NtResumeThread); IMPORT(NtResetEvent); IMPORT(NtSetEvent); IMPORT(NtSetInformationFile); diff --git a/src/core/kernel/support/EmuNtDll.h b/src/core/kernel/support/EmuNtDll.h index 04093329e..252a3441f 100644 --- a/src/core/kernel/support/EmuNtDll.h +++ b/src/core/kernel/support/EmuNtDll.h @@ -1468,7 +1468,7 @@ typedef NTSTATUS(NTAPI *FPTR_NtPulseEvent) IN HANDLE EventHandle, OUT PLONG PreviousState OPTIONAL ); - + // ****************************************************************** // * NtResetEvent // ****************************************************************** @@ -1476,7 +1476,7 @@ typedef NTSTATUS(NTAPI *FPTR_NtResetEvent) ( IN HANDLE EventHandle, OUT PLONG PreviousState OPTIONAL -); +); // ****************************************************************** // * NtCreateMutant @@ -1949,7 +1949,7 @@ EXTERN(NtQueryVolumeInformationFile); EXTERN(NtQueueApcThread); EXTERN(NtReadFile); EXTERN(NtReleaseMutant); -EXTERN(NtReleaseSemaphore); +EXTERN(NtReleaseSemaphore); EXTERN(NtResetEvent); EXTERN(NtResumeThread); EXTERN(NtSetEvent); @@ -2013,6 +2013,6 @@ EXTERN(RtlUpcaseUnicodeChar); EXTERN(RtlUpcaseUnicodeString); EXTERN(RtlUpcaseUnicodeToMultiByteN); EXTERN(RtlUpperString); -EXTERN(RtlUshortByteSwap); +EXTERN(RtlUshortByteSwap); #endif diff --git a/src/devices/EmuNVNet.cpp b/src/devices/EmuNVNet.cpp index b638fcbfd..44fb3445b 100644 --- a/src/devices/EmuNVNet.cpp +++ b/src/devices/EmuNVNet.cpp @@ -318,7 +318,7 @@ uint32_t EmuNVNet_Read(xbaddr addr, int size) void EmuNVNet_DMAPacketFromGuest() { struct RingDesc desc; - bool is_last_packet; + bool is_last_packet; bool packet_sent = false; NvNetState_t* s = &NvNetState; @@ -334,7 +334,7 @@ void EmuNVNet_DMAPacketFromGuest() EmuLog(LOG_LEVEL::DEBUG, "Looking at ring desc %d (%llx): " "\n Buffer: 0x%x " "\n Length: 0x%x " - "\n Flags: 0x%x ", + "\n Flags: 0x%x ", s->tx_ring_index, tx_ring_addr, desc.packet_buffer, desc.length, desc.flags); s->tx_ring_index += 1; @@ -348,8 +348,8 @@ void EmuNVNet_DMAPacketFromGuest() memcpy(s->txrx_dma_buf, (void*)(desc.packet_buffer | CONTIGUOUS_MEMORY_BASE), desc.length + 1); g_NVNet->PCAPSend(s->txrx_dma_buf, desc.length + 1); - - packet_sent = true; + + packet_sent = true; /* Update descriptor */ is_last_packet = desc.flags & NV_TX_LASTPACKET; @@ -363,12 +363,12 @@ void EmuNVNet_DMAPacketFromGuest() break; } } - + if (packet_sent) { /* Trigger interrupt */ EmuLog(LOG_LEVEL::DEBUG, "Triggering interrupt"); EmuNVNet_SetRegister(NvRegIrqStatus, NVREG_IRQSTAT_BIT4, 4); - EmuNVNet_UpdateIRQ(); + EmuNVNet_UpdateIRQ(); } } @@ -389,7 +389,7 @@ bool EmuNVNet_DMAPacketToGuest(void* packet, size_t size) EmuLog(LOG_LEVEL::DEBUG, "Looking at ring descriptor %d (0x%llx): " "\n Buffer: 0x%x " "\n Length: 0x%x " - "\n Flags: 0x%x ", + "\n Flags: 0x%x ", s->rx_ring_index, rx_ring_addr, desc.packet_buffer, desc.length, desc.flags); s->rx_ring_index += 1; @@ -408,7 +408,7 @@ bool EmuNVNet_DMAPacketToGuest(void* packet, size_t size) memcpy((void*)(rx_ring_addr | CONTIGUOUS_MEMORY_BASE), &desc, sizeof(desc)); EmuLog(LOG_LEVEL::DEBUG, "Updated ring descriptor: " "\n Length: 0x%x " - "\n Flags: 0x%x ", + "\n Flags: 0x%x ", desc.flags, desc.length); /* Trigger interrupt */ @@ -593,18 +593,18 @@ void NVNetDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigne return; } - EmuNVNet_Write(addr, value, size); // For now, forward - - // Cache guest MAC address for packet filter - if (addr == NvRegMacAddrA) { - m_GuestMacAddress.bytes[0] = NvNetState.regs[NvRegMacAddrA + 0]; - m_GuestMacAddress.bytes[1] = NvNetState.regs[NvRegMacAddrA + 1]; - m_GuestMacAddress.bytes[2] = NvNetState.regs[NvRegMacAddrA + 2]; - m_GuestMacAddress.bytes[3] = NvNetState.regs[NvRegMacAddrA + 3]; - } - else if (addr == NvRegMacAddrB) { + EmuNVNet_Write(addr, value, size); // For now, forward + + // Cache guest MAC address for packet filter + if (addr == NvRegMacAddrA) { + m_GuestMacAddress.bytes[0] = NvNetState.regs[NvRegMacAddrA + 0]; + m_GuestMacAddress.bytes[1] = NvNetState.regs[NvRegMacAddrA + 1]; + m_GuestMacAddress.bytes[2] = NvNetState.regs[NvRegMacAddrA + 2]; + m_GuestMacAddress.bytes[3] = NvNetState.regs[NvRegMacAddrA + 3]; + } + else if (addr == NvRegMacAddrB) { m_GuestMacAddress.bytes[4] = NvNetState.regs[NvRegMacAddrB + 0]; - m_GuestMacAddress.bytes[5] = NvNetState.regs[NvRegMacAddrB + 1]; + m_GuestMacAddress.bytes[5] = NvNetState.regs[NvRegMacAddrB + 1]; } } @@ -671,7 +671,7 @@ void PrintPacket(void* buffer, size_t length) size_t payloadLength = length - sizeof(ethernet_header); // TODO: If we support the EtherType, decode it, otherwise, just dump the raw payload - //switch (ntohs(header->protocol)) + //switch (ntohs(header->protocol)) { // default: PrintRawPayload(payloadPtr, payloadLength); @@ -688,11 +688,11 @@ bool NVNetDevice::PCAPSend(void* packet, size_t length) // TODO: Optional // PrintPacket(packet, length); - + // Forward broadcast packets direct to the host PC, as well as over the network if (memcmp(header->dst.bytes, m_BroadcastMacAddress.bytes, 6) == 0) { static char pack[65536]; - memcpy(pack, packet, length); + memcpy(pack, packet, length); ethernet_header* _header = (ethernet_header*)pack; _header->dst = m_HostMacAddress; pcap_sendpacket((pcap_t*)m_AdapterHandle, (uint8_t*)pack, length); @@ -711,7 +711,7 @@ size_t NVNetDevice::PCAPReceive(void* packet, size_t max_length) const uint8_t *pkt_data; if (int res = pcap_next_ex((pcap_t*)m_AdapterHandle, &header, &pkt_data) > 0) { - // Only forward packets that are broadcast or specifically for Cxbx-R's MAC + // Only forward packets that are broadcast or specifically for Cxbx-R's MAC ethernet_header* e_header = (ethernet_header*)pkt_data; if (memcmp(e_header->dst.bytes, m_GuestMacAddress.bytes, 6) == 0 || memcmp(e_header->dst.bytes, m_BroadcastMacAddress.bytes, 6) == 0) { memcpy(packet, pkt_data, header->len); diff --git a/src/devices/EmuNVNet.h b/src/devices/EmuNVNet.h index 956ca30d4..a73311c98 100644 --- a/src/devices/EmuNVNet.h +++ b/src/devices/EmuNVNet.h @@ -23,7 +23,7 @@ // * // ****************************************************************** #pragma once - + #include #include "PCIDevice.h" // For PCIDevice @@ -226,6 +226,6 @@ private: void* m_AdapterHandle = nullptr; std::string m_HostAdapterName; mac_address m_HostMacAddress; - mac_address m_GuestMacAddress = { 0x00, 0x50, 0xF2, 0x00, 0x00, 0x34 }; + mac_address m_GuestMacAddress = { 0x00, 0x50, 0xF2, 0x00, 0x00, 0x34 }; mac_address m_BroadcastMacAddress = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; }; diff --git a/src/devices/MCPXDevice.cpp b/src/devices/MCPXDevice.cpp index 44d226fff..dccd8fc5e 100644 --- a/src/devices/MCPXDevice.cpp +++ b/src/devices/MCPXDevice.cpp @@ -28,7 +28,7 @@ #define LOG_PREFIX CXBXR_MODULE::MCPX -#include "MCPXDevice.h" +#include "MCPXDevice.h" #include "Logging.h" /* MCPXDevice */ diff --git a/src/devices/PCIBus.cpp b/src/devices/PCIBus.cpp index a8ace9630..7957de7f8 100644 --- a/src/devices/PCIBus.cpp +++ b/src/devices/PCIBus.cpp @@ -152,4 +152,4 @@ void PCIBus::Reset() for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) { it->second->Reset(); } -} +} diff --git a/src/devices/PCIBus.h b/src/devices/PCIBus.h index f49070393..557af66fb 100644 --- a/src/devices/PCIBus.h +++ b/src/devices/PCIBus.h @@ -75,4 +75,4 @@ private: PCIConfigAddressRegister m_configAddressRegister; }; -#endif +#endif diff --git a/src/devices/PCIDevice.h b/src/devices/PCIDevice.h index 0c642d863..6381ef73b 100644 --- a/src/devices/PCIDevice.h +++ b/src/devices/PCIDevice.h @@ -141,4 +141,4 @@ private: */ }; -#endif +#endif diff --git a/src/devices/SMBus.cpp b/src/devices/SMBus.cpp index 7826d9001..74c439e77 100644 --- a/src/devices/SMBus.cpp +++ b/src/devices/SMBus.cpp @@ -245,4 +245,4 @@ uint32_t SMBus::MMIORead(int barIndex, uint32_t addr, unsigned size) void SMBus::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) { -} +} diff --git a/src/devices/SMBus.h b/src/devices/SMBus.h index 506264883..94d403877 100644 --- a/src/devices/SMBus.h +++ b/src/devices/SMBus.h @@ -105,4 +105,4 @@ class SMBus : public PCIDevice { std::map m_Devices; }; -#endif +#endif diff --git a/src/devices/SMCDevice.cpp b/src/devices/SMCDevice.cpp index 08d66bd14..583f937f4 100644 --- a/src/devices/SMCDevice.cpp +++ b/src/devices/SMCDevice.cpp @@ -58,7 +58,7 @@ void SetLEDSequence(LED::Sequence aLEDSequence) SMCDevice::SMCDevice(SCMRevision revision, uint8_t av_pack) { - m_revision = revision; + m_revision = revision; buffer[SMC_COMMAND_AV_PACK] = av_pack; } diff --git a/src/devices/Xbox.cpp b/src/devices/Xbox.cpp index ae8d275b5..68b21371e 100644 --- a/src/devices/Xbox.cpp +++ b/src/devices/Xbox.cpp @@ -25,8 +25,8 @@ // * // ****************************************************************** -#include "Xbox.h" // For HardwareModel -#include "common\xbe\Xbe.h" // Without this HLEIntercept complains about some undefined xbe variables +#include "Xbox.h" // For HardwareModel +#include "common\xbe\Xbe.h" // Without this HLEIntercept complains about some undefined xbe variables #include "core\hle\Intercept.hpp" PCIBus* g_PCIBus; @@ -116,18 +116,18 @@ void InitXboxHardware(HardwareModel hardwareModel) g_SMBus = new SMBus(); // Create devices - g_MCPX = new MCPXDevice(mcpx_revision); + g_MCPX = new MCPXDevice(mcpx_revision); - g_SMC = new SMCDevice(smc_revision, g_bIsChihiro ? 6 : 1); // 6 = AV_PACK_STANDARD, 1 = AV_PACK_HDTV. Chihiro doesn't support HDTV! - // SMC uses different AV_PACK values than the Kernel + g_SMC = new SMCDevice(smc_revision, g_bIsChihiro ? 6 : 1); // 6 = AV_PACK_STANDARD, 1 = AV_PACK_HDTV. Chihiro doesn't support HDTV! + // SMC uses different AV_PACK values than the Kernel // See https://xboxdevwiki.net/PIC#The_AV_Pack g_EEPROM = new EEPROMDevice(); g_NVNet = new NVNetDevice(); g_NV2A = new NV2ADevice(); - g_ADM1032 = new ADM1032Device(); + g_ADM1032 = new ADM1032Device(); if (bLLE_USB) { g_USB0 = new USBDevice(); - } + } // Connect devices to SM bus g_SMBus->ConnectDevice(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, g_SMC); // W 0x20 R 0x21 @@ -155,15 +155,15 @@ void InitXboxHardware(HardwareModel hardwareModel) //g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(4, 1)), g_MCPX); // MCPX device ID = 0x0808 ? //g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(5, 0)), g_NVAPU); //g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(6, 0)), g_AC97); - g_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), g_NV2A); - if (bLLE_USB) { - // ergo720: according to some research done by LukeUsher, only Xbox Alpha Kits have a two HCs configuration. This seems to also be confirmed by the xboxdevwiki, - // which states that it has a xircom PGPCI2(OPTI 82C861) 2 USB port PCI card -> 2 ports, not 4. Finally, I disassembled various xbe's and discovered that the number - // of ports per HC is hardcoded as 4 in the driver instead of being detected at runtime by reading the HcRhDescriptorA register and so a game would have to be - // recompiled to support 2 HCs, which further confirms the point. Because we are not going to emulate an Alpha Kit, we can simply ignore the USB1 device. + g_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), g_NV2A); + if (bLLE_USB) { + // ergo720: according to some research done by LukeUsher, only Xbox Alpha Kits have a two HCs configuration. This seems to also be confirmed by the xboxdevwiki, + // which states that it has a xircom PGPCI2(OPTI 82C861) 2 USB port PCI card -> 2 ports, not 4. Finally, I disassembled various xbe's and discovered that the number + // of ports per HC is hardcoded as 4 in the driver instead of being detected at runtime by reading the HcRhDescriptorA register and so a game would have to be + // recompiled to support 2 HCs, which further confirms the point. Because we are not going to emulate an Alpha Kit, we can simply ignore the USB1 device. g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(2, 0)), g_USB0); - } + } // TODO : Handle other SMBUS Addresses, like PIC_ADDRESS, XCALIBUR_ADDRESS // Resources : http://pablot.com/misc/fancontroller.cpp diff --git a/src/devices/Xbox.h b/src/devices/Xbox.h index 7121fad5c..462d8e961 100644 --- a/src/devices/Xbox.h +++ b/src/devices/Xbox.h @@ -72,4 +72,4 @@ extern NVNetDevice* g_NVNet; extern NV2ADevice* g_NV2A; extern USBDevice* g_USB0; -extern void InitXboxHardware(HardwareModel hardwareModel); +extern void InitXboxHardware(HardwareModel hardwareModel); diff --git a/src/devices/usb/Hub.cpp b/src/devices/usb/Hub.cpp index 572a3e206..0b005c5b3 100644 --- a/src/devices/usb/Hub.cpp +++ b/src/devices/usb/Hub.cpp @@ -25,9 +25,9 @@ // * // ****************************************************************** -// Acknowledgment: QEMU hub device emulation as used in XQEMU (GPLv2) +// Acknowledgment: QEMU hub device emulation as used in XQEMU (GPLv2) // https://xqemu.com/ - + /* * QEMU USB HUB emulation * @@ -54,9 +54,9 @@ #define LOG_PREFIX CXBXR_MODULE::HUB - -#include // For PKINTERRUPT, etc. + +#include // For PKINTERRUPT, etc. #include // For memcpy #include "OHCI.h" #include "Hub.h" diff --git a/src/devices/usb/Hub.h b/src/devices/usb/Hub.h index c3f6bc69b..91005cdf2 100644 --- a/src/devices/usb/Hub.h +++ b/src/devices/usb/Hub.h @@ -1,84 +1,84 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2018 ergo720 -// * -// * All rights reserved -// * -// ****************************************************************** - +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018 ergo720 +// * +// * All rights reserved +// * +// ****************************************************************** + #ifndef HUB_H_ -#define HUB_H_ - -#include "UsbDevice.h" - - -struct USBHubState; // forward declare - - -/* Class which implements a usb hub */ -class Hub -{ - public: - // usb device this hub is attached to - USBDevice* m_UsbDev = nullptr; - - // initialize this hub - int Init(int port); - // start hub destruction - void HubDestroy(); - - - private: - // hub state - USBHubState* m_HubState = nullptr; - // hub class functions - USBDeviceClass* m_pPeripheralFuncStruct = nullptr; - - // initialize various member variables/functions - XboxDeviceState* ClassInitFn(); - // see USBDeviceClass for comments about these functions - int UsbHub_Initfn(XboxDeviceState* dev); - XboxDeviceState* UsbHub_FindDevice(XboxDeviceState* dev, uint8_t addr); - void UsbHub_HandleReset(); - void UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p, - int request, int value, int index, int length, uint8_t* data); - void UsbHub_HandleData(XboxDeviceState* dev, USBPacket* p); - void UsbHub_HandleDestroy(); - // see USBPortOps struct for info - void UsbHub_Attach(USBPort* port1); - void UsbHub_Detach(USBPort* port1); - void UsbHub_ChildDetach(XboxDeviceState* child); - void UsbHub_Wakeup(USBPort* port1); - void UsbHub_Complete(USBPort* port, USBPacket* packet); - // reserve a usb port for this hub - int UsbHubClaimPort(XboxDeviceState* dev, int port); - // free the usb port used by this hub - void UsbHubReleasePort(XboxDeviceState* dev); - // retieve the name of the feature of the usb request - std::string GetFeatureName(int feature); - // destroy hub resources - void HubCleanUp(); -}; - -extern Hub* g_HubObjArray[4]; - -#endif +#define HUB_H_ + +#include "UsbDevice.h" + + +struct USBHubState; // forward declare + + +/* Class which implements a usb hub */ +class Hub +{ + public: + // usb device this hub is attached to + USBDevice* m_UsbDev = nullptr; + + // initialize this hub + int Init(int port); + // start hub destruction + void HubDestroy(); + + + private: + // hub state + USBHubState* m_HubState = nullptr; + // hub class functions + USBDeviceClass* m_pPeripheralFuncStruct = nullptr; + + // initialize various member variables/functions + XboxDeviceState* ClassInitFn(); + // see USBDeviceClass for comments about these functions + int UsbHub_Initfn(XboxDeviceState* dev); + XboxDeviceState* UsbHub_FindDevice(XboxDeviceState* dev, uint8_t addr); + void UsbHub_HandleReset(); + void UsbHub_HandleControl(XboxDeviceState* dev, USBPacket* p, + int request, int value, int index, int length, uint8_t* data); + void UsbHub_HandleData(XboxDeviceState* dev, USBPacket* p); + void UsbHub_HandleDestroy(); + // see USBPortOps struct for info + void UsbHub_Attach(USBPort* port1); + void UsbHub_Detach(USBPort* port1); + void UsbHub_ChildDetach(XboxDeviceState* child); + void UsbHub_Wakeup(USBPort* port1); + void UsbHub_Complete(USBPort* port, USBPacket* packet); + // reserve a usb port for this hub + int UsbHubClaimPort(XboxDeviceState* dev, int port); + // free the usb port used by this hub + void UsbHubReleasePort(XboxDeviceState* dev); + // retieve the name of the feature of the usb request + std::string GetFeatureName(int feature); + // destroy hub resources + void HubCleanUp(); +}; + +extern Hub* g_HubObjArray[4]; + +#endif diff --git a/src/devices/usb/OHCI.cpp b/src/devices/usb/OHCI.cpp index 0d7f10da1..bac3a5712 100644 --- a/src/devices/usb/OHCI.cpp +++ b/src/devices/usb/OHCI.cpp @@ -25,9 +25,9 @@ // * // ****************************************************************** -// Acknowledgment: QEMU ohci subsystem as used in XQEMU (GPLv2) +// Acknowledgment: QEMU ohci subsystem as used in XQEMU (GPLv2) // https://xqemu.com/ - + /* * QEMU USB OHCI Emulation * Copyright (c) 2004 Gianni Tedesco @@ -56,50 +56,50 @@ #include "OHCI.h" #include "core\kernel\exports\EmuKrnl.h" // For HalSystemInterrupt #include "common\util\CxbxUtil.h" -#include "Logging.h" - +#include "Logging.h" + static const char* OHCI_RegNames[] = { - "HcRevision", - "HcControl", - "HcCommandStatus", - "HcInterruptStatus", - "HcInterruptEnable", - "HcInterruptDisable", - "HcHCCA", - "HcPeriodCurrentED", - "HcControlHeadED", - "HcControlCurrentED", - "HcBulkHeadED", - "HcBulkCurrentED", - "HcDoneHead", - "HcFmInterval", - "HcFmRemaining", - "HcFmNumber", - "HcPeriodicStart", - "HcLSThreshold", - "HcRhDescriptorA", - "HcRhDescriptorB", - "HcRhStatus", - "HcRhPortStatus[0]", - "HcRhPortStatus[1]", - "HcRhPortStatus[2]", + "HcRevision", + "HcControl", + "HcCommandStatus", + "HcInterruptStatus", + "HcInterruptEnable", + "HcInterruptDisable", + "HcHCCA", + "HcPeriodCurrentED", + "HcControlHeadED", + "HcControlCurrentED", + "HcBulkHeadED", + "HcBulkCurrentED", + "HcDoneHead", + "HcFmInterval", + "HcFmRemaining", + "HcFmNumber", + "HcPeriodicStart", + "HcLSThreshold", + "HcRhDescriptorA", + "HcRhDescriptorB", + "HcRhStatus", + "HcRhPortStatus[0]", + "HcRhPortStatus[1]", + "HcRhPortStatus[2]", "HcRhPortStatus[3]" }; /* Define these two if you want to dump usb packets and OHCI registers */ //#define DEBUG_ISOCH -//#define DEBUG_PACKET +//#define DEBUG_PACKET //#define DEBUG_OHCI_REG - -#ifdef DEBUG_OHCI_REG -#define DUMP_REG_R(reg_val) EmuLog(LOG_LEVEL::DEBUG, "%s, R, reg_val: 0x%X", OHCI_RegNames[Addr >> 2], reg_val) -#define DUMP_REG_W(reg_val, val) EmuLog(LOG_LEVEL::DEBUG, "%s, W, reg_val: 0x%X, val: 0x%X", OHCI_RegNames[Addr >> 2], reg_val, val) -#define DUMP_REG_RO(reg_val, val) EmuLog(LOG_LEVEL::DEBUG, "%s, RO, reg_val: 0x%X, val: 0x%X", OHCI_RegNames[Addr >> 2], reg_val, val) -#else -#define DUMP_REG_R(reg_val) -#define DUMP_REG_W(reg_val, val) -#define DUMP_REG_RO(reg_val, val) -#endif + +#ifdef DEBUG_OHCI_REG +#define DUMP_REG_R(reg_val) EmuLog(LOG_LEVEL::DEBUG, "%s, R, reg_val: 0x%X", OHCI_RegNames[Addr >> 2], reg_val) +#define DUMP_REG_W(reg_val, val) EmuLog(LOG_LEVEL::DEBUG, "%s, W, reg_val: 0x%X, val: 0x%X", OHCI_RegNames[Addr >> 2], reg_val, val) +#define DUMP_REG_RO(reg_val, val) EmuLog(LOG_LEVEL::DEBUG, "%s, RO, reg_val: 0x%X, val: 0x%X", OHCI_RegNames[Addr >> 2], reg_val, val) +#else +#define DUMP_REG_R(reg_val) +#define DUMP_REG_W(reg_val, val) +#define DUMP_REG_RO(reg_val, val) +#endif /* These macros are used to access the bits of the various registers */ // HcControl @@ -426,7 +426,7 @@ bool OHCI::OHCI_WriteIsoTD(xbaddr Paddr, OHCI_ISO_TD* td) bool OHCI::OHCI_CopyTDBuffer(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsWrite) { - uint32_t ptr; + uint32_t ptr; int n; // Figure out if we are crossing a 4K page boundary @@ -457,7 +457,7 @@ bool OHCI::OHCI_CopyTDBuffer(OHCI_TD* Td, uint8_t* Buffer, int Length, bool bIsW bool OHCI::OHCI_CopyIsoTDBuffer(uint32_t start_addr, uint32_t end_addr, uint8_t* Buffer, int Length, bool bIsWrite) { - uint32_t ptr; + uint32_t ptr; int n; ptr = start_addr; @@ -964,124 +964,124 @@ uint32_t OHCI::OHCI_ReadRegister(xbaddr Addr) switch (Addr >> 2) // read the register { case 0: // HcRevision - ret = m_Registers.HcRevision; + ret = m_Registers.HcRevision; DUMP_REG_R(ret); break; case 1: // HcControl - ret = m_Registers.HcControl; + ret = m_Registers.HcControl; DUMP_REG_R(ret); break; case 2: // HcCommandStatus - ret = m_Registers.HcCommandStatus; + ret = m_Registers.HcCommandStatus; DUMP_REG_R(ret); break; case 3: // HcInterruptStatus - ret = m_Registers.HcInterruptStatus; + ret = m_Registers.HcInterruptStatus; DUMP_REG_R(ret); break; case 4: // HcInterruptEnable case 5: // HcInterruptDisable - ret = m_Registers.HcInterrupt; + ret = m_Registers.HcInterrupt; DUMP_REG_R(ret); break; case 6: // HcHCCA - ret = m_Registers.HcHCCA; + ret = m_Registers.HcHCCA; DUMP_REG_R(ret); break; case 7: // HcPeriodCurrentED - ret = m_Registers.HcPeriodCurrentED; + ret = m_Registers.HcPeriodCurrentED; DUMP_REG_R(ret); break; case 8: // HcControlHeadED - ret = m_Registers.HcControlHeadED; + ret = m_Registers.HcControlHeadED; DUMP_REG_R(ret); break; case 9: // HcControlCurrentED - ret = m_Registers.HcControlCurrentED; + ret = m_Registers.HcControlCurrentED; DUMP_REG_R(ret); break; case 10: // HcBulkHeadED - ret = m_Registers.HcBulkHeadED; + ret = m_Registers.HcBulkHeadED; DUMP_REG_R(ret); break; case 11: // HcBulkCurrentED - ret = m_Registers.HcBulkCurrentED; + ret = m_Registers.HcBulkCurrentED; DUMP_REG_R(ret); break; case 12: // HcDoneHead - ret = m_Registers.HcDoneHead; + ret = m_Registers.HcDoneHead; DUMP_REG_R(ret); break; case 13: // HcFmInterval - ret = m_Registers.HcFmInterval; + ret = m_Registers.HcFmInterval; DUMP_REG_R(ret); break; case 14: // HcFmRemaining - ret = OHCI_GetFrameRemaining(); + ret = OHCI_GetFrameRemaining(); DUMP_REG_R(ret); break; case 15: // HcFmNumber - ret = m_Registers.HcFmNumber; + ret = m_Registers.HcFmNumber; DUMP_REG_R(ret); break; case 16: // HcPeriodicStart - ret = m_Registers.HcPeriodicStart; + ret = m_Registers.HcPeriodicStart; DUMP_REG_R(ret); break; case 17: // HcLSThreshold - ret = m_Registers.HcLSThreshold; + ret = m_Registers.HcLSThreshold; DUMP_REG_R(ret); break; case 18: // HcRhDescriptorA - ret = m_Registers.HcRhDescriptorA; + ret = m_Registers.HcRhDescriptorA; DUMP_REG_R(ret); break; case 19: // HcRhDescriptorB - ret = m_Registers.HcRhDescriptorB; + ret = m_Registers.HcRhDescriptorB; DUMP_REG_R(ret); break; case 20: // HcRhStatus - ret = m_Registers.HcRhStatus; + ret = m_Registers.HcRhStatus; DUMP_REG_R(ret); break; // Always report that the port power is on since the Xbox cannot switch off the electrical current to it case 21: // RhPort 0 - ret = m_Registers.RhPort[0].HcRhPortStatus | OHCI_PORT_PPS; + ret = m_Registers.RhPort[0].HcRhPortStatus | OHCI_PORT_PPS; DUMP_REG_R(ret); break; case 22: // RhPort 1 - ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS; + ret = m_Registers.RhPort[1].HcRhPortStatus | OHCI_PORT_PPS; DUMP_REG_R(ret); break; case 23: // RhPort 2 - ret = m_Registers.RhPort[2].HcRhPortStatus | OHCI_PORT_PPS; + ret = m_Registers.RhPort[2].HcRhPortStatus | OHCI_PORT_PPS; DUMP_REG_R(ret); break; case 24: // RhPort 3 - ret = m_Registers.RhPort[3].HcRhPortStatus | OHCI_PORT_PPS; + ret = m_Registers.RhPort[3].HcRhPortStatus | OHCI_PORT_PPS; DUMP_REG_R(ret); break; @@ -1103,12 +1103,12 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value) switch (Addr >> 2) { case 0: // HcRevision - // This register is read-only + // This register is read-only DUMP_REG_RO(m_Registers.HcRevision, Value); break; case 1: // HcControl - OHCI_ChangeState(Value); + OHCI_ChangeState(Value); DUMP_REG_W(m_Registers.HcControl, Value); break; @@ -1124,62 +1124,62 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value) if (m_Registers.HcCommandStatus & OHCI_STATUS_HCR) { // Do a hardware reset OHCI_StateReset(); - } + } DUMP_REG_W(m_Registers.HcCommandStatus, Value); } break; case 3: // HcInterruptStatus m_Registers.HcInterruptStatus &= ~Value; - OHCI_UpdateInterrupt(); + OHCI_UpdateInterrupt(); DUMP_REG_W(m_Registers.HcInterruptStatus, Value); break; case 4: // HcInterruptEnable m_Registers.HcInterrupt |= Value; - OHCI_UpdateInterrupt(); + OHCI_UpdateInterrupt(); DUMP_REG_W(m_Registers.HcInterrupt, Value); break; case 5: // HcInterruptDisable m_Registers.HcInterrupt &= ~Value; - OHCI_UpdateInterrupt(); + OHCI_UpdateInterrupt(); DUMP_REG_W(m_Registers.HcInterrupt, Value); break; case 6: // HcHCCA // The standard says the minimum alignment is 256 bytes and so bits 0 through 7 are always zero - m_Registers.HcHCCA = Value & OHCI_HCCA_MASK; + m_Registers.HcHCCA = Value & OHCI_HCCA_MASK; DUMP_REG_W(m_Registers.HcHCCA, Value); break; case 7: // HcPeriodCurrentED - // This register is read-only + // This register is read-only DUMP_REG_RO(m_Registers.HcPeriodCurrentED, Value); break; case 8: // HcControlHeadED - m_Registers.HcControlHeadED = Value & OHCI_DPTR_MASK; + m_Registers.HcControlHeadED = Value & OHCI_DPTR_MASK; DUMP_REG_W(m_Registers.HcControlHeadED, Value); break; case 9: // HcControlCurrentED - m_Registers.HcControlCurrentED = Value & OHCI_DPTR_MASK; + m_Registers.HcControlCurrentED = Value & OHCI_DPTR_MASK; DUMP_REG_W(m_Registers.HcControlCurrentED, Value); break; case 10: // HcBulkHeadED - m_Registers.HcBulkHeadED = Value & OHCI_DPTR_MASK; + m_Registers.HcBulkHeadED = Value & OHCI_DPTR_MASK; DUMP_REG_W(m_Registers.HcBulkHeadED, Value); break; case 11: // HcBulkCurrentED - m_Registers.HcBulkCurrentED = Value & OHCI_DPTR_MASK; + m_Registers.HcBulkCurrentED = Value & OHCI_DPTR_MASK; DUMP_REG_W(m_Registers.HcBulkCurrentED, Value); break; case 12: // HcDoneHead - // This register is read-only + // This register is read-only DUMP_REG_RO(m_Registers.HcDoneHead, Value); break; @@ -1188,34 +1188,34 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value) if ((Value & OHCI_FMI_FI) != (m_Registers.HcFmInterval & OHCI_FMI_FI)) { EmuLog(LOG_LEVEL::DEBUG, "Changing frame interval duration. New value is %u", Value & OHCI_FMI_FI); } - m_Registers.HcFmInterval = Value & ~0xC000; + m_Registers.HcFmInterval = Value & ~0xC000; DUMP_REG_W(m_Registers.HcFmInterval, Value); } break; case 14: // HcFmRemaining - // This register is read-only + // This register is read-only DUMP_REG_RO(m_Registers.HcFmRemaining, Value); break; case 15: // HcFmNumber - // This register is read-only + // This register is read-only DUMP_REG_RO(m_Registers.HcFmNumber, Value); break; case 16: // HcPeriodicStart - m_Registers.HcPeriodicStart = Value & 0x3FFF; + m_Registers.HcPeriodicStart = Value & 0x3FFF; DUMP_REG_W(m_Registers.HcPeriodicStart, Value); break; case 17: // HcLSThreshold - m_Registers.HcLSThreshold = Value & 0xFFF; + m_Registers.HcLSThreshold = Value & 0xFFF; DUMP_REG_W(m_Registers.HcLSThreshold, Value); break; case 18: // HcRhDescriptorA m_Registers.HcRhDescriptorA &= ~OHCI_RHA_RW_MASK; - m_Registers.HcRhDescriptorA |= Value & OHCI_RHA_RW_MASK; // ?? + m_Registers.HcRhDescriptorA |= Value & OHCI_RHA_RW_MASK; // ?? DUMP_REG_W(m_Registers.HcRhDescriptorA, Value); break; @@ -1224,27 +1224,27 @@ void OHCI::OHCI_WriteRegister(xbaddr Addr, uint32_t Value) break; case 20: // HcRhStatus - OHCI_SetHubStatus(Value); + OHCI_SetHubStatus(Value); DUMP_REG_W(m_Registers.HcRhStatus, Value); break; case 21: // RhPort 0 - OHCI_PortSetStatus(0, Value); + OHCI_PortSetStatus(0, Value); DUMP_REG_W(m_Registers.RhPort[0].HcRhPortStatus, Value); break; case 22: // RhPort 1 - OHCI_PortSetStatus(1, Value); + OHCI_PortSetStatus(1, Value); DUMP_REG_W(m_Registers.RhPort[1].HcRhPortStatus, Value); break; case 23: // RhPort 2 - OHCI_PortSetStatus(2, Value); + OHCI_PortSetStatus(2, Value); DUMP_REG_W(m_Registers.RhPort[2].HcRhPortStatus, Value); break; case 24: // RhPort 3 - OHCI_PortSetStatus(3, Value); + OHCI_PortSetStatus(3, Value); DUMP_REG_W(m_Registers.RhPort[3].HcRhPortStatus, Value); break; diff --git a/src/devices/usb/OHCI.h b/src/devices/usb/OHCI.h index a7ab20eb8..8048ee1e4 100644 --- a/src/devices/usb/OHCI.h +++ b/src/devices/usb/OHCI.h @@ -40,7 +40,7 @@ // SOF: start of frame; the beginning of a USB-defined frame // EOF: end of frame; the end of a USB-defined frame // ED: endpoint descriptor; a memory structure used by the HC to communicate with an endpoint -// TD: transfer descriptor; a memory structure used by the HC to transfer a block of data to/from a device endpoint +// TD: transfer descriptor; a memory structure used by the HC to transfer a block of data to/from a device endpoint // HCCA: Host Controller Communications Area; shared memory between the HC and HCD @@ -132,10 +132,10 @@ struct OHCI_Registers /* OHCI class representing the state of the HC */ class OHCI { - public: - // Indicates that the timer thread is accessing the OHCI object. Necessary because the input thread from the - // InputDeviceManager will access us when it needs to create or destroy a device - std::mutex m_FrameTimeMutex; + public: + // Indicates that the timer thread is accessing the OHCI object. Necessary because the input thread from the + // InputDeviceManager will access us when it needs to create or destroy a device + std::mutex m_FrameTimeMutex; // constructor OHCI(USBDevice* UsbObj); diff --git a/src/devices/usb/USBDevice.cpp b/src/devices/usb/USBDevice.cpp index 63523d1aa..26ed2fdbb 100644 --- a/src/devices/usb/USBDevice.cpp +++ b/src/devices/usb/USBDevice.cpp @@ -25,9 +25,9 @@ // * // ****************************************************************** -// Acknowledgment: QEMU usb subsystem as used in XQEMU (GPLv2) +// Acknowledgment: QEMU usb subsystem as used in XQEMU (GPLv2) // https://xqemu.com/ - + /* * QEMU USB emulation * @@ -56,9 +56,9 @@ #define LOG_PREFIX CXBXR_MODULE::USB - -#include // For PKINTERRUPT, etc. + +#include // For PKINTERRUPT, etc. #include #include "USBDevice.h" #include "OHCI.h" @@ -208,7 +208,7 @@ USBEndpoint* USBDevice::USB_GetEP(XboxDeviceState* Dev, int Pid, int Ep) return eps + Ep - 1; } -void USBDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, uint64_t Id, +void USBDevice::USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, uint64_t Id, bool ShortNotOK, bool IntReq) { assert(!USB_IsPacketInflight(p)); diff --git a/src/devices/usb/USBDevice.h b/src/devices/usb/USBDevice.h index 30d826e52..f07f91d11 100644 --- a/src/devices/usb/USBDevice.h +++ b/src/devices/usb/USBDevice.h @@ -85,7 +85,7 @@ class USBDevice : public PCIDevice { // find the requested endpoint in the supplied device USBEndpoint* USB_GetEP(XboxDeviceState* Dev, int Pid, int Ep); // setup a packet for transfer - void USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, uint64_t Id, + void USB_PacketSetup(USBPacket* p, int Pid, USBEndpoint* Ep, uint64_t Id, bool ShortNotOK, bool IntReq); // check if the state of the packet is queued or async bool USB_IsPacketInflight(USBPacket* p); @@ -142,26 +142,26 @@ class USBDevice : public PCIDevice { void USB_PortLocation(USBPort* downstream, USBPort* upstream, int portnr); // initialize the endpoints of this peripheral void USB_EpInit(XboxDeviceState* dev); - // reset all endpoints of this peripheral + // reset all endpoints of this peripheral void USB_EpReset(XboxDeviceState* dev); - // create a serial number for the device + // create a serial number for the device void USB_CreateSerial(XboxDeviceState* dev, std::string&& str); - // start descriptors initialization + // start descriptors initialization void USBDesc_Init(XboxDeviceState* dev); - // get device descriptor + // get device descriptor const USBDesc* USBDesc_GetUsbDeviceDesc(XboxDeviceState* dev); - // set the descriptors to use for this device + // set the descriptors to use for this device void USBDesc_SetDefaults(XboxDeviceState* dev); - // set the configuration to use + // set the configuration to use int USBDesc_SetConfig(XboxDeviceState* dev, int value); - // set the interface to use + // set the interface to use int USBDesc_SetInterface(XboxDeviceState* dev, int index, int value); - // find the interface to use + // find the interface to use const USBDescIface* USBDesc_FindInterface(XboxDeviceState* dev, int nif, int alt); - // setup endpoints and their descriptors + // setup endpoints and their descriptors void USBDesc_EpInit(XboxDeviceState* dev); // handle standard control request - int USBDesc_HandleControl(XboxDeviceState *dev, USBPacket *p, + int USBDesc_HandleControl(XboxDeviceState *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data); // handle standard GetDescriptor() request int USBDesc_HandleStandardGetDescriptor(XboxDeviceState* dev, USBPacket* p, diff --git a/src/devices/usb/UsbCommon.h b/src/devices/usb/UsbCommon.h index 5f802caa6..74765f0ec 100644 --- a/src/devices/usb/UsbCommon.h +++ b/src/devices/usb/UsbCommon.h @@ -1,33 +1,33 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2018 ergo720 -// * -// * All rights reserved -// * -// ****************************************************************** - -// Acknowledgment: some these functions are from the QEMU usb api used in XQEMU (GPLv2) -// https://xqemu.com/ - +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018 ergo720 +// * +// * All rights reserved +// * +// ****************************************************************** + +// Acknowledgment: some these functions are from the QEMU usb api used in XQEMU (GPLv2) +// https://xqemu.com/ + /* * QEMU USB API * @@ -53,21 +53,21 @@ */ #ifndef USBCOMMON_H_ -#define USBCOMMON_H_ - -#include "common\util\CxbxUtil.h" -#include "..\devices\video\queue.h" -#include - -#define USB_MAX_ENDPOINTS 15 -#define USB_MAX_INTERFACES 16 - -#define USB_STATE_NOTATTACHED 0 -#define USB_STATE_ATTACHED 1 -#define USB_STATE_DEFAULT 2 - -#define USB_CLASS_HUB 9 - +#define USBCOMMON_H_ + +#include "common\util\CxbxUtil.h" +#include "..\devices\video\queue.h" +#include + +#define USB_MAX_ENDPOINTS 15 +#define USB_MAX_INTERFACES 16 + +#define USB_STATE_NOTATTACHED 0 +#define USB_STATE_ATTACHED 1 +#define USB_STATE_DEFAULT 2 + +#define USB_CLASS_HUB 9 + #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 #define USB_ENDPOINT_XFER_BULK 2 @@ -77,70 +77,70 @@ #define USB_INTERFACE_INVALID 255 #define USB_DIR_OUT 0 -#define USB_DIR_IN 0x80 - +#define USB_DIR_IN 0x80 + #define USB_TOKEN_SETUP 0x2D #define USB_TOKEN_IN 0x69 // device -> host -#define USB_TOKEN_OUT 0xE1 // host -> device - +#define USB_TOKEN_OUT 0xE1 // host -> device + #define USB_SPEED_LOW 0 -#define USB_SPEED_FULL 1 - +#define USB_SPEED_FULL 1 + #define USB_DEVICE_SELF_POWERED 0 -#define USB_DEVICE_REMOTE_WAKEUP 1 - -#define USB_TYPE_MASK (0x03 << 5) -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -#define USB_RECIP_MASK 0x1F -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) -#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) -#define VendorDeviceRequest ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) -#define VendorDeviceOutRequest ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) - -#define InterfaceRequest \ - ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) -#define InterfaceOutRequest \ - ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) -#define ClassInterfaceRequest \ - ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) -#define ClassInterfaceOutRequest \ - ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) -#define VendorInterfaceRequest \ - ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) -#define VendorInterfaceOutRequest \ - ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) - -#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) -#define EndpointOutRequest \ - ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) - -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -#define USB_REQ_SET_FEATURE 0x03 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - +#define USB_DEVICE_REMOTE_WAKEUP 1 + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1F +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define VendorDeviceRequest ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) +#define VendorDeviceOutRequest ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) + +#define InterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define InterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define ClassInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define ClassInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define VendorInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) +#define VendorInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE)<<8) + +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + #define USB_RET_SUCCESS (0) #define USB_RET_NODEV (-1) #define USB_RET_NAK (-2) @@ -149,129 +149,129 @@ #define USB_RET_IOERROR (-5) #define USB_RET_ASYNC (-6) #define USB_RET_ADD_TO_QUEUE (-7) -#define USB_RET_REMOVE_FROM_QUEUE (-8) - - +#define USB_RET_REMOVE_FROM_QUEUE (-8) + + typedef enum _USB_SPEED { USB_SPEED_MASK_LOW = 1 << 0, USB_SPEED_MASK_FULL = 1 << 1, } -USB_SPEED; - -typedef enum USBPacketState { - USB_PACKET_UNDEFINED = 0, - USB_PACKET_SETUP, - USB_PACKET_QUEUED, - USB_PACKET_ASYNC, - USB_PACKET_COMPLETE, - USB_PACKET_CANCELED, -} -USBPacketState; - -/* same as Linux kernel root hubs */ -typedef enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, -} -STRING_DESC_INDEX; - -// Forward declarations -struct USBPacket; -struct XboxDeviceState; -struct USBPortOps; - -/* String descriptor */ +USB_SPEED; + +typedef enum USBPacketState { + USB_PACKET_UNDEFINED = 0, + USB_PACKET_SETUP, + USB_PACKET_QUEUED, + USB_PACKET_ASYNC, + USB_PACKET_COMPLETE, + USB_PACKET_CANCELED, +} +USBPacketState; + +/* same as Linux kernel root hubs */ +typedef enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, +} +STRING_DESC_INDEX; + +// Forward declarations +struct USBPacket; +struct XboxDeviceState; +struct USBPortOps; + +/* String descriptor */ struct USBDescString { uint8_t index; // index of this string descriptor std::string str; // the string of this string descriptor QLIST_ENTRY(USBDescString) next; -}; - -// Device-specific class descriptors, if any. No idea if some Xbox devices use this but, if not, this can be removed -struct USBDescOther { - uint8_t length; - const uint8_t* data; -}; - -/* Endpoint descriptor */ -struct USBDescEndpoint { - uint8_t bEndpointAddress; // the address of the endpoint on the USB device described by this descriptor - uint8_t bmAttributes; // this field describes the endpoint's attributes when it is configured using the bConfigurationValue - uint16_t wMaxPacketSize; // maximum packet size this endpoint is capable of sending or receiving when this configuration is selected - uint8_t bInterval; // interval for polling endpoint for data transfers, expressed in milliseconds. - uint8_t bRefresh; // for audio devices only: the rate at which synchronization feedback is provided - uint8_t bSynchAddress; // for audio devices only: the address of the synchronization endpoint - - uint8_t is_audio; // has bRefresh + bSynchAddress - uint8_t* extra; // class-specific descriptors (if any) associated with this endpoint - - // Dropped from QEMU the parameters bMaxBurst, bmAttributes_super and wBytesPerInterval because those are only defined for - // superspeed (usb 3.0) devices in the superspeed endpoint companion -}; - -/* Interface descriptor */ -struct USBDescIface { - uint8_t bInterfaceNumber; // number of interface - uint8_t bAlternateSetting; // value used to select the alternate setting for the interface identified by bInterfaceNumber - uint8_t bNumEndpoints; // number of endpoints used by this interface (excluding endpoint zero) - uint8_t bInterfaceClass; // class code (assigned by the USB) - uint8_t bInterfaceSubClass; // subclass code (assigned by the USB) - uint8_t bInterfaceProtocol; // protocol code (assigned by the USB) - uint8_t iInterface; // index of string descriptor describing this interface - - uint8_t ndesc; // number of device-specific class descriptors (if any) - USBDescOther* descs; // pointer to the extra class descriptors - const USBDescEndpoint* eps; // endpoints supported by this interface -}; - -/* -* ergo720: I removed the Interface Association Descriptor (IAD) since, at the time of this writing (2018), the xboxdevwiki documents that all -* known xid devices don't use them and also, according to the corresponding standard, IAD applies to usb revision 2.0 while the xbox uses -* usb revision 1.1 so it shouldn't support them either. If this turns out to be incorrect, then IAD support will have to be added -*/ - -/* Configuration descriptor */ -struct USBDescConfig { - uint8_t bNumInterfaces; // number of interfaces supported by this configuration - uint8_t bConfigurationValue; // value to use as an argument to the SetConfiguration() request to select this configuration - uint8_t iConfiguration; // index of string descriptor describing this configuration - uint8_t bmAttributes; // configuration characteristics - uint8_t bMaxPower; // maximum power consumption of the USB device in this configuration expressed in 2mA units - uint8_t nif; // number of interfaces (again) - const USBDescIface* ifs; // interfaces supported by this configuration -}; - -/* Device descriptor part 1 */ -struct USBDescDevice { - uint16_t bcdUSB; // USB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H) - uint8_t bDeviceClass; // class code (assigned by the USB) - uint8_t bDeviceSubClass; // subclass code (assigned by the USB) - uint8_t bDeviceProtocol; // protocol code (assigned by the USB) - uint8_t bMaxPacketSize0; // maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid) - uint8_t bNumConfigurations; // number of possible configurations - const USBDescConfig* confs; // configurations supported by this device -}; - -/* Device descriptor part 2 */ -struct USBDescID { - uint16_t idVendor; // vendor ID (assigned by the USB) - uint16_t idProduct; // product ID (assigned by the manufacturer) - uint16_t bcdDevice; // device release number in binary-coded decimal - uint8_t iManufacturer; // index of string descriptor describing manufacturer - uint8_t iProduct; // index of string descriptor describing product - uint8_t iSerialNumber; // index of string descriptor describing the device’s serial number -}; - -/* Global USB Descriptor struct */ -struct USBDesc { - USBDescID id; // id-specific info of the device descriptor - const USBDescDevice* full; // remaining fields of the device descriptor -}; - -#pragma pack(1) - +}; + +// Device-specific class descriptors, if any. No idea if some Xbox devices use this but, if not, this can be removed +struct USBDescOther { + uint8_t length; + const uint8_t* data; +}; + +/* Endpoint descriptor */ +struct USBDescEndpoint { + uint8_t bEndpointAddress; // the address of the endpoint on the USB device described by this descriptor + uint8_t bmAttributes; // this field describes the endpoint's attributes when it is configured using the bConfigurationValue + uint16_t wMaxPacketSize; // maximum packet size this endpoint is capable of sending or receiving when this configuration is selected + uint8_t bInterval; // interval for polling endpoint for data transfers, expressed in milliseconds. + uint8_t bRefresh; // for audio devices only: the rate at which synchronization feedback is provided + uint8_t bSynchAddress; // for audio devices only: the address of the synchronization endpoint + + uint8_t is_audio; // has bRefresh + bSynchAddress + uint8_t* extra; // class-specific descriptors (if any) associated with this endpoint + + // Dropped from QEMU the parameters bMaxBurst, bmAttributes_super and wBytesPerInterval because those are only defined for + // superspeed (usb 3.0) devices in the superspeed endpoint companion +}; + +/* Interface descriptor */ +struct USBDescIface { + uint8_t bInterfaceNumber; // number of interface + uint8_t bAlternateSetting; // value used to select the alternate setting for the interface identified by bInterfaceNumber + uint8_t bNumEndpoints; // number of endpoints used by this interface (excluding endpoint zero) + uint8_t bInterfaceClass; // class code (assigned by the USB) + uint8_t bInterfaceSubClass; // subclass code (assigned by the USB) + uint8_t bInterfaceProtocol; // protocol code (assigned by the USB) + uint8_t iInterface; // index of string descriptor describing this interface + + uint8_t ndesc; // number of device-specific class descriptors (if any) + USBDescOther* descs; // pointer to the extra class descriptors + const USBDescEndpoint* eps; // endpoints supported by this interface +}; + +/* +* ergo720: I removed the Interface Association Descriptor (IAD) since, at the time of this writing (2018), the xboxdevwiki documents that all +* known xid devices don't use them and also, according to the corresponding standard, IAD applies to usb revision 2.0 while the xbox uses +* usb revision 1.1 so it shouldn't support them either. If this turns out to be incorrect, then IAD support will have to be added +*/ + +/* Configuration descriptor */ +struct USBDescConfig { + uint8_t bNumInterfaces; // number of interfaces supported by this configuration + uint8_t bConfigurationValue; // value to use as an argument to the SetConfiguration() request to select this configuration + uint8_t iConfiguration; // index of string descriptor describing this configuration + uint8_t bmAttributes; // configuration characteristics + uint8_t bMaxPower; // maximum power consumption of the USB device in this configuration expressed in 2mA units + uint8_t nif; // number of interfaces (again) + const USBDescIface* ifs; // interfaces supported by this configuration +}; + +/* Device descriptor part 1 */ +struct USBDescDevice { + uint16_t bcdUSB; // USB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H) + uint8_t bDeviceClass; // class code (assigned by the USB) + uint8_t bDeviceSubClass; // subclass code (assigned by the USB) + uint8_t bDeviceProtocol; // protocol code (assigned by the USB) + uint8_t bMaxPacketSize0; // maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid) + uint8_t bNumConfigurations; // number of possible configurations + const USBDescConfig* confs; // configurations supported by this device +}; + +/* Device descriptor part 2 */ +struct USBDescID { + uint16_t idVendor; // vendor ID (assigned by the USB) + uint16_t idProduct; // product ID (assigned by the manufacturer) + uint16_t bcdDevice; // device release number in binary-coded decimal + uint8_t iManufacturer; // index of string descriptor describing manufacturer + uint8_t iProduct; // index of string descriptor describing product + uint8_t iSerialNumber; // index of string descriptor describing the device’s serial number +}; + +/* Global USB Descriptor struct */ +struct USBDesc { + USBDescID id; // id-specific info of the device descriptor + const USBDescDevice* full; // remaining fields of the device descriptor +}; + +#pragma pack(1) + // Binary representation of the descriptors // Dropped from QEMU usb 2.0 and 3.0 only descriptors struct USBDescriptor { @@ -324,128 +324,128 @@ struct USBDescriptor { uint8_t bSynchAddress; // only audio ep } endpoint; // endpoint descriptor } u; -}; - -#pragma pack() - -/* USB endpoint */ -struct USBEndpoint { - uint8_t Num; // endpoint number - uint8_t Type; // the type of this endpoint - uint8_t IfNum; // interface number this endpoint belongs to - int MaxPacketSize; // maximum packet size supported by this endpoint - bool Halted; // indicates that the endpoint is halted - XboxDeviceState* Dev; // device this endpoint belongs to - QTAILQ_HEAD(, USBPacket) Queue; // queue of packets to this endpoint -}; - -/* Struct describing the status of a usb port */ -struct USBPort { - XboxDeviceState* Dev; // usb device (if present) - USBPortOps* Operations; // functions to call when a port event happens - int SpeedMask; // usb speeds supported - std::string Path; // the number of the port + 1, used to create a serial number for this device - int PortIndex; // internal port index -}; - -/* Struct which stores general functions/variables regarding the peripheral */ -struct USBDeviceClass { - std::function init; - - // Walk (enabled) downstream ports, check for a matching device. - // Only hubs implement this. - std::function find_device; - - // Called when a packet is canceled. - std::function cancel_packet; - - // Called when device is destroyed. - std::function handle_destroy; - - // Attach the device - std::function handle_attach; - - // Reset the device - std::function handle_reset; - - // Process control request. - // Called from handle_packet(). - // Status gets stored in p->status, and if p->status == USB_RET_SUCCESS - // then the number of bytes transferred is stored in p->actual_length - std::function handle_control; - - // Process data transfers (both BULK and ISOC). - // Called from handle_packet(). - // Status gets stored in p->status, and if p->status == USB_RET_SUCCESS - // then the number of bytes transferred is stored in p->actual_length - std::function handle_data; - - std::function set_interface; - - // Called when the hcd is done queuing packets for an endpoint, only - // necessary for devices which can return USB_RET_ADD_TO_QUEUE. - std::function flush_ep_queue; - - // Called by the hcd to let the device know the queue for an endpoint - // has been unlinked / stopped. Optional may be NULL. - std::function ep_stopped; - - const char* product_desc; // friendly name of the device - const USBDesc* usb_desc; // device descriptor -}; - -/* definition of an Xbox usb device */ -struct XboxDeviceState { - USBPort* Port; // usb port struct of this device - int PortPath; // port index to which this device is attached to - USBDeviceClass* klass; // usb class struct of this device - - int Speed; // actual speed of the connected device - int SpeedMask; // supported speeds, not in info because it may be variable (hostdevs) - uint8_t Addr; // device function address - std::string ProductDesc; // the friendly name of this device - int Attached; // device is attached - - int32_t State; // current state of device - uint8_t SetupBuffer[8]; // setup packet buffer - 8 bytes (control transfers only) - uint8_t DataBuffer[4096]; // buffer where to write the data requested during usb requests - int32_t RemoteWakeup; // wakeup flag - int32_t SetupState; // result of a control transfer processing operation - int32_t SetupLength; // this field specifies the length of the data transferred during the second phase of the control transfer - int32_t SetupIndex; // index of the parameter in a setup token? - - USBEndpoint EP_ctl; // control endpoint - USBEndpoint EP_in[USB_MAX_ENDPOINTS]; // device endpoint (input direction) - USBEndpoint EP_out[USB_MAX_ENDPOINTS]; // device endpoint (output direction) - - QLIST_HEAD(, USBDescString) Strings; // strings of the string descriptors - const USBDesc* UsbDesc; // Overrides class usb_desc if not nullptr - const USBDescDevice* Device; // device descriptor part 1 - - int Configuration; // number of the selected configuration descriptor - int NumInterfaces; // number of available interface descriptors - int AltSetting[USB_MAX_INTERFACES]; // alternate setting numbers for the current interface - const USBDescConfig* Config; // configuration in use - const USBDescIface* Ifaces[USB_MAX_INTERFACES]; // interface in use -}; - -/* Structure used to hold information about an active USB packet */ -struct USBPacket { - int Pid; // Packet ID (used to identify the type of packet that is being processed) - uint32_t Id; // Paddr of the TD for this packet - USBEndpoint* Endpoint; // endpoint this packet is transferred to - IOVector IoVec; // used to perform vectored I/O - bool ShortNotOK; // the bufferRounding mode of the TD for this packet - bool IntReq; // whether or not to generate an interrupt for this packet (DelayInterrupt of the TD is zero) - int Status; // USB_RET_* status code - int ActualLength; // number of bytes actually written to DataBuffer - // Internal use by the USB layer - USBPacketState State; - QTAILQ_ENTRY(USBPacket) Queue; -}; - +}; + +#pragma pack() + +/* USB endpoint */ +struct USBEndpoint { + uint8_t Num; // endpoint number + uint8_t Type; // the type of this endpoint + uint8_t IfNum; // interface number this endpoint belongs to + int MaxPacketSize; // maximum packet size supported by this endpoint + bool Halted; // indicates that the endpoint is halted + XboxDeviceState* Dev; // device this endpoint belongs to + QTAILQ_HEAD(, USBPacket) Queue; // queue of packets to this endpoint +}; + +/* Struct describing the status of a usb port */ +struct USBPort { + XboxDeviceState* Dev; // usb device (if present) + USBPortOps* Operations; // functions to call when a port event happens + int SpeedMask; // usb speeds supported + std::string Path; // the number of the port + 1, used to create a serial number for this device + int PortIndex; // internal port index +}; + +/* Struct which stores general functions/variables regarding the peripheral */ +struct USBDeviceClass { + std::function init; + + // Walk (enabled) downstream ports, check for a matching device. + // Only hubs implement this. + std::function find_device; + + // Called when a packet is canceled. + std::function cancel_packet; + + // Called when device is destroyed. + std::function handle_destroy; + + // Attach the device + std::function handle_attach; + + // Reset the device + std::function handle_reset; + + // Process control request. + // Called from handle_packet(). + // Status gets stored in p->status, and if p->status == USB_RET_SUCCESS + // then the number of bytes transferred is stored in p->actual_length + std::function handle_control; + + // Process data transfers (both BULK and ISOC). + // Called from handle_packet(). + // Status gets stored in p->status, and if p->status == USB_RET_SUCCESS + // then the number of bytes transferred is stored in p->actual_length + std::function handle_data; + + std::function set_interface; + + // Called when the hcd is done queuing packets for an endpoint, only + // necessary for devices which can return USB_RET_ADD_TO_QUEUE. + std::function flush_ep_queue; + + // Called by the hcd to let the device know the queue for an endpoint + // has been unlinked / stopped. Optional may be NULL. + std::function ep_stopped; + + const char* product_desc; // friendly name of the device + const USBDesc* usb_desc; // device descriptor +}; + +/* definition of an Xbox usb device */ +struct XboxDeviceState { + USBPort* Port; // usb port struct of this device + int PortPath; // port index to which this device is attached to + USBDeviceClass* klass; // usb class struct of this device + + int Speed; // actual speed of the connected device + int SpeedMask; // supported speeds, not in info because it may be variable (hostdevs) + uint8_t Addr; // device function address + std::string ProductDesc; // the friendly name of this device + int Attached; // device is attached + + int32_t State; // current state of device + uint8_t SetupBuffer[8]; // setup packet buffer - 8 bytes (control transfers only) + uint8_t DataBuffer[4096]; // buffer where to write the data requested during usb requests + int32_t RemoteWakeup; // wakeup flag + int32_t SetupState; // result of a control transfer processing operation + int32_t SetupLength; // this field specifies the length of the data transferred during the second phase of the control transfer + int32_t SetupIndex; // index of the parameter in a setup token? + + USBEndpoint EP_ctl; // control endpoint + USBEndpoint EP_in[USB_MAX_ENDPOINTS]; // device endpoint (input direction) + USBEndpoint EP_out[USB_MAX_ENDPOINTS]; // device endpoint (output direction) + + QLIST_HEAD(, USBDescString) Strings; // strings of the string descriptors + const USBDesc* UsbDesc; // Overrides class usb_desc if not nullptr + const USBDescDevice* Device; // device descriptor part 1 + + int Configuration; // number of the selected configuration descriptor + int NumInterfaces; // number of available interface descriptors + int AltSetting[USB_MAX_INTERFACES]; // alternate setting numbers for the current interface + const USBDescConfig* Config; // configuration in use + const USBDescIface* Ifaces[USB_MAX_INTERFACES]; // interface in use +}; + +/* Structure used to hold information about an active USB packet */ +struct USBPacket { + int Pid; // Packet ID (used to identify the type of packet that is being processed) + uint32_t Id; // Paddr of the TD for this packet + USBEndpoint* Endpoint; // endpoint this packet is transferred to + IOVector IoVec; // used to perform vectored I/O + bool ShortNotOK; // the bufferRounding mode of the TD for this packet + bool IntReq; // whether or not to generate an interrupt for this packet (DelayInterrupt of the TD is zero) + int Status; // USB_RET_* status code + int ActualLength; // number of bytes actually written to DataBuffer + // Internal use by the USB layer + USBPacketState State; + QTAILQ_ENTRY(USBPacket) Queue; +}; + struct USBPortOps { std::function attach; std::function detach; @@ -460,6 +460,6 @@ struct USBPortOps { * the packet originated when a hub is involved. */ std::function complete; -}; - -#endif +}; + +#endif diff --git a/src/devices/usb/XidGamepad.cpp b/src/devices/usb/XidGamepad.cpp index 11597ba0c..d31e7e045 100644 --- a/src/devices/usb/XidGamepad.cpp +++ b/src/devices/usb/XidGamepad.cpp @@ -1,560 +1,560 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2018 ergo720 -// * -// * All rights reserved -// * -// ****************************************************************** - -// Acknowledgment: XQEMU xid emulation (GPLv2) -// https://xqemu.com/ - -/* -* QEMU USB XID Devices -* -* Copyright (c) 2013 espes -* Copyright (c) 2017 Jannik Vogel -* Copyright (c) 2018 Matt Borgerson -* -* This library 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 of the License, or (at your option) any later version. -* -* This library 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 library; if not, see . -*/ - - -#define LOG_PREFIX CXBXR_MODULE::XIDCTRL - - -#include // For PKINTERRUPT, etc. -#include -#include "XidGamepad.h" -#include "USBDevice.h" -#include "common\input\InputManager.h" -#include "common\input\SdlJoystick.h" -#include "OHCI.h" -#include "core\kernel\exports\EmuKrnl.h" -#include "Logging.h" - -#define USB_CLASS_XID 0x58 -#define USB_DT_XID 0x42 - -#define HID_GET_REPORT 0x01 -#define HID_SET_REPORT 0x09 -#define XID_GET_CAPABILITIES 0x01 - - -// To avoid including Xbox.h -extern USBDevice* g_USB0; - -XidGamepad* g_XidControllerObjArray[4]; - -#pragma pack(1) - -/* Class-specific xid descriptor */ -struct XIDDesc { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bcdXid; - uint8_t bType; - uint8_t bSubType; - uint8_t bMaxInputReportSize; - uint8_t bMaxOutputReportSize; - uint16_t wAlternateProductIds[4]; -}; - -/* Struct used by the Get_Report request -> button's state */ -struct XIDGamepadReport { - uint8_t bReportId; - uint8_t bLength; - uint16_t wButtons; // all non-analog buttons - uint8_t bAnalogButtons[8]; // X, Y, A, B, white, black, left/right trigger - int16_t sThumbLX; // analog stick, left X - int16_t sThumbLY; // analog stick, left Y - int16_t sThumbRX; // analog stick, right X - int16_t sThumbRY; // analog stick, right Y -}; - -/* Struct used by the Set_Report request -> vibration strenght */ -struct XIDGamepadOutputReport { - uint8_t report_id; // From XQEMU: FIXME: is this correct? - uint8_t length; - uint16_t left_actuator_strength; // strenght of left vibration motor - uint16_t right_actuator_strength; // strenght of right vibration motor -}; - -#pragma pack() - -struct USBXIDState { - XboxDeviceState dev; // gamepad device status - USBEndpoint* intr; // interrupt endpoint of the gamepad - - const XIDDesc* xid_desc; // xid-specific descriptor - - XIDGamepadReport in_state; // Get_Report struct - XIDGamepadReport in_state_capabilities; // Get_Capabilities struct (in) - XIDGamepadOutputReport out_state; // Set_Report struct - XIDGamepadOutputReport out_state_capabilities; // Get_Capabilities struct (out) -}; - -static const USBDescEndpoint desc_endp_xbox_gamepad[2] = { - { - USB_DIR_IN | 0x02, // bEndpointAddress; - USB_ENDPOINT_XFER_INT, // bmAttributes; - 0x20, // wMaxPacketSize; - 4, // bInterval; - 0, // bRefresh; - 0, // bSynchAddress - 0, // is_audio - nullptr // extra - }, - { - USB_DIR_OUT | 0x02, - USB_ENDPOINT_XFER_INT, - 0x20, - 4, - 0, - 0, - 0, - nullptr - } -}; - -static const USBDescIface desc_iface_xbox_gamepad = { - 0, // bInterfaceNumber; - 0, // bAlternateSetting; - 2, // bNumEndpoints; - USB_CLASS_XID, // bInterfaceClass; - 0x42, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0, // iInterface - 0, // ndesc - nullptr, // descs - desc_endp_xbox_gamepad -}; - -static const USBDescConfig desc_config_xbox_gamepad = { - 1, // bNumInterfaces - 1, // bConfigurationValue - 0, // iConfiguration - 0x80, // bmAttributes - 50, // bMaxPower - 1, // nif - &desc_iface_xbox_gamepad -}; - -static const USBDescDevice desc_device_xbox_gamepad = { - 0x0110, // bcdUSB - 0, // bDeviceClass - 0, // bDeviceSubClass - 0, // bDeviceProtocol - 0x40, // bMaxPacketSize0 - 1, // bNumConfigurations - &desc_config_xbox_gamepad -}; - -static const USBDesc desc_xbox_gamepad = { - { - 0x045E, // idVendor - 0x0202, // idProduct - 0x0100, // bcdDevice - STR_MANUFACTURER, // iManufacturer - STR_PRODUCT, // iProduct - STR_SERIALNUMBER // iSerialNumber - }, - &desc_device_xbox_gamepad -}; - -static const XIDDesc desc_xid_xbox_gamepad = { - 0x10, // bLength - USB_DT_XID, // bDescriptorType - 0x100, // bcdXid - 1, // bType - 1, // bSubType - 20, // bMaxInputReportSize - 6, // bMaxOutputReportSize - { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF } // wAlternateProductIds -}; - -int XidGamepad::Init(int port) -{ - if (port > 4 || port < 1) { return -1; } - - XboxDeviceState* dev = ClassInitFn(); - int rc = UsbXidClaimPort(dev, port); - if (rc != 0) { - m_UsbDev->m_HostController->m_FrameTimeMutex.unlock(); - return rc; - } - m_UsbDev->USB_EpInit(dev); - m_UsbDev->USB_DeviceInit(dev); - m_UsbDev->USB_DeviceAttach(dev); - - m_UsbDev->m_HostController->m_FrameTimeMutex.unlock(); - - return 0; -} - -XboxDeviceState* XidGamepad::ClassInitFn() -{ - m_pPeripheralFuncStruct = new USBDeviceClass(); - m_XidState = new USBXIDState(); - XboxDeviceState* dev = &m_XidState->dev; - - dev->ProductDesc = "Microsoft Xbox Controller"; - QLIST_INIT(&dev->Strings); - dev->klass = m_pPeripheralFuncStruct; - - { - using namespace std::placeholders; - - m_pPeripheralFuncStruct->init = std::bind(&XidGamepad::UsbXid_Initfn, this, _1); - m_pPeripheralFuncStruct->handle_reset = std::bind(&XidGamepad::UsbXid_HandleReset, this); - m_pPeripheralFuncStruct->handle_control = std::bind(&XidGamepad::UsbXid_HandleControl, this, _1, _2, _3, _4, _5, _6, _7); - m_pPeripheralFuncStruct->handle_data = std::bind(&XidGamepad::UsbXid_HandleData, this, _1, _2); - m_pPeripheralFuncStruct->handle_destroy = std::bind(&XidGamepad::UsbXid_HandleDestroy, this); - m_pPeripheralFuncStruct->handle_attach = std::bind(&XidGamepad::UsbXid_Attach, this, _1); - m_pPeripheralFuncStruct->product_desc = dev->ProductDesc.c_str(); - m_pPeripheralFuncStruct->usb_desc = &desc_xbox_gamepad; - } - - return dev; -} - -int XidGamepad::UsbXidClaimPort(XboxDeviceState* dev, int port) -{ - int i; - std::vector::iterator it; - - assert(dev->Port == nullptr); - - m_UsbDev = g_USB0; - it = m_UsbDev->m_FreePorts.end(); - i = 0; - - m_UsbDev->m_HostController->m_FrameTimeMutex.lock(); - - for (auto usb_port : m_UsbDev->m_FreePorts) { - if (usb_port->Path == (std::to_string(port) + ".2")) { - it = m_UsbDev->m_FreePorts.begin() + i; - break; - } - i++; - } - if (it == m_UsbDev->m_FreePorts.end()) { - EmuLog(LOG_LEVEL::WARNING, "Port requested %d.2 not found (in use?)", port); - return -1; - } - - m_Port = port; - dev->Port = *it; - (*it)->Dev = dev; - m_UsbDev->m_FreePorts.erase(it); - - return 0; -} - -void XidGamepad::UsbXidReleasePort(XboxDeviceState* dev) -{ - USBPort* port = dev->Port; - - assert(port != nullptr); - - port->Dev = nullptr; - dev->Port = nullptr; -} - -int XidGamepad::UsbXid_Initfn(XboxDeviceState* dev) -{ - m_UsbDev->USB_CreateSerial(dev, std::string("1")); - m_UsbDev->USBDesc_SetString(dev, STR_MANUFACTURER, std::string("Cxbx-Reloaded")); - m_UsbDev->USBDesc_SetString(dev, STR_PRODUCT, std::string("Microsoft Gamepad")); - m_UsbDev->USBDesc_Init(dev); - m_XidState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 2); - - m_XidState->in_state.bLength = sizeof(m_XidState->in_state); - m_XidState->in_state.bReportId = 0; - m_XidState->out_state.length = sizeof(m_XidState->out_state); - m_XidState->out_state.report_id = 0; - - std::memset(&m_XidState->in_state_capabilities, 0xFF, sizeof(m_XidState->in_state_capabilities)); - m_XidState->in_state_capabilities.bLength = sizeof(m_XidState->in_state_capabilities); - m_XidState->in_state_capabilities.bReportId = 0; - - std::memset(&m_XidState->out_state_capabilities, 0xFF, sizeof(m_XidState->out_state_capabilities)); - m_XidState->out_state_capabilities.length = sizeof(m_XidState->out_state_capabilities); - m_XidState->out_state_capabilities.report_id = 0; - - m_XidState->xid_desc = &desc_xid_xbox_gamepad; - - return 0; -} - -void XidGamepad::UsbXid_HandleDestroy() -{ - UsbXidReleasePort(&m_XidState->dev); - XpadCleanUp(); -} - -void XidGamepad::UsbXid_Attach(XboxDeviceState* dev) -{ - if ((dev->Port->SpeedMask & USB_SPEED_MASK_FULL)) { - dev->Speed = USB_SPEED_FULL; - } - else { - return; - } - m_UsbDev->USBDesc_SetDefaults(dev); -} - -void XidGamepad::UsbXid_HandleReset() -{ - EmuLog(LOG_LEVEL::DEBUG, "Reset event"); -} - -void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p, - int request, int value, int index, int length, uint8_t* data) -{ - int ret = m_UsbDev->USBDesc_HandleControl(dev, p, request, value, index, length, data); - if (ret >= 0) { - EmuLog(LOG_LEVEL::DEBUG, "Handled by USBDesc_HandleControl, ret is %d", ret); - return; - } - - switch (request) { - // HID-specific requests - case ClassInterfaceRequest | HID_GET_REPORT: { - // From the HID standard: "The Get_Report request allows the host to receive a report via the Control pipe. - // The wValue field specifies the Report Type in the high byte and the Report ID in the low byte. Set Report ID - // to 0 (zero) if Report IDs are not used. 01 = input, 02 = output, 03 = feature, 04-FF = reserved" - EmuLog(LOG_LEVEL::DEBUG, "GET_REPORT xpad request 0x%X", value); - // JayFoxRox's analysis: "This 0x0100 case is for input. - // This is the case where the Xbox wants to read input data from the controller. - // Confirmed with a real Duke controller : - // If the buffer provided by the Xbox is too small, the controller will cut the transfer when the buffer is full (actual_length is patched). - // If the buffer is too large the controller will STALL instead. - // If the buffer has the correct length the full input data is transferred." - if (value == 0x0100) { - if (length <= m_XidState->in_state.bLength) { -#if 0 // Reenable this when LLE USB actually works - SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port); - if (controller != nullptr) { - controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons, - &m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX, - &m_XidState->in_state.sThumbRY); - } - else { - // ergo720: this shouldn't really happen. If it does, it either means that m_Port is wrong or there's a bug - // in the InputDeviceManager - p->Status = USB_RET_STALL; - assert(0); - } -#endif - std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength); - p->ActualLength = length; - } - else { - p->Status = USB_RET_STALL; - } - } - else { - p->Status = USB_RET_STALL; - } - break; - } - - case ClassInterfaceOutRequest | HID_SET_REPORT: { - // From the HID standard: "The Set_Report request allows the host to send a report to the device, possibly - // setting the state of input, output, or feature controls. The meaning of the request fields for the Set_Report - // request is the same as for the Get_Report request, however the data direction is reversed and the Report - // Data is sent from host to device." - EmuLog(LOG_LEVEL::DEBUG, "SET_REPORT xpad request 0x%X", value); - // JayFoxRox's analysis: "The 0x0200 case below is for output. - // This is the case where the Xbox wants to write rumble data to the controller. - // To my knowledge : - // If the buffer provided by the Xbox is too small the transfer will STALL. - // If the buffer is too large the transfer will STALL. - // If the buffer has the correct length the full output data is transferred." - if (value == 0x0200) { - if (length == m_XidState->out_state.length) { - // Read length, then the entire packet - std::memcpy(&m_XidState->out_state, data, sizeof(m_XidState->out_state)); - /* FIXME: This should also be a STALL */ - assert(m_XidState->out_state.length == sizeof(m_XidState->out_state)); - - p->ActualLength = length; - } - else { - p->Status = USB_RET_STALL; - } - UpdateForceFeedback(); - } - else { - p->Status = USB_RET_STALL; - assert(0); - } - break; - } - - // XID-specific requests - case VendorInterfaceRequest | USB_REQ_GET_DESCRIPTOR: { - EmuLog(LOG_LEVEL::DEBUG, "GET_DESCRIPTOR xpad request 0x%x", value); - if (value == 0x4200) { - assert(m_XidState->xid_desc->bLength <= length); - std::memcpy(data, m_XidState->xid_desc, m_XidState->xid_desc->bLength); - p->ActualLength = m_XidState->xid_desc->bLength; - } - else { - p->Status = USB_RET_STALL; - assert(0); - } - break; - } - - case VendorInterfaceRequest | XID_GET_CAPABILITIES: { - EmuLog(LOG_LEVEL::DEBUG, "XID_GET_CAPABILITIES xpad request 0x%x", value); - if (value == 0x0100) { - if (length > m_XidState->in_state_capabilities.bLength) { - length = m_XidState->in_state_capabilities.bLength; - } - std::memcpy(data, &m_XidState->in_state_capabilities, length); - p->ActualLength = length; - } - else if (value == 0x0200) { - if (length > m_XidState->out_state_capabilities.length) { - length = m_XidState->out_state_capabilities.length; - } - std::memcpy(data, &m_XidState->out_state_capabilities, length); - p->ActualLength = length; - } - else { - p->Status = USB_RET_STALL; - assert(0); - } - break; - } - - case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8) | USB_REQ_GET_DESCRIPTOR: { - /* FIXME: ! */ - EmuLog(LOG_LEVEL::DEBUG, "Unknown xpad request 0x%X: value = 0x%X", request, value); - std::memset(data, 0x00, length); - //FIXME: Intended for the hub: usbd_get_hub_descriptor, UT_READ_CLASS?! - p->Status = USB_RET_STALL; - //assert(false); - break; - } - - case ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT) << 8) | USB_REQ_CLEAR_FEATURE: { - /* FIXME: ! */ - EmuLog(LOG_LEVEL::DEBUG, "Unknown xpad request 0x%X: value = 0x%X", request, value); - std::memset(data, 0x00, length); - p->Status = USB_RET_STALL; - break; - } - - default: - EmuLog(LOG_LEVEL::DEBUG, "USB stalled on request 0x%X value 0x%X", request, value); - p->Status = USB_RET_STALL; - assert(0); - break; - } -} - -void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p) -{ - switch (p->Pid) { - case USB_TOKEN_IN: { - if (p->Endpoint->Num == 2) { -#if 0 // Reenable this when LLE USB actually works - SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port); - if (controller != nullptr) { - bool ret; - ret = controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons, - &m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX, - &m_XidState->in_state.sThumbRY); - if (ret) { - m_UsbDev->USB_PacketCopy(p, &m_XidState->in_state, m_XidState->in_state.bLength); - } - else { -#endif - p->Status = USB_RET_NAK; - } -#if 0 // Reenable this when LLE USB actually works - } - else { - p->Status = USB_RET_STALL; - assert(0); - } - } - else { - assert(0); - } -#endif - break; - } - - case USB_TOKEN_OUT: { - if (p->Endpoint->Num == 2) { - m_UsbDev->USB_PacketCopy(p, &m_XidState->out_state, m_XidState->out_state.length); - UpdateForceFeedback(); - } - else { - assert(0); - } - break; - } - - default: - p->Status = USB_RET_STALL; - assert(0); - break; - } -} - -void XidGamepad::XpadCleanUp() -{ - delete m_pPeripheralFuncStruct; - delete m_XidState; - m_pPeripheralFuncStruct = nullptr; - m_XidState = nullptr; -} - -void XidGamepad::UpdateForceFeedback() -{ - // JayFoxRox's remarks: "Xbox -> XID packets were not tested - // The handling of output packets / force feedback was not checked." - // For the above reason we don't implement vibration support for now since the current - // implementation is untested and could potentially contain errors - - /* FIXME: Check actuator endianess */ - EmuLog(LOG_LEVEL::DEBUG, "Set rumble power to left: 0x%X and right: 0x%X", - m_XidState->out_state.left_actuator_strength, - m_XidState->out_state.right_actuator_strength); -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2018 ergo720 +// * +// * All rights reserved +// * +// ****************************************************************** + +// Acknowledgment: XQEMU xid emulation (GPLv2) +// https://xqemu.com/ + +/* +* QEMU USB XID Devices +* +* Copyright (c) 2013 espes +* Copyright (c) 2017 Jannik Vogel +* Copyright (c) 2018 Matt Borgerson +* +* This library 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 of the License, or (at your option) any later version. +* +* This library 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 library; if not, see . +*/ + + +#define LOG_PREFIX CXBXR_MODULE::XIDCTRL + + +#include // For PKINTERRUPT, etc. +#include +#include "XidGamepad.h" +#include "USBDevice.h" +#include "common\input\InputManager.h" +#include "common\input\SdlJoystick.h" +#include "OHCI.h" +#include "core\kernel\exports\EmuKrnl.h" +#include "Logging.h" + +#define USB_CLASS_XID 0x58 +#define USB_DT_XID 0x42 + +#define HID_GET_REPORT 0x01 +#define HID_SET_REPORT 0x09 +#define XID_GET_CAPABILITIES 0x01 + + +// To avoid including Xbox.h +extern USBDevice* g_USB0; + +XidGamepad* g_XidControllerObjArray[4]; + +#pragma pack(1) + +/* Class-specific xid descriptor */ +struct XIDDesc { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdXid; + uint8_t bType; + uint8_t bSubType; + uint8_t bMaxInputReportSize; + uint8_t bMaxOutputReportSize; + uint16_t wAlternateProductIds[4]; +}; + +/* Struct used by the Get_Report request -> button's state */ +struct XIDGamepadReport { + uint8_t bReportId; + uint8_t bLength; + uint16_t wButtons; // all non-analog buttons + uint8_t bAnalogButtons[8]; // X, Y, A, B, white, black, left/right trigger + int16_t sThumbLX; // analog stick, left X + int16_t sThumbLY; // analog stick, left Y + int16_t sThumbRX; // analog stick, right X + int16_t sThumbRY; // analog stick, right Y +}; + +/* Struct used by the Set_Report request -> vibration strenght */ +struct XIDGamepadOutputReport { + uint8_t report_id; // From XQEMU: FIXME: is this correct? + uint8_t length; + uint16_t left_actuator_strength; // strenght of left vibration motor + uint16_t right_actuator_strength; // strenght of right vibration motor +}; + +#pragma pack() + +struct USBXIDState { + XboxDeviceState dev; // gamepad device status + USBEndpoint* intr; // interrupt endpoint of the gamepad + + const XIDDesc* xid_desc; // xid-specific descriptor + + XIDGamepadReport in_state; // Get_Report struct + XIDGamepadReport in_state_capabilities; // Get_Capabilities struct (in) + XIDGamepadOutputReport out_state; // Set_Report struct + XIDGamepadOutputReport out_state_capabilities; // Get_Capabilities struct (out) +}; + +static const USBDescEndpoint desc_endp_xbox_gamepad[2] = { + { + USB_DIR_IN | 0x02, // bEndpointAddress; + USB_ENDPOINT_XFER_INT, // bmAttributes; + 0x20, // wMaxPacketSize; + 4, // bInterval; + 0, // bRefresh; + 0, // bSynchAddress + 0, // is_audio + nullptr // extra + }, + { + USB_DIR_OUT | 0x02, + USB_ENDPOINT_XFER_INT, + 0x20, + 4, + 0, + 0, + 0, + nullptr + } +}; + +static const USBDescIface desc_iface_xbox_gamepad = { + 0, // bInterfaceNumber; + 0, // bAlternateSetting; + 2, // bNumEndpoints; + USB_CLASS_XID, // bInterfaceClass; + 0x42, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + 0, // ndesc + nullptr, // descs + desc_endp_xbox_gamepad +}; + +static const USBDescConfig desc_config_xbox_gamepad = { + 1, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + 1, // nif + &desc_iface_xbox_gamepad +}; + +static const USBDescDevice desc_device_xbox_gamepad = { + 0x0110, // bcdUSB + 0, // bDeviceClass + 0, // bDeviceSubClass + 0, // bDeviceProtocol + 0x40, // bMaxPacketSize0 + 1, // bNumConfigurations + &desc_config_xbox_gamepad +}; + +static const USBDesc desc_xbox_gamepad = { + { + 0x045E, // idVendor + 0x0202, // idProduct + 0x0100, // bcdDevice + STR_MANUFACTURER, // iManufacturer + STR_PRODUCT, // iProduct + STR_SERIALNUMBER // iSerialNumber + }, + &desc_device_xbox_gamepad +}; + +static const XIDDesc desc_xid_xbox_gamepad = { + 0x10, // bLength + USB_DT_XID, // bDescriptorType + 0x100, // bcdXid + 1, // bType + 1, // bSubType + 20, // bMaxInputReportSize + 6, // bMaxOutputReportSize + { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF } // wAlternateProductIds +}; + +int XidGamepad::Init(int port) +{ + if (port > 4 || port < 1) { return -1; } + + XboxDeviceState* dev = ClassInitFn(); + int rc = UsbXidClaimPort(dev, port); + if (rc != 0) { + m_UsbDev->m_HostController->m_FrameTimeMutex.unlock(); + return rc; + } + m_UsbDev->USB_EpInit(dev); + m_UsbDev->USB_DeviceInit(dev); + m_UsbDev->USB_DeviceAttach(dev); + + m_UsbDev->m_HostController->m_FrameTimeMutex.unlock(); + + return 0; +} + +XboxDeviceState* XidGamepad::ClassInitFn() +{ + m_pPeripheralFuncStruct = new USBDeviceClass(); + m_XidState = new USBXIDState(); + XboxDeviceState* dev = &m_XidState->dev; + + dev->ProductDesc = "Microsoft Xbox Controller"; + QLIST_INIT(&dev->Strings); + dev->klass = m_pPeripheralFuncStruct; + + { + using namespace std::placeholders; + + m_pPeripheralFuncStruct->init = std::bind(&XidGamepad::UsbXid_Initfn, this, _1); + m_pPeripheralFuncStruct->handle_reset = std::bind(&XidGamepad::UsbXid_HandleReset, this); + m_pPeripheralFuncStruct->handle_control = std::bind(&XidGamepad::UsbXid_HandleControl, this, _1, _2, _3, _4, _5, _6, _7); + m_pPeripheralFuncStruct->handle_data = std::bind(&XidGamepad::UsbXid_HandleData, this, _1, _2); + m_pPeripheralFuncStruct->handle_destroy = std::bind(&XidGamepad::UsbXid_HandleDestroy, this); + m_pPeripheralFuncStruct->handle_attach = std::bind(&XidGamepad::UsbXid_Attach, this, _1); + m_pPeripheralFuncStruct->product_desc = dev->ProductDesc.c_str(); + m_pPeripheralFuncStruct->usb_desc = &desc_xbox_gamepad; + } + + return dev; +} + +int XidGamepad::UsbXidClaimPort(XboxDeviceState* dev, int port) +{ + int i; + std::vector::iterator it; + + assert(dev->Port == nullptr); + + m_UsbDev = g_USB0; + it = m_UsbDev->m_FreePorts.end(); + i = 0; + + m_UsbDev->m_HostController->m_FrameTimeMutex.lock(); + + for (auto usb_port : m_UsbDev->m_FreePorts) { + if (usb_port->Path == (std::to_string(port) + ".2")) { + it = m_UsbDev->m_FreePorts.begin() + i; + break; + } + i++; + } + if (it == m_UsbDev->m_FreePorts.end()) { + EmuLog(LOG_LEVEL::WARNING, "Port requested %d.2 not found (in use?)", port); + return -1; + } + + m_Port = port; + dev->Port = *it; + (*it)->Dev = dev; + m_UsbDev->m_FreePorts.erase(it); + + return 0; +} + +void XidGamepad::UsbXidReleasePort(XboxDeviceState* dev) +{ + USBPort* port = dev->Port; + + assert(port != nullptr); + + port->Dev = nullptr; + dev->Port = nullptr; +} + +int XidGamepad::UsbXid_Initfn(XboxDeviceState* dev) +{ + m_UsbDev->USB_CreateSerial(dev, std::string("1")); + m_UsbDev->USBDesc_SetString(dev, STR_MANUFACTURER, std::string("Cxbx-Reloaded")); + m_UsbDev->USBDesc_SetString(dev, STR_PRODUCT, std::string("Microsoft Gamepad")); + m_UsbDev->USBDesc_Init(dev); + m_XidState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 2); + + m_XidState->in_state.bLength = sizeof(m_XidState->in_state); + m_XidState->in_state.bReportId = 0; + m_XidState->out_state.length = sizeof(m_XidState->out_state); + m_XidState->out_state.report_id = 0; + + std::memset(&m_XidState->in_state_capabilities, 0xFF, sizeof(m_XidState->in_state_capabilities)); + m_XidState->in_state_capabilities.bLength = sizeof(m_XidState->in_state_capabilities); + m_XidState->in_state_capabilities.bReportId = 0; + + std::memset(&m_XidState->out_state_capabilities, 0xFF, sizeof(m_XidState->out_state_capabilities)); + m_XidState->out_state_capabilities.length = sizeof(m_XidState->out_state_capabilities); + m_XidState->out_state_capabilities.report_id = 0; + + m_XidState->xid_desc = &desc_xid_xbox_gamepad; + + return 0; +} + +void XidGamepad::UsbXid_HandleDestroy() +{ + UsbXidReleasePort(&m_XidState->dev); + XpadCleanUp(); +} + +void XidGamepad::UsbXid_Attach(XboxDeviceState* dev) +{ + if ((dev->Port->SpeedMask & USB_SPEED_MASK_FULL)) { + dev->Speed = USB_SPEED_FULL; + } + else { + return; + } + m_UsbDev->USBDesc_SetDefaults(dev); +} + +void XidGamepad::UsbXid_HandleReset() +{ + EmuLog(LOG_LEVEL::DEBUG, "Reset event"); +} + +void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p, + int request, int value, int index, int length, uint8_t* data) +{ + int ret = m_UsbDev->USBDesc_HandleControl(dev, p, request, value, index, length, data); + if (ret >= 0) { + EmuLog(LOG_LEVEL::DEBUG, "Handled by USBDesc_HandleControl, ret is %d", ret); + return; + } + + switch (request) { + // HID-specific requests + case ClassInterfaceRequest | HID_GET_REPORT: { + // From the HID standard: "The Get_Report request allows the host to receive a report via the Control pipe. + // The wValue field specifies the Report Type in the high byte and the Report ID in the low byte. Set Report ID + // to 0 (zero) if Report IDs are not used. 01 = input, 02 = output, 03 = feature, 04-FF = reserved" + EmuLog(LOG_LEVEL::DEBUG, "GET_REPORT xpad request 0x%X", value); + // JayFoxRox's analysis: "This 0x0100 case is for input. + // This is the case where the Xbox wants to read input data from the controller. + // Confirmed with a real Duke controller : + // If the buffer provided by the Xbox is too small, the controller will cut the transfer when the buffer is full (actual_length is patched). + // If the buffer is too large the controller will STALL instead. + // If the buffer has the correct length the full input data is transferred." + if (value == 0x0100) { + if (length <= m_XidState->in_state.bLength) { +#if 0 // Reenable this when LLE USB actually works + SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port); + if (controller != nullptr) { + controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons, + &m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX, + &m_XidState->in_state.sThumbRY); + } + else { + // ergo720: this shouldn't really happen. If it does, it either means that m_Port is wrong or there's a bug + // in the InputDeviceManager + p->Status = USB_RET_STALL; + assert(0); + } +#endif + std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength); + p->ActualLength = length; + } + else { + p->Status = USB_RET_STALL; + } + } + else { + p->Status = USB_RET_STALL; + } + break; + } + + case ClassInterfaceOutRequest | HID_SET_REPORT: { + // From the HID standard: "The Set_Report request allows the host to send a report to the device, possibly + // setting the state of input, output, or feature controls. The meaning of the request fields for the Set_Report + // request is the same as for the Get_Report request, however the data direction is reversed and the Report + // Data is sent from host to device." + EmuLog(LOG_LEVEL::DEBUG, "SET_REPORT xpad request 0x%X", value); + // JayFoxRox's analysis: "The 0x0200 case below is for output. + // This is the case where the Xbox wants to write rumble data to the controller. + // To my knowledge : + // If the buffer provided by the Xbox is too small the transfer will STALL. + // If the buffer is too large the transfer will STALL. + // If the buffer has the correct length the full output data is transferred." + if (value == 0x0200) { + if (length == m_XidState->out_state.length) { + // Read length, then the entire packet + std::memcpy(&m_XidState->out_state, data, sizeof(m_XidState->out_state)); + /* FIXME: This should also be a STALL */ + assert(m_XidState->out_state.length == sizeof(m_XidState->out_state)); + + p->ActualLength = length; + } + else { + p->Status = USB_RET_STALL; + } + UpdateForceFeedback(); + } + else { + p->Status = USB_RET_STALL; + assert(0); + } + break; + } + + // XID-specific requests + case VendorInterfaceRequest | USB_REQ_GET_DESCRIPTOR: { + EmuLog(LOG_LEVEL::DEBUG, "GET_DESCRIPTOR xpad request 0x%x", value); + if (value == 0x4200) { + assert(m_XidState->xid_desc->bLength <= length); + std::memcpy(data, m_XidState->xid_desc, m_XidState->xid_desc->bLength); + p->ActualLength = m_XidState->xid_desc->bLength; + } + else { + p->Status = USB_RET_STALL; + assert(0); + } + break; + } + + case VendorInterfaceRequest | XID_GET_CAPABILITIES: { + EmuLog(LOG_LEVEL::DEBUG, "XID_GET_CAPABILITIES xpad request 0x%x", value); + if (value == 0x0100) { + if (length > m_XidState->in_state_capabilities.bLength) { + length = m_XidState->in_state_capabilities.bLength; + } + std::memcpy(data, &m_XidState->in_state_capabilities, length); + p->ActualLength = length; + } + else if (value == 0x0200) { + if (length > m_XidState->out_state_capabilities.length) { + length = m_XidState->out_state_capabilities.length; + } + std::memcpy(data, &m_XidState->out_state_capabilities, length); + p->ActualLength = length; + } + else { + p->Status = USB_RET_STALL; + assert(0); + } + break; + } + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8) | USB_REQ_GET_DESCRIPTOR: { + /* FIXME: ! */ + EmuLog(LOG_LEVEL::DEBUG, "Unknown xpad request 0x%X: value = 0x%X", request, value); + std::memset(data, 0x00, length); + //FIXME: Intended for the hub: usbd_get_hub_descriptor, UT_READ_CLASS?! + p->Status = USB_RET_STALL; + //assert(false); + break; + } + + case ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT) << 8) | USB_REQ_CLEAR_FEATURE: { + /* FIXME: ! */ + EmuLog(LOG_LEVEL::DEBUG, "Unknown xpad request 0x%X: value = 0x%X", request, value); + std::memset(data, 0x00, length); + p->Status = USB_RET_STALL; + break; + } + + default: + EmuLog(LOG_LEVEL::DEBUG, "USB stalled on request 0x%X value 0x%X", request, value); + p->Status = USB_RET_STALL; + assert(0); + break; + } +} + +void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p) +{ + switch (p->Pid) { + case USB_TOKEN_IN: { + if (p->Endpoint->Num == 2) { +#if 0 // Reenable this when LLE USB actually works + SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port); + if (controller != nullptr) { + bool ret; + ret = controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons, + &m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX, + &m_XidState->in_state.sThumbRY); + if (ret) { + m_UsbDev->USB_PacketCopy(p, &m_XidState->in_state, m_XidState->in_state.bLength); + } + else { +#endif + p->Status = USB_RET_NAK; + } +#if 0 // Reenable this when LLE USB actually works + } + else { + p->Status = USB_RET_STALL; + assert(0); + } + } + else { + assert(0); + } +#endif + break; + } + + case USB_TOKEN_OUT: { + if (p->Endpoint->Num == 2) { + m_UsbDev->USB_PacketCopy(p, &m_XidState->out_state, m_XidState->out_state.length); + UpdateForceFeedback(); + } + else { + assert(0); + } + break; + } + + default: + p->Status = USB_RET_STALL; + assert(0); + break; + } +} + +void XidGamepad::XpadCleanUp() +{ + delete m_pPeripheralFuncStruct; + delete m_XidState; + m_pPeripheralFuncStruct = nullptr; + m_XidState = nullptr; +} + +void XidGamepad::UpdateForceFeedback() +{ + // JayFoxRox's remarks: "Xbox -> XID packets were not tested + // The handling of output packets / force feedback was not checked." + // For the above reason we don't implement vibration support for now since the current + // implementation is untested and could potentially contain errors + + /* FIXME: Check actuator endianess */ + EmuLog(LOG_LEVEL::DEBUG, "Set rumble power to left: 0x%X and right: 0x%X", + m_XidState->out_state.left_actuator_strength, + m_XidState->out_state.right_actuator_strength); +} diff --git a/src/devices/usb/XidGamepad.h b/src/devices/usb/XidGamepad.h index ea8f5750d..8d1adb12e 100644 --- a/src/devices/usb/XidGamepad.h +++ b/src/devices/usb/XidGamepad.h @@ -23,19 +23,19 @@ // * // * All rights reserved // * -// ****************************************************************** - -#ifndef XIDGAMEPAD_H_ -#define XIDGAMEPAD_H_ - -#include "Hub.h" - - -struct USBXIDState; // forward declare - +// ****************************************************************** + +#ifndef XIDGAMEPAD_H_ +#define XIDGAMEPAD_H_ + +#include "Hub.h" + + +struct USBXIDState; // forward declare + /* Class which implements an xbox gamepad */ class XidGamepad -{ +{ public: // initialize this peripheral int Init(int port); @@ -44,13 +44,13 @@ class XidGamepad private: - // usb device this gamepad is attached to through the hub - USBDevice* m_UsbDev = nullptr; - // gamepad state - USBXIDState* m_XidState = nullptr; - // gamepad class functions - USBDeviceClass* m_pPeripheralFuncStruct = nullptr; - // xbox port this gamepad is attached to + // usb device this gamepad is attached to through the hub + USBDevice* m_UsbDev = nullptr; + // gamepad state + USBXIDState* m_XidState = nullptr; + // gamepad class functions + USBDeviceClass* m_pPeripheralFuncStruct = nullptr; + // xbox port this gamepad is attached to int m_Port = 0; // initialize various member variables/functions @@ -59,19 +59,19 @@ class XidGamepad int UsbXidClaimPort(XboxDeviceState* dev, int port); // free the usb port used by this gamepad void UsbXidReleasePort(XboxDeviceState* dev); - // see USBDeviceClass for comments about these functions - int UsbXid_Initfn(XboxDeviceState* dev); + // see USBDeviceClass for comments about these functions + int UsbXid_Initfn(XboxDeviceState* dev); void UsbXid_HandleDestroy(); - void UsbXid_Attach(XboxDeviceState* dev); - void UsbXid_HandleReset(); - void UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p, - int request, int value, int index, int length, uint8_t* data); - void UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p); - // this should update the vibration strenght of the real controller this gamepad represents. - // It doesn't do anything at the moment + void UsbXid_Attach(XboxDeviceState* dev); + void UsbXid_HandleReset(); + void UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p, + int request, int value, int index, int length, uint8_t* data); + void UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p); + // this should update the vibration strenght of the real controller this gamepad represents. + // It doesn't do anything at the moment void UpdateForceFeedback(); -}; - -extern XidGamepad* g_XidControllerObjArray[4]; - -#endif +}; + +extern XidGamepad* g_XidControllerObjArray[4]; + +#endif diff --git a/src/devices/video/EmuNV2A_PCRTC.cpp b/src/devices/video/EmuNV2A_PCRTC.cpp index 67c986976..ae2754432 100644 --- a/src/devices/video/EmuNV2A_PCRTC.cpp +++ b/src/devices/video/EmuNV2A_PCRTC.cpp @@ -48,28 +48,28 @@ DEVICE_READ32(PCRTC) break; case NV_PCRTC_RASTER: { // Test case: Alter Echo - // Hack: Alternate between 0, mid-frame, and end-of-frame, this is enough to satisfy any title - // that waits for VBlank using D3DDevice_GetRasterStatus, but not harm performance as much as + // Hack: Alternate between 0, mid-frame, and end-of-frame, this is enough to satisfy any title + // that waits for VBlank using D3DDevice_GetRasterStatus, but not harm performance as much as // the previous implementation - - static int stage = 0; - - switch (stage) { - case 0: - result = 0; - break; - case 1: - result = NV2ADevice::GetFrameHeight(d) / 2; - break; - case 2: - result = NV2ADevice::GetFrameHeight(d) + 1; - break; - } - - stage++; - - if (stage > 2) { - stage = 0; + + static int stage = 0; + + switch (stage) { + case 0: + result = 0; + break; + case 1: + result = NV2ADevice::GetFrameHeight(d) / 2; + break; + case 2: + result = NV2ADevice::GetFrameHeight(d) + 1; + break; + } + + stage++; + + if (stage > 2) { + stage = 0; } } break; default: diff --git a/src/devices/video/EmuNV2A_PFIFO.cpp b/src/devices/video/EmuNV2A_PFIFO.cpp index aa09e17c4..1fb2c2a74 100644 --- a/src/devices/video/EmuNV2A_PFIFO.cpp +++ b/src/devices/video/EmuNV2A_PFIFO.cpp @@ -1,519 +1,519 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * This file is heavily based on code from XQEMU -// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pfifo.c -// * Copyright (c) 2012 espes -// * Copyright (c) 2015 Jannik Vogel -// * Copyright (c) 2018 Matt Borgerson -// * -// * Contributions for Cxbx-Reloaded -// * Copyright (c) 2017-2018 Luke Usher -// * Copyright (c) 2018 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -typedef struct RAMHTEntry { - uint32_t handle; - xbaddr instance; - enum FIFOEngine engine; - unsigned int channel_id : 5; - bool valid; -} RAMHTEntry; - -static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle); // forward declaration - -/* PFIFO - MMIO and DMA FIFO submission to PGRAPH and VPE */ -DEVICE_READ32(PFIFO) -{ - qemu_mutex_lock(&d->pfifo.pfifo_lock); - - DEVICE_READ32_SWITCH() { - case NV_PFIFO_RAMHT: - result = 0x03000100; // = NV_PFIFO_RAMHT_SIZE_4K | NV_PFIFO_RAMHT_BASE_ADDRESS(NumberOfPaddingBytes >> 12) | NV_PFIFO_RAMHT_SEARCH_128 - break; - case NV_PFIFO_RAMFC: - result = 0x00890110; // = ? | NV_PFIFO_RAMFC_SIZE_2K | ? - break; - case NV_PFIFO_INTR_0: - result = d->pfifo.pending_interrupts; - break; - case NV_PFIFO_INTR_EN_0: - result = d->pfifo.enabled_interrupts; - break; - case NV_PFIFO_RUNOUT_STATUS: - result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */ - break; - default: - DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO); - break; - } - - qemu_mutex_unlock(&d->pfifo.pfifo_lock); - - DEVICE_READ32_END(PFIFO); -} - -DEVICE_WRITE32(PFIFO) -{ - qemu_mutex_lock(&d->pfifo.pfifo_lock); - - switch(addr) { - case NV_PFIFO_INTR_0: - d->pfifo.pending_interrupts &= ~value; - update_irq(d); - break; - case NV_PFIFO_INTR_EN_0: - d->pfifo.enabled_interrupts = value; - update_irq(d); - break; - default: - DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO); - break; - } - - qemu_cond_broadcast(&d->pfifo.pusher_cond); - qemu_cond_broadcast(&d->pfifo.puller_cond); - - qemu_mutex_unlock(&d->pfifo.pfifo_lock); - - DEVICE_WRITE32_END(PFIFO); -} - -static void pfifo_run_puller(NV2AState *d) -{ - uint32_t *pull0 = &d->pfifo.regs[NV_PFIFO_CACHE1_PULL0]; - uint32_t *pull1 = &d->pfifo.regs[NV_PFIFO_CACHE1_PULL1]; - uint32_t *engine_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_ENGINE]; - - uint32_t *status = &d->pfifo.regs[NV_PFIFO_CACHE1_STATUS]; - uint32_t *get_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_GET]; - uint32_t *put_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_PUT]; - - // TODO - // CacheEntry working_cache[NV2A_CACHE1_SIZE]; - // int working_cache_size = 0; - // pull everything into our own queue - - // TODO think more about locking - - while (true) { - if (!GET_MASK(*pull0, NV_PFIFO_CACHE1_PULL0_ACCESS)) return; - - /* empty cache1 */ - if (*status & NV_PFIFO_CACHE1_STATUS_LOW_MARK) break; - - uint32_t get = *get_reg; - uint32_t put = *put_reg; - - assert(get < 128*4 && (get % 4) == 0); - uint32_t method_entry = d->pfifo.regs[NV_PFIFO_CACHE1_METHOD + get*2]; - uint32_t parameter = d->pfifo.regs[NV_PFIFO_CACHE1_DATA + get*2]; - - uint32_t new_get = (get+4) & 0x1fc; - *get_reg = new_get; - - if (new_get == put) { - // set low mark - *status |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; - } - if (*status & NV_PFIFO_CACHE1_STATUS_HIGH_MARK) { - // unset high mark - *status &= ~NV_PFIFO_CACHE1_STATUS_HIGH_MARK; - // signal pusher - qemu_cond_signal(&d->pfifo.pusher_cond); - } - - - uint32_t method = method_entry & 0x1FFC; - uint32_t subchannel = GET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_SUBCHANNEL); - - // NV2A_DPRINTF("pull %d 0x%08X 0x%08X - subch %d\n", get/4, method_entry, parameter, subchannel); - - if (method == 0) { - RAMHTEntry entry = ramht_lookup(d, parameter); - assert(entry.valid); - - // assert(entry.channel_id == state->channel_id); - - assert(entry.engine == ENGINE_GRAPHICS); - - - /* the engine is bound to the subchannel */ - assert(subchannel < 8); - SET_MASK(*engine_reg, 3 << (4*subchannel), entry.engine); - SET_MASK(*pull1, NV_PFIFO_CACHE1_PULL1_ENGINE, entry.engine); - // NV2A_DPRINTF("engine_reg1 %d 0x%08X\n", subchannel, *engine_reg); - - - // TODO: this is fucked - qemu_mutex_lock(&d->pgraph.pgraph_lock); - //make pgraph busy - qemu_mutex_unlock(&d->pfifo.pfifo_lock); - - pgraph_switch_context(d, entry.channel_id); - pgraph_wait_fifo_access(d); - pgraph_handle_method(d, subchannel, 0, entry.instance); - - // make pgraph not busy - qemu_mutex_unlock(&d->pgraph.pgraph_lock); - qemu_mutex_lock(&d->pfifo.pfifo_lock); - - } else if (method >= 0x100) { - // method passed to engine - - /* methods that take objects. - * TODO: Check this range is correct for the nv2a */ - if (method >= 0x180 && method < 0x200) { - //qemu_mutex_lock_iothread(); - RAMHTEntry entry = ramht_lookup(d, parameter); - assert(entry.valid); - // assert(entry.channel_id == state->channel_id); - parameter = entry.instance; - //qemu_mutex_unlock_iothread(); - } - - enum FIFOEngine engine = (enum FIFOEngine)GET_MASK(*engine_reg, 3 << (4*subchannel)); - // NV2A_DPRINTF("engine_reg2 %d 0x%08X\n", subchannel, *engine_reg); - assert(engine == ENGINE_GRAPHICS); - SET_MASK(*pull1, NV_PFIFO_CACHE1_PULL1_ENGINE, engine); - - // TODO: this is fucked - qemu_mutex_lock(&d->pgraph.pgraph_lock); - //make pgraph busy - qemu_mutex_unlock(&d->pfifo.pfifo_lock); - - pgraph_wait_fifo_access(d); - pgraph_handle_method(d, subchannel, method, parameter); - - // make pgraph not busy - qemu_mutex_unlock(&d->pgraph.pgraph_lock); - qemu_mutex_lock(&d->pfifo.pfifo_lock); - } else { - assert(false); - } - - } -} - -int pfifo_puller_thread(NV2AState *d) -{ - SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); - CxbxSetThreadName("Cxbx NV2A FIFO puller"); - - glo_set_current(d->pgraph.gl_context); - - qemu_mutex_lock(&d->pfifo.pfifo_lock); - while (true) { - pfifo_run_puller(d); - qemu_cond_wait(&d->pfifo.puller_cond, &d->pfifo.pfifo_lock); - - if (d->exiting) { - break; - } - } - qemu_mutex_unlock(&d->pfifo.pfifo_lock); - - glo_set_current(NULL); // Cxbx addition - - return NULL; -} - -static void pfifo_run_pusher(NV2AState *d) -{ - uint32_t *push0 = &d->pfifo.regs[NV_PFIFO_CACHE1_PUSH0]; - uint32_t *push1 = &d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1]; - uint32_t *dma_subroutine = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_SUBROUTINE]; - uint32_t *dma_state = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_STATE]; - uint32_t *dma_push = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUSH]; - uint32_t *dma_get = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET]; - uint32_t *dma_put = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUT]; - uint32_t *dma_dcount = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_DCOUNT]; - - uint32_t *status = &d->pfifo.regs[NV_PFIFO_CACHE1_STATUS]; - uint32_t *get_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_GET]; - uint32_t *put_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_PUT]; - - if (!GET_MASK(*push0, NV_PFIFO_CACHE1_PUSH0_ACCESS)) return; - if (!GET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS)) return; - - /* suspended */ - if (GET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) return; - - // TODO: should we become busy here?? - // NV_PFIFO_CACHE1_DMA_PUSH_STATE _BUSY - - unsigned int channel_id = GET_MASK(*push1, - NV_PFIFO_CACHE1_PUSH1_CHID); - - - /* Channel running DMA */ - uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE]; - assert(channel_modes & (1 << channel_id)); - - assert(GET_MASK(*push1, NV_PFIFO_CACHE1_PUSH1_MODE) - == NV_PFIFO_CACHE1_PUSH1_MODE_DMA); - - /* We're running so there should be no pending errors... */ - assert(GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR) - == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE); - - hwaddr dma_instance = - GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_DMA_INSTANCE], - NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; // TODO : Use NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MOVE? - - hwaddr dma_len; - uint8_t *dma = (uint8_t*)nv_dma_map(d, dma_instance, &dma_len); - - /* based on the convenient pseudocode in envytools */ - while (true) { - uint32_t dma_get_v = *dma_get; - uint32_t dma_put_v = *dma_put; - if (dma_get_v == dma_put_v) break; - if (dma_get_v >= dma_len) { - assert(false); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, - NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION); - break; - } - - uint32_t word = ldl_le_p((uint32_t*)(dma + dma_get_v)); - dma_get_v += 4; - - uint32_t method_type = - GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); - uint32_t method_subchannel = - GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); - uint32_t method = - GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; - uint32_t method_count = - GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); - - uint32_t subroutine_state = - GET_MASK(*dma_subroutine, NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); - - if (method_count) { - /* full */ - if (*status & NV_PFIFO_CACHE1_STATUS_HIGH_MARK) return; - - - /* data word of methods command */ - d->pfifo.regs[NV_PFIFO_CACHE1_DMA_DATA_SHADOW] = word; - - uint32_t put = *put_reg; - uint32_t get = *get_reg; - - assert((method & 3) == 0); - uint32_t method_entry = 0; - SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_ADDRESS, method >> 2); - SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_TYPE, method_type); - SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_SUBCHANNEL, method_subchannel); - - // NV2A_DPRINTF("push %d 0x%08X 0x%08X - subch %d\n", put/4, method_entry, word, method_subchannel); - - assert(put < 128*4 && (put%4) == 0); - d->pfifo.regs[NV_PFIFO_CACHE1_METHOD + put*2] = method_entry; - d->pfifo.regs[NV_PFIFO_CACHE1_DATA + put*2] = word; - - uint32_t new_put = (put+4) & 0x1fc; - *put_reg = new_put; - if (new_put == get) { - // set high mark - *status |= NV_PFIFO_CACHE1_STATUS_HIGH_MARK; - } - if (*status & NV_PFIFO_CACHE1_STATUS_LOW_MARK) { - // unset low mark - *status &= ~NV_PFIFO_CACHE1_STATUS_LOW_MARK; - // signal puller - qemu_cond_signal(&d->pfifo.puller_cond); - } - - if (method_type == NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_INC) { - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD, - (method + 4) >> 2); - } - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, - method_count - 1); - (*dma_dcount)++; - } else { - /* no command active - this is the first word of a new one */ - d->pfifo.regs[NV_PFIFO_CACHE1_DMA_RSVD_SHADOW] = word; - - /* match all forms */ - if ((word & 0xe0000003) == 0x20000000) { - /* old jump */ - d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW] = - dma_get_v; - dma_get_v = word & 0x1fffffff; - NV2A_DPRINTF("pb OLD_JMP 0x%08X\n", dma_get_v); - } else if ((word & 3) == 1) { - /* jump */ - d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW] = - dma_get_v; - dma_get_v = word & 0xfffffffc; - NV2A_DPRINTF("pb JMP 0x%08X\n", dma_get_v); - } else if ((word & 3) == 2) { - /* call */ - if (subroutine_state) { - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, - NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL); - break; - } else { - *dma_subroutine = dma_get_v; - SET_MASK(*dma_subroutine, - NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE, 1); - dma_get_v = word & 0xfffffffc; - NV2A_DPRINTF("pb CALL 0x%08X\n", dma_get_v); - } - } else if (word == 0x00020000) { - /* return */ - if (!subroutine_state) { - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, - NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN); - // break; - } else { - dma_get_v = *dma_subroutine & 0xfffffffc; - SET_MASK(*dma_subroutine, - NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE, 0); - NV2A_DPRINTF("pb RET 0x%08X\n", dma_get_v); - } - } else if ((word & 0xe0030003) == 0) { - /* increasing methods */ - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD, - (word & 0x1fff) >> 2 ); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL, - (word >> 13) & 7); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, - (word >> 18) & 0x7ff); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE, - NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_INC); - *dma_dcount = 0; - } else if ((word & 0xe0030003) == 0x40000000) { - /* non-increasing methods */ - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD, - (word & 0x1fff) >> 2 ); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL, - (word >> 13) & 7); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, - (word >> 18) & 0x7ff); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE, - NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_NON_INC); - *dma_dcount = 0; - } else { - NV2A_DPRINTF("pb reserved cmd 0x%08X - 0x%08X\n", - dma_get_v, word); - SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, - NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD); - // break; - assert(false); - } - } - - *dma_get = dma_get_v; - - if (GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR)) { - break; - } - } - - // NV2A_DPRINTF("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n", - // dma_len, control->dma_get, control->dma_put); - - uint32_t error = GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR); - if (error) { - NV2A_DPRINTF("pb error: %d\n", error); - assert(false); - - SET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_STATUS, 1); /* suspended */ - - // d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER; - // update_irq(d); - } -} - -int pfifo_pusher_thread(NV2AState *d) -{ - SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); - CxbxSetThreadName("Cxbx NV2A FIFO pusher"); - - qemu_mutex_lock(&d->pfifo.pfifo_lock); - while (true) { - pfifo_run_pusher(d); - qemu_cond_wait(&d->pfifo.pusher_cond, &d->pfifo.pfifo_lock); - - if (d->exiting) { - break; - } - } - qemu_mutex_unlock(&d->pfifo.pfifo_lock); - - return 0; -} - -unsigned int ramht_size(NV2AState *d) -{ - return - 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); -} - -static uint32_t ramht_hash(NV2AState *d, uint32_t handle) -{ - /* XXX: Think this is different to what nouveau calculates... */ - unsigned int bits = ffs(ramht_size(d)) - 2; - - uint32_t hash = 0; - while (handle) { - hash ^= (handle & ((1 << bits) - 1)); - handle >>= bits; - } - - unsigned int channel_id = GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1], - NV_PFIFO_CACHE1_PUSH1_CHID); - hash ^= channel_id << (bits - 4); - - return hash; -} - -static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle) -{ - uint32_t hash = ramht_hash(d, handle); - assert(hash * 8 < ramht_size(d)); - - xbaddr ramht_address = - GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], - NV_PFIFO_RAMHT_BASE_ADDRESS_MASK) << 12; - - uint8_t *entry_ptr = d->pramin.ramin_ptr + ramht_address + hash * 8; - - uint32_t entry_handle = ldl_le_p((uint32_t*)entry_ptr); - uint32_t entry_context = ldl_le_p((uint32_t*)(entry_ptr + 4)); - - RAMHTEntry entry; - entry.handle = entry_handle; - entry.instance = (entry_context & NV_RAMHT_INSTANCE) << 4; - entry.engine = (FIFOEngine)((entry_context & NV_RAMHT_ENGINE) >> 16); - entry.channel_id = (entry_context & NV_RAMHT_CHID) >> 24; - entry.valid = entry_context & NV_RAMHT_STATUS; - - return entry; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * This file is heavily based on code from XQEMU +// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pfifo.c +// * Copyright (c) 2012 espes +// * Copyright (c) 2015 Jannik Vogel +// * Copyright (c) 2018 Matt Borgerson +// * +// * Contributions for Cxbx-Reloaded +// * Copyright (c) 2017-2018 Luke Usher +// * Copyright (c) 2018 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +typedef struct RAMHTEntry { + uint32_t handle; + xbaddr instance; + enum FIFOEngine engine; + unsigned int channel_id : 5; + bool valid; +} RAMHTEntry; + +static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle); // forward declaration + +/* PFIFO - MMIO and DMA FIFO submission to PGRAPH and VPE */ +DEVICE_READ32(PFIFO) +{ + qemu_mutex_lock(&d->pfifo.pfifo_lock); + + DEVICE_READ32_SWITCH() { + case NV_PFIFO_RAMHT: + result = 0x03000100; // = NV_PFIFO_RAMHT_SIZE_4K | NV_PFIFO_RAMHT_BASE_ADDRESS(NumberOfPaddingBytes >> 12) | NV_PFIFO_RAMHT_SEARCH_128 + break; + case NV_PFIFO_RAMFC: + result = 0x00890110; // = ? | NV_PFIFO_RAMFC_SIZE_2K | ? + break; + case NV_PFIFO_INTR_0: + result = d->pfifo.pending_interrupts; + break; + case NV_PFIFO_INTR_EN_0: + result = d->pfifo.enabled_interrupts; + break; + case NV_PFIFO_RUNOUT_STATUS: + result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */ + break; + default: + DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO); + break; + } + + qemu_mutex_unlock(&d->pfifo.pfifo_lock); + + DEVICE_READ32_END(PFIFO); +} + +DEVICE_WRITE32(PFIFO) +{ + qemu_mutex_lock(&d->pfifo.pfifo_lock); + + switch(addr) { + case NV_PFIFO_INTR_0: + d->pfifo.pending_interrupts &= ~value; + update_irq(d); + break; + case NV_PFIFO_INTR_EN_0: + d->pfifo.enabled_interrupts = value; + update_irq(d); + break; + default: + DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO); + break; + } + + qemu_cond_broadcast(&d->pfifo.pusher_cond); + qemu_cond_broadcast(&d->pfifo.puller_cond); + + qemu_mutex_unlock(&d->pfifo.pfifo_lock); + + DEVICE_WRITE32_END(PFIFO); +} + +static void pfifo_run_puller(NV2AState *d) +{ + uint32_t *pull0 = &d->pfifo.regs[NV_PFIFO_CACHE1_PULL0]; + uint32_t *pull1 = &d->pfifo.regs[NV_PFIFO_CACHE1_PULL1]; + uint32_t *engine_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_ENGINE]; + + uint32_t *status = &d->pfifo.regs[NV_PFIFO_CACHE1_STATUS]; + uint32_t *get_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_GET]; + uint32_t *put_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_PUT]; + + // TODO + // CacheEntry working_cache[NV2A_CACHE1_SIZE]; + // int working_cache_size = 0; + // pull everything into our own queue + + // TODO think more about locking + + while (true) { + if (!GET_MASK(*pull0, NV_PFIFO_CACHE1_PULL0_ACCESS)) return; + + /* empty cache1 */ + if (*status & NV_PFIFO_CACHE1_STATUS_LOW_MARK) break; + + uint32_t get = *get_reg; + uint32_t put = *put_reg; + + assert(get < 128*4 && (get % 4) == 0); + uint32_t method_entry = d->pfifo.regs[NV_PFIFO_CACHE1_METHOD + get*2]; + uint32_t parameter = d->pfifo.regs[NV_PFIFO_CACHE1_DATA + get*2]; + + uint32_t new_get = (get+4) & 0x1fc; + *get_reg = new_get; + + if (new_get == put) { + // set low mark + *status |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; + } + if (*status & NV_PFIFO_CACHE1_STATUS_HIGH_MARK) { + // unset high mark + *status &= ~NV_PFIFO_CACHE1_STATUS_HIGH_MARK; + // signal pusher + qemu_cond_signal(&d->pfifo.pusher_cond); + } + + + uint32_t method = method_entry & 0x1FFC; + uint32_t subchannel = GET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_SUBCHANNEL); + + // NV2A_DPRINTF("pull %d 0x%08X 0x%08X - subch %d\n", get/4, method_entry, parameter, subchannel); + + if (method == 0) { + RAMHTEntry entry = ramht_lookup(d, parameter); + assert(entry.valid); + + // assert(entry.channel_id == state->channel_id); + + assert(entry.engine == ENGINE_GRAPHICS); + + + /* the engine is bound to the subchannel */ + assert(subchannel < 8); + SET_MASK(*engine_reg, 3 << (4*subchannel), entry.engine); + SET_MASK(*pull1, NV_PFIFO_CACHE1_PULL1_ENGINE, entry.engine); + // NV2A_DPRINTF("engine_reg1 %d 0x%08X\n", subchannel, *engine_reg); + + + // TODO: this is fucked + qemu_mutex_lock(&d->pgraph.pgraph_lock); + //make pgraph busy + qemu_mutex_unlock(&d->pfifo.pfifo_lock); + + pgraph_switch_context(d, entry.channel_id); + pgraph_wait_fifo_access(d); + pgraph_handle_method(d, subchannel, 0, entry.instance); + + // make pgraph not busy + qemu_mutex_unlock(&d->pgraph.pgraph_lock); + qemu_mutex_lock(&d->pfifo.pfifo_lock); + + } else if (method >= 0x100) { + // method passed to engine + + /* methods that take objects. + * TODO: Check this range is correct for the nv2a */ + if (method >= 0x180 && method < 0x200) { + //qemu_mutex_lock_iothread(); + RAMHTEntry entry = ramht_lookup(d, parameter); + assert(entry.valid); + // assert(entry.channel_id == state->channel_id); + parameter = entry.instance; + //qemu_mutex_unlock_iothread(); + } + + enum FIFOEngine engine = (enum FIFOEngine)GET_MASK(*engine_reg, 3 << (4*subchannel)); + // NV2A_DPRINTF("engine_reg2 %d 0x%08X\n", subchannel, *engine_reg); + assert(engine == ENGINE_GRAPHICS); + SET_MASK(*pull1, NV_PFIFO_CACHE1_PULL1_ENGINE, engine); + + // TODO: this is fucked + qemu_mutex_lock(&d->pgraph.pgraph_lock); + //make pgraph busy + qemu_mutex_unlock(&d->pfifo.pfifo_lock); + + pgraph_wait_fifo_access(d); + pgraph_handle_method(d, subchannel, method, parameter); + + // make pgraph not busy + qemu_mutex_unlock(&d->pgraph.pgraph_lock); + qemu_mutex_lock(&d->pfifo.pfifo_lock); + } else { + assert(false); + } + + } +} + +int pfifo_puller_thread(NV2AState *d) +{ + SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); + CxbxSetThreadName("Cxbx NV2A FIFO puller"); + + glo_set_current(d->pgraph.gl_context); + + qemu_mutex_lock(&d->pfifo.pfifo_lock); + while (true) { + pfifo_run_puller(d); + qemu_cond_wait(&d->pfifo.puller_cond, &d->pfifo.pfifo_lock); + + if (d->exiting) { + break; + } + } + qemu_mutex_unlock(&d->pfifo.pfifo_lock); + + glo_set_current(NULL); // Cxbx addition + + return NULL; +} + +static void pfifo_run_pusher(NV2AState *d) +{ + uint32_t *push0 = &d->pfifo.regs[NV_PFIFO_CACHE1_PUSH0]; + uint32_t *push1 = &d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1]; + uint32_t *dma_subroutine = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_SUBROUTINE]; + uint32_t *dma_state = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_STATE]; + uint32_t *dma_push = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUSH]; + uint32_t *dma_get = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET]; + uint32_t *dma_put = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_PUT]; + uint32_t *dma_dcount = &d->pfifo.regs[NV_PFIFO_CACHE1_DMA_DCOUNT]; + + uint32_t *status = &d->pfifo.regs[NV_PFIFO_CACHE1_STATUS]; + uint32_t *get_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_GET]; + uint32_t *put_reg = &d->pfifo.regs[NV_PFIFO_CACHE1_PUT]; + + if (!GET_MASK(*push0, NV_PFIFO_CACHE1_PUSH0_ACCESS)) return; + if (!GET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS)) return; + + /* suspended */ + if (GET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) return; + + // TODO: should we become busy here?? + // NV_PFIFO_CACHE1_DMA_PUSH_STATE _BUSY + + unsigned int channel_id = GET_MASK(*push1, + NV_PFIFO_CACHE1_PUSH1_CHID); + + + /* Channel running DMA */ + uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE]; + assert(channel_modes & (1 << channel_id)); + + assert(GET_MASK(*push1, NV_PFIFO_CACHE1_PUSH1_MODE) + == NV_PFIFO_CACHE1_PUSH1_MODE_DMA); + + /* We're running so there should be no pending errors... */ + assert(GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR) + == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE); + + hwaddr dma_instance = + GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_DMA_INSTANCE], + NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; // TODO : Use NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MOVE? + + hwaddr dma_len; + uint8_t *dma = (uint8_t*)nv_dma_map(d, dma_instance, &dma_len); + + /* based on the convenient pseudocode in envytools */ + while (true) { + uint32_t dma_get_v = *dma_get; + uint32_t dma_put_v = *dma_put; + if (dma_get_v == dma_put_v) break; + if (dma_get_v >= dma_len) { + assert(false); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, + NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION); + break; + } + + uint32_t word = ldl_le_p((uint32_t*)(dma + dma_get_v)); + dma_get_v += 4; + + uint32_t method_type = + GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); + uint32_t method_subchannel = + GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); + uint32_t method = + GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; + uint32_t method_count = + GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); + + uint32_t subroutine_state = + GET_MASK(*dma_subroutine, NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); + + if (method_count) { + /* full */ + if (*status & NV_PFIFO_CACHE1_STATUS_HIGH_MARK) return; + + + /* data word of methods command */ + d->pfifo.regs[NV_PFIFO_CACHE1_DMA_DATA_SHADOW] = word; + + uint32_t put = *put_reg; + uint32_t get = *get_reg; + + assert((method & 3) == 0); + uint32_t method_entry = 0; + SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_ADDRESS, method >> 2); + SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_TYPE, method_type); + SET_MASK(method_entry, NV_PFIFO_CACHE1_METHOD_SUBCHANNEL, method_subchannel); + + // NV2A_DPRINTF("push %d 0x%08X 0x%08X - subch %d\n", put/4, method_entry, word, method_subchannel); + + assert(put < 128*4 && (put%4) == 0); + d->pfifo.regs[NV_PFIFO_CACHE1_METHOD + put*2] = method_entry; + d->pfifo.regs[NV_PFIFO_CACHE1_DATA + put*2] = word; + + uint32_t new_put = (put+4) & 0x1fc; + *put_reg = new_put; + if (new_put == get) { + // set high mark + *status |= NV_PFIFO_CACHE1_STATUS_HIGH_MARK; + } + if (*status & NV_PFIFO_CACHE1_STATUS_LOW_MARK) { + // unset low mark + *status &= ~NV_PFIFO_CACHE1_STATUS_LOW_MARK; + // signal puller + qemu_cond_signal(&d->pfifo.puller_cond); + } + + if (method_type == NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_INC) { + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD, + (method + 4) >> 2); + } + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, + method_count - 1); + (*dma_dcount)++; + } else { + /* no command active - this is the first word of a new one */ + d->pfifo.regs[NV_PFIFO_CACHE1_DMA_RSVD_SHADOW] = word; + + /* match all forms */ + if ((word & 0xe0000003) == 0x20000000) { + /* old jump */ + d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW] = + dma_get_v; + dma_get_v = word & 0x1fffffff; + NV2A_DPRINTF("pb OLD_JMP 0x%08X\n", dma_get_v); + } else if ((word & 3) == 1) { + /* jump */ + d->pfifo.regs[NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW] = + dma_get_v; + dma_get_v = word & 0xfffffffc; + NV2A_DPRINTF("pb JMP 0x%08X\n", dma_get_v); + } else if ((word & 3) == 2) { + /* call */ + if (subroutine_state) { + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, + NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL); + break; + } else { + *dma_subroutine = dma_get_v; + SET_MASK(*dma_subroutine, + NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE, 1); + dma_get_v = word & 0xfffffffc; + NV2A_DPRINTF("pb CALL 0x%08X\n", dma_get_v); + } + } else if (word == 0x00020000) { + /* return */ + if (!subroutine_state) { + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, + NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN); + // break; + } else { + dma_get_v = *dma_subroutine & 0xfffffffc; + SET_MASK(*dma_subroutine, + NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE, 0); + NV2A_DPRINTF("pb RET 0x%08X\n", dma_get_v); + } + } else if ((word & 0xe0030003) == 0) { + /* increasing methods */ + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD, + (word & 0x1fff) >> 2 ); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL, + (word >> 13) & 7); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, + (word >> 18) & 0x7ff); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE, + NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_INC); + *dma_dcount = 0; + } else if ((word & 0xe0030003) == 0x40000000) { + /* non-increasing methods */ + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD, + (word & 0x1fff) >> 2 ); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL, + (word >> 13) & 7); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, + (word >> 18) & 0x7ff); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE, + NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE_NON_INC); + *dma_dcount = 0; + } else { + NV2A_DPRINTF("pb reserved cmd 0x%08X - 0x%08X\n", + dma_get_v, word); + SET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR, + NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD); + // break; + assert(false); + } + } + + *dma_get = dma_get_v; + + if (GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR)) { + break; + } + } + + // NV2A_DPRINTF("DMA pusher done: max 0x%08X, 0x%08X - 0x%08X\n", + // dma_len, control->dma_get, control->dma_put); + + uint32_t error = GET_MASK(*dma_state, NV_PFIFO_CACHE1_DMA_STATE_ERROR); + if (error) { + NV2A_DPRINTF("pb error: %d\n", error); + assert(false); + + SET_MASK(*dma_push, NV_PFIFO_CACHE1_DMA_PUSH_STATUS, 1); /* suspended */ + + // d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER; + // update_irq(d); + } +} + +int pfifo_pusher_thread(NV2AState *d) +{ + SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); + CxbxSetThreadName("Cxbx NV2A FIFO pusher"); + + qemu_mutex_lock(&d->pfifo.pfifo_lock); + while (true) { + pfifo_run_pusher(d); + qemu_cond_wait(&d->pfifo.pusher_cond, &d->pfifo.pfifo_lock); + + if (d->exiting) { + break; + } + } + qemu_mutex_unlock(&d->pfifo.pfifo_lock); + + return 0; +} + +unsigned int ramht_size(NV2AState *d) +{ + return + 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); +} + +static uint32_t ramht_hash(NV2AState *d, uint32_t handle) +{ + /* XXX: Think this is different to what nouveau calculates... */ + unsigned int bits = ffs(ramht_size(d)) - 2; + + uint32_t hash = 0; + while (handle) { + hash ^= (handle & ((1 << bits) - 1)); + handle >>= bits; + } + + unsigned int channel_id = GET_MASK(d->pfifo.regs[NV_PFIFO_CACHE1_PUSH1], + NV_PFIFO_CACHE1_PUSH1_CHID); + hash ^= channel_id << (bits - 4); + + return hash; +} + +static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle) +{ + uint32_t hash = ramht_hash(d, handle); + assert(hash * 8 < ramht_size(d)); + + xbaddr ramht_address = + GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], + NV_PFIFO_RAMHT_BASE_ADDRESS_MASK) << 12; + + uint8_t *entry_ptr = d->pramin.ramin_ptr + ramht_address + hash * 8; + + uint32_t entry_handle = ldl_le_p((uint32_t*)entry_ptr); + uint32_t entry_context = ldl_le_p((uint32_t*)(entry_ptr + 4)); + + RAMHTEntry entry; + entry.handle = entry_handle; + entry.instance = (entry_context & NV_RAMHT_INSTANCE) << 4; + entry.engine = (FIFOEngine)((entry_context & NV_RAMHT_ENGINE) >> 16); + entry.channel_id = (entry_context & NV_RAMHT_CHID) >> 24; + entry.valid = entry_context & NV_RAMHT_STATUS; + + return entry; +} diff --git a/src/devices/video/EmuNV2A_PGRAPH.cpp b/src/devices/video/EmuNV2A_PGRAPH.cpp index c8df89c70..f0c3796d5 100644 --- a/src/devices/video/EmuNV2A_PGRAPH.cpp +++ b/src/devices/video/EmuNV2A_PGRAPH.cpp @@ -1,4894 +1,4894 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * This file is heavily based on code from XQEMU -// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pgraph.c -// * Copyright (c) 2012 espes -// * Copyright (c) 2015 Jannik Vogel -// * Copyright (c) 2018 Matt Borgerson -// * -// * Contributions for Cxbx-Reloaded -// * Copyright (c) 2017-2018 Luke Usher -// * Copyright (c) 2018 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -// FIXME -#define qemu_mutex_lock_iothread() -#define qemu_mutex_unlock_iothread() - -// Xbox uses 4 KiB pages -#define TARGET_PAGE_BITS 12 -#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) -#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) -#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK) - -static const GLenum pgraph_texture_min_filter_map[] = { - 0, - GL_NEAREST, - GL_LINEAR, - GL_NEAREST_MIPMAP_NEAREST, - GL_LINEAR_MIPMAP_NEAREST, - GL_NEAREST_MIPMAP_LINEAR, - GL_LINEAR_MIPMAP_LINEAR, - GL_LINEAR, /* TODO: Convolution filter... */ -}; - -static const GLenum pgraph_texture_mag_filter_map[] = { - 0, - GL_NEAREST, - GL_LINEAR, - 0, - GL_LINEAR /* TODO: Convolution filter... */ -}; - -static const GLenum pgraph_texture_addr_map[] = { - 0, - GL_REPEAT, - GL_MIRRORED_REPEAT, - GL_CLAMP_TO_EDGE, - GL_CLAMP_TO_BORDER, - // GL_CLAMP -}; - -static const GLenum pgraph_blend_factor_map[] = { - GL_ZERO, - GL_ONE, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_SRC_ALPHA_SATURATE, - 0, - GL_CONSTANT_COLOR, - GL_ONE_MINUS_CONSTANT_COLOR, - GL_CONSTANT_ALPHA, - GL_ONE_MINUS_CONSTANT_ALPHA, -}; - -static const GLenum pgraph_blend_equation_map[] = { - GL_FUNC_SUBTRACT, - GL_FUNC_REVERSE_SUBTRACT, - GL_FUNC_ADD, - GL_MIN, - GL_MAX, - GL_FUNC_REVERSE_SUBTRACT, - GL_FUNC_ADD, -}; - -static const GLenum pgraph_blend_logicop_map[] = { - GL_CLEAR, - GL_AND, - GL_AND_REVERSE, - GL_COPY, - GL_AND_INVERTED, - GL_NOOP, - GL_XOR, - GL_OR, - GL_NOR, - GL_EQUIV, - GL_INVERT, - GL_OR_REVERSE, - GL_COPY_INVERTED, - GL_OR_INVERTED, - GL_NAND, - GL_SET, -}; - -static const GLenum pgraph_cull_face_map[] = { - 0, - GL_FRONT, - GL_BACK, - GL_FRONT_AND_BACK -}; - -static const GLenum pgraph_depth_func_map[] = { - GL_NEVER, - GL_LESS, - GL_EQUAL, - GL_LEQUAL, - GL_GREATER, - GL_NOTEQUAL, - GL_GEQUAL, - GL_ALWAYS, -}; - -static const GLenum pgraph_stencil_func_map[] = { - GL_NEVER, - GL_LESS, - GL_EQUAL, - GL_LEQUAL, - GL_GREATER, - GL_NOTEQUAL, - GL_GEQUAL, - GL_ALWAYS, -}; - -static const GLenum pgraph_stencil_op_map[] = { - 0, - GL_KEEP, - GL_ZERO, - GL_REPLACE, - GL_INCR, - GL_DECR, - GL_INVERT, - GL_INCR_WRAP, - GL_DECR_WRAP, -}; - -enum FormatEncoding { - linear = 0, - swizzled, // for all NV097_SET_TEXTURE_FORMAT_*_SZ_* - compressed // for all NV097_SET_TEXTURE_FORMAT_*_DXT* -}; - -typedef struct ColorFormatInfo { - unsigned int bytes_per_pixel; // Derived from the total number of channel bits - FormatEncoding encoding; - GLint gl_internal_format; - GLenum gl_format; // == 0 for compressed formats - GLenum gl_type; - GLint *gl_swizzle_mask; // == nullptr when gl_internal_format, gl_format and gl_type are sufficient -} ColorFormatInfo; - -// Resulting gl_internal_format, gl_format and gl_type values, for formats handled by convert_texture_data() -#define GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV - -static GLint gl_swizzle_mask_0RG1[4] = { GL_ZERO, GL_RED, GL_GREEN, GL_ONE }; -static GLint gl_swizzle_mask_111R[4] = { GL_ONE, GL_ONE, GL_ONE, GL_RED }; -static GLint gl_swizzle_mask_ARGB[4] = { GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE }; -static GLint gl_swizzle_mask_BGRA[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA }; -static GLint gl_swizzle_mask_GGGR[4] = { GL_GREEN, GL_GREEN, GL_GREEN, GL_RED }; -static GLint gl_swizzle_mask_R0G1[4] = { GL_RED, GL_ZERO, GL_GREEN, GL_ONE }; -static GLint gl_swizzle_mask_RRR1[4] = { GL_RED, GL_RED, GL_RED, GL_ONE }; -static GLint gl_swizzle_mask_RRRG[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; -static GLint gl_swizzle_mask_RRRR[4] = { GL_RED, GL_RED, GL_RED, GL_RED }; - -// Note : Avoid designated initializers to facilitate C++ builds -static const ColorFormatInfo kelvin_color_format_map[256] = { - //0x00 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y8] = - {1, swizzled, GL_R8, GL_RED, GL_UNSIGNED_BYTE, - gl_swizzle_mask_RRR1}, - //0x01 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_AY8] = - {1, swizzled, GL_R8, GL_RED, GL_UNSIGNED_BYTE, - gl_swizzle_mask_RRRR}, - //0x02 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A1R5G5B5] = - {2, swizzled, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - //0x03 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X1R5G5B5] = - {2, swizzled, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - //0x04 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A4R4G4B4] = - {2, swizzled, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, - //0x05 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G6B5] = - {2, swizzled, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, - //0x06 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8] = - {4, swizzled, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, - //0x07 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X8R8G8B8] = - {4, swizzled, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, - //0x08 [?] = - {}, - //0x09 [?] = - {}, - //0x0A [?] = - {}, - - /* paletted texture */ - //0x0B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8] = // See convert_texture_data - {1, swizzled, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, - - //0x0C [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT1_A1R5G5B5] = - {4, compressed, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_RGBA}, - //0x0D [?] = - {}, - //0x0E [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT23_A8R8G8B8] = - {4, compressed, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_RGBA}, - //0x0F [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT45_A8R8G8B8] = - {4, compressed, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_RGBA}, - //0x10 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A1R5G5B5] = - {2, linear, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - //0x11 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5] = - {2, linear, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, - //0x12 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8] = - {4, linear, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, - //0x13 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y8] = - {1, linear, GL_R8, GL_RED, GL_UNSIGNED_BYTE, - gl_swizzle_mask_RRR1}, - //0x14 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_SY8] = - {1, linear, GL_R8_SNORM, GL_RED, GL_BYTE, - gl_swizzle_mask_RRR1}, // TODO : Verify - //0x15 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X7SY9] = // See convert_texture_data FIXME - {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - //0x16 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8B8] = - {2, linear, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, - gl_swizzle_mask_R0G1}, // TODO : Verify - //0x17 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_G8B8] = - {2, linear, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, - gl_swizzle_mask_0RG1}, // TODO : Verify - //0x18 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_SG8SB8] = - {2, linear, GL_RG8_SNORM, GL_RG, GL_BYTE, - gl_swizzle_mask_0RG1}, // TODO : Verify - - //0x19 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8] = - {1, swizzled, GL_R8, GL_RED, GL_UNSIGNED_BYTE, - gl_swizzle_mask_111R}, - //0x1A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8Y8] = - {2, swizzled, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, - gl_swizzle_mask_GGGR}, - //0x1B [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_AY8] = - {1, linear, GL_R8, GL_RED, GL_UNSIGNED_BYTE, - gl_swizzle_mask_RRRR}, - //0x1C [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5] = - {2, linear, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - //0x1D [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A4R4G4B4] = - {2, linear, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // TODO : Verify this is truely linear - //0x1E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8] = - {4, linear, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, - //0x1F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8] = - {1, linear, GL_R8, GL_RED, GL_UNSIGNED_BYTE, - gl_swizzle_mask_111R}, - //0x20 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8Y8] = - {2, linear, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, - gl_swizzle_mask_GGGR}, - //0x21 [?] = - {}, - //0x22 [?] = - {}, - //0x23 [?] = - {}, - //0x24 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8] = // See convert_texture_data calling ____UYVYToARGBRow_C - {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - //0x25 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_YB8CR8YA8CB8] = // See convert_texture_data calling ____YUY2ToARGBRow_C - {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - //0x26 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8CR8CB8Y8] = // See convert_texture_data FIXME - {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - - //0x27 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5] = // See convert_texture_data calling __R6G5B5ToARGBRow_C - {2, swizzled, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - //0x28 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_G8B8] = - {2, swizzled, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, - gl_swizzle_mask_0RG1}, - //0x29 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8B8] = - {2, swizzled, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, - gl_swizzle_mask_R0G1}, - //0x2A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_X8_Y24_FIXED] = - {4, swizzled, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // TODO : Verify - //0x2B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_X8_Y24_FLOAT] = - {4, swizzled, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // TODO : Verify - //0x2C [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_Y16_FIXED] = - {2, swizzled, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // TODO : Verify - //0x2D [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_Y16_FLOAT] = - {2, swizzled, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT}, // TODO : Verify - - - /* TODO: format conversion */ - //0x2E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] = - {4, linear, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, - //0x2F [?] = - {}, - //0x30 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FIXED] = - {2, linear, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, - //0x31 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FLOAT] = - {2, linear, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT}, // TODO : Verify - //0x32 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y16] = - {2, swizzled, GL_R16, GL_RED, GL_UNSIGNED_SHORT, // TODO : Verify - gl_swizzle_mask_RRR1}, - //0x33 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_YB16YA16] = - {4, swizzled, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, // TODO : Verify - gl_swizzle_mask_RRRG}, - //0x34 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_A4V6YB6A4U6YA6] = // TODO : handle in convert_texture_data - {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - //0x35 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y16] = - {2, linear, GL_R16, GL_RED, GL_UNSIGNED_SHORT, - gl_swizzle_mask_RRR1}, - //0x36 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_YB16YA16] = - {4, linear, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, // TODO : Verify - gl_swizzle_mask_RRRG}, - //0x37 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R6G5B5] = // See convert_texture_data calling __R6G5B5ToARGBRow_C - {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify - //0x38 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G5B5A1] = - {2, swizzled, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // TODO : Verify - //0x39 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R4G4B4A4] = - {2, swizzled, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // TODO : Verify - //0x3A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8B8G8R8] = - {4, swizzled, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // TODO : Verify - //0x3B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_B8G8R8A8] = - {4, swizzled, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, // TODO : Verify - - //0x3C [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8G8B8A8] = - {4, swizzled, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, - //0x3D [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G5B5A1] = - {2, linear, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // TODO : Verify - //0x3E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R4G4B4A4] = - {2, linear, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // TODO : Verify - - //0x3F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8B8G8R8] = - {4, linear, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // TODO : Verify - //0x40 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_B8G8R8A8] = - {4, linear, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, // TODO : Verify - //0x41 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8G8B8A8] = - {4, linear, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // TODO : Verify -}; - -typedef struct SurfaceColorFormatInfo { - unsigned int bytes_per_pixel; - GLint gl_internal_format; - GLenum gl_format; - GLenum gl_type; -} SurfaceColorFormatInfo; - -// Note : Avoid designated initializers to facilitate C++ builds -static const SurfaceColorFormatInfo kelvin_surface_color_format_map[16] = { - //0x00 [?] = - {}, - //0x01 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5] = - {2, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - //0x02 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5] = - {}, - //0x03 [NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] = - {2, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, - //0x04 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8] = - {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, - //0x05 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8] = - {}, - //0x06 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8] = - {}, - //0x07 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8] = - {}, - //0x08 [NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] = - {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, - //0x09 [NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] = - {}, // TODO : {1, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // PatrickvL guesstimate - //0x0A [NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] = - {}, // TODO : {2, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // PatrickvL guesstimate - //0x0B [?] = - {}, - //0x0C [?] = - {}, - //0x0D [?] = - {}, - //0x0E [?] = - {}, - //0x0F [?] = - {} -}; - -void (*pgraph_draw_arrays)(NV2AState *d); -void (*pgraph_draw_inline_buffer)(NV2AState *d); -void (*pgraph_draw_inline_array)(NV2AState *d); -void (*pgraph_draw_inline_elements)(NV2AState *d); -void (*pgraph_draw_state_update)(NV2AState *d); -void (*pgraph_draw_clear)(NV2AState *d); - -//static void pgraph_set_context_user(NV2AState *d, uint32_t value); -void pgraph_handle_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter); -static void pgraph_log_method(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter); -static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr); -static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg); -static void pgraph_update_shader_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function); -static void pgraph_bind_shaders(PGRAPHState *pg); -static bool pgraph_get_framebuffer_dirty(PGRAPHState *pg); -static bool pgraph_get_color_write_enabled(PGRAPHState *pg); -static bool pgraph_get_zeta_write_enabled(PGRAPHState *pg); -static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta); -static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color); -static void pgraph_update_surface(NV2AState *d, bool upload, bool color_write, bool zeta_write); -static void pgraph_bind_textures(NV2AState *d); -static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height); -static void pgraph_get_surface_dimensions(PGRAPHState *pg, unsigned int *width, unsigned int *height); -static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool f); -static void pgraph_bind_vertex_attributes(NV2AState *d, unsigned int num_elements, bool inline_data, unsigned int inline_stride); -static unsigned int pgraph_bind_inline_array(NV2AState *d); - -static float convert_f16_to_float(uint16_t f16); -static float convert_f24_to_float(uint32_t f24); -static uint8_t* convert_texture_data(const unsigned int color_format, const uint8_t *data, const uint8_t *palette_data, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int row_pitch, const unsigned int slice_pitch); -static int upload_gl_texture(GLenum gl_target, const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); -static TextureBinding* generate_texture(const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); -static guint texture_key_hash(gconstpointer key); -static gboolean texture_key_equal(gconstpointer a, gconstpointer b); -static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error); -static void texture_key_destroy(gpointer data); -static void texture_binding_destroy(gpointer data); -static guint shader_hash(gconstpointer key); -static gboolean shader_equal(gconstpointer a, gconstpointer b); -static unsigned int kelvin_map_stencil_op(uint32_t parameter); -static unsigned int kelvin_map_polygon_mode(uint32_t parameter); -static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel); -static uint64_t fnv_hash(const uint8_t *data, size_t len); -static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples); - -/* PGRAPH - accelerated 2d/3d drawing engine */ -DEVICE_READ32(PGRAPH) -{ - qemu_mutex_lock(&d->pgraph.pgraph_lock); - - DEVICE_READ32_SWITCH() { - case NV_PGRAPH_INTR: - result = d->pgraph.pending_interrupts; - break; - case NV_PGRAPH_INTR_EN: - result = d->pgraph.enabled_interrupts; - break; - default: - DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH); - } - - qemu_mutex_unlock(&d->pgraph.pgraph_lock); - -// reg_log_read(NV_PGRAPH, addr, r); - - DEVICE_READ32_END(PGRAPH); -} - -DEVICE_WRITE32(PGRAPH) -{ -// reg_log_write(NV_PGRAPH, addr, val); - - qemu_mutex_lock(&d->pgraph.pgraph_lock); - - switch (addr) { - case NV_PGRAPH_INTR: - d->pgraph.pending_interrupts &= ~value; - qemu_cond_broadcast(&d->pgraph.interrupt_cond); - break; - case NV_PGRAPH_INTR_EN: - d->pgraph.enabled_interrupts = value; - break; - case NV_PGRAPH_INCREMENT: - if (value & NV_PGRAPH_INCREMENT_READ_3D) { - SET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_READ_3D, - (GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_READ_3D) + 1) - % GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_MODULO_3D)); - qemu_cond_broadcast(&d->pgraph.flip_3d); - } - break; - case NV_PGRAPH_CHANNEL_CTX_TRIGGER: { - xbaddr context_address = - GET_MASK(d->pgraph.regs[NV_PGRAPH_CHANNEL_CTX_POINTER], NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4; - - if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) { - unsigned pgraph_channel_id = - GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID); - - NV2A_DPRINTF("PGRAPH: read channel %d context from %" HWADDR_PRIx "\n", - pgraph_channel_id, context_address); - - uint8_t *context_ptr = d->pramin.ramin_ptr + context_address; - uint32_t context_user = ldl_le_p((uint32_t*)context_ptr); - - NV2A_DPRINTF(" - CTX_USER = 0x%08X\n", context_user); - - d->pgraph.regs[NV_PGRAPH_CTX_USER] = context_user; - // pgraph_set_context_user(d, context_user); - } - if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) { - /* do stuff ... */ - } - - break; - } - default: - DEVICE_WRITE32_REG(pgraph); // Was : DEBUG_WRITE32_UNHANDLED(PGRAPH); - break; - } - - // events - switch (addr) { - case NV_PGRAPH_FIFO: - qemu_cond_broadcast(&d->pgraph.fifo_access_cond); - break; - } - - qemu_mutex_unlock(&d->pgraph.pgraph_lock); - - DEVICE_WRITE32_END(PGRAPH); -} - -void OpenGL_draw_end(NV2AState *d); // forward declaration - -void OpenGL_draw_arrays(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - assert(pg->shader_binding); - - pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count, - false, 0); - glMultiDrawArrays(pg->shader_binding->gl_primitive_mode, - pg->gl_draw_arrays_start, - pg->gl_draw_arrays_count, - pg->draw_arrays_length); - - OpenGL_draw_end(d); -} - -void OpenGL_draw_inline_buffer(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - assert(pg->shader_binding); - - for (unsigned int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) { - VertexAttribute *vertex_attribute = &pg->vertex_attributes[i]; - - if (vertex_attribute->inline_buffer) { - - glBindBuffer(GL_ARRAY_BUFFER, - vertex_attribute->gl_inline_buffer); - glBufferData(GL_ARRAY_BUFFER, - pg->inline_buffer_length - * sizeof(float) * 4, - vertex_attribute->inline_buffer, - GL_DYNAMIC_DRAW); - - /* Clear buffer for next batch */ - g_free(vertex_attribute->inline_buffer); - vertex_attribute->inline_buffer = NULL; - - glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(i); - } - else { - glDisableVertexAttribArray(i); - glVertexAttrib4fv(i, vertex_attribute->inline_value); - } - } - - glDrawArrays(pg->shader_binding->gl_primitive_mode, - 0, pg->inline_buffer_length); - - OpenGL_draw_end(d); -} - -void OpenGL_draw_inline_array(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - assert(pg->shader_binding); - - unsigned int index_count = pgraph_bind_inline_array(d); - glDrawArrays(pg->shader_binding->gl_primitive_mode, - 0, index_count); - - OpenGL_draw_end(d); -} - -void OpenGL_draw_inline_elements(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - assert(pg->shader_binding); - - uint32_t max_element = 0; - uint32_t min_element = (uint32_t)-1; - for (unsigned int i = 0; i < pg->inline_elements_length; i++) { - max_element = MAX(pg->inline_elements[i], max_element); - min_element = MIN(pg->inline_elements[i], min_element); - } - pgraph_bind_vertex_attributes(d, max_element + 1, false, 0); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - pg->inline_elements_length * sizeof(pg->inline_elements[0]), - pg->inline_elements, - GL_DYNAMIC_DRAW); - - glDrawRangeElements(pg->shader_binding->gl_primitive_mode, - min_element, max_element, - pg->inline_elements_length, - GL_UNSIGNED_SHORT, // Cxbx-Reloaded TODO : Restore GL_UNSIGNED_INT once HLE_draw_inline_elements can draw using uint32_t - (void*)0); - - OpenGL_draw_end(d); -} - -void OpenGL_draw_state_update(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - - NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", pg->primitive_mode); - - uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0]; - uint32_t control_1 = pg->regs[NV_PGRAPH_CONTROL_1]; - - bool depth_test = control_0 - & NV_PGRAPH_CONTROL_0_ZENABLE; - bool stencil_test = control_1 - & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE; - - pgraph_update_surface(d, true, true, depth_test || stencil_test); - - bool alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE; - bool red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE; - bool green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE; - bool blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE; - glColorMask(red, green, blue, alpha); - glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE)); - glStencilMask(GET_MASK(control_1, - NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE)); - - uint32_t blend = pg->regs[NV_PGRAPH_BLEND]; - if (blend & NV_PGRAPH_BLEND_EN) { - glEnable(GL_BLEND); - uint32_t sfactor = GET_MASK(blend, - NV_PGRAPH_BLEND_SFACTOR); - uint32_t dfactor = GET_MASK(blend, - NV_PGRAPH_BLEND_DFACTOR); - assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_map)); - assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_map)); - glBlendFunc(pgraph_blend_factor_map[sfactor], - pgraph_blend_factor_map[dfactor]); - - uint32_t equation = GET_MASK(blend, - NV_PGRAPH_BLEND_EQN); - assert(equation < ARRAY_SIZE(pgraph_blend_equation_map)); - glBlendEquation(pgraph_blend_equation_map[equation]); - - uint32_t blend_color = pg->regs[NV_PGRAPH_BLENDCOLOR]; - glBlendColor(((blend_color >> 16) & 0xFF) / 255.0f, /* red */ - ((blend_color >> 8) & 0xFF) / 255.0f, /* green */ - (blend_color & 0xFF) / 255.0f, /* blue */ - ((blend_color >> 24) & 0xFF) / 255.0f);/* alpha */ - } - else { - glDisable(GL_BLEND); - } - - /* Face culling */ - uint32_t setupraster = pg->regs[NV_PGRAPH_SETUPRASTER]; - if (setupraster - & NV_PGRAPH_SETUPRASTER_CULLENABLE) { - uint32_t cull_face = GET_MASK(setupraster, - NV_PGRAPH_SETUPRASTER_CULLCTRL); - assert(cull_face < ARRAY_SIZE(pgraph_cull_face_map)); - glCullFace(pgraph_cull_face_map[cull_face]); - glEnable(GL_CULL_FACE); - } - else { - glDisable(GL_CULL_FACE); - } - - /* Front-face select */ - glFrontFace(setupraster - & NV_PGRAPH_SETUPRASTER_FRONTFACE - ? GL_CCW : GL_CW); - - /* Polygon offset */ - /* FIXME: GL implementation-specific, maybe do this in VS? */ - if (setupraster & - NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) { - glEnable(GL_POLYGON_OFFSET_FILL); - } - else { - glDisable(GL_POLYGON_OFFSET_FILL); - } - if (setupraster & - NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) { - glEnable(GL_POLYGON_OFFSET_LINE); - } - else { - glDisable(GL_POLYGON_OFFSET_LINE); - } - if (setupraster & - NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) { - glEnable(GL_POLYGON_OFFSET_POINT); - } - else { - glDisable(GL_POLYGON_OFFSET_POINT); - } - if (setupraster & - (NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE | - NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE | - NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) { - GLfloat zfactor = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETFACTOR]; - GLfloat zbias = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETBIAS]; - glPolygonOffset(zfactor, zbias); - } - - /* Depth testing */ - if (depth_test) { - glEnable(GL_DEPTH_TEST); - - uint32_t depth_func = GET_MASK(control_0, - NV_PGRAPH_CONTROL_0_ZFUNC); - assert(depth_func < ARRAY_SIZE(pgraph_depth_func_map)); - glDepthFunc(pgraph_depth_func_map[depth_func]); - } - else { - glDisable(GL_DEPTH_TEST); - } - - if (stencil_test) { - glEnable(GL_STENCIL_TEST); - - uint32_t stencil_func = GET_MASK(control_1, - NV_PGRAPH_CONTROL_1_STENCIL_FUNC); - uint32_t stencil_ref = GET_MASK(control_1, - NV_PGRAPH_CONTROL_1_STENCIL_REF); - uint32_t func_mask = GET_MASK(control_1, - NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ); - uint32_t control2 = pg->regs[NV_PGRAPH_CONTROL_2]; - uint32_t op_fail = GET_MASK(control2, - NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL); - uint32_t op_zfail = GET_MASK(control2, - NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL); - uint32_t op_zpass = GET_MASK(control2, - NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS); - - assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_map)); - assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_map)); - assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_map)); - assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_map)); - - glStencilFunc( - pgraph_stencil_func_map[stencil_func], - stencil_ref, - func_mask); - - glStencilOp( - pgraph_stencil_op_map[op_fail], - pgraph_stencil_op_map[op_zfail], - pgraph_stencil_op_map[op_zpass]); - - } - else { - glDisable(GL_STENCIL_TEST); - } - - /* Dither */ - /* FIXME: GL implementation dependent */ - if (control_0 & - NV_PGRAPH_CONTROL_0_DITHERENABLE) { - glEnable(GL_DITHER); - } - else { - glDisable(GL_DITHER); - } - - pgraph_bind_shaders(pg); - pgraph_bind_textures(d); - - //glDisableVertexAttribArray(NV2A_VERTEX_ATTR_DIFFUSE); - //glVertexAttrib4f(NV2A_VERTEX_ATTR_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f); - - unsigned int width, height; - pgraph_get_surface_dimensions(pg, &width, &height); - pgraph_apply_anti_aliasing_factor(pg, &width, &height); - glViewport(0, 0, width, height); - - /* Visibility testing */ - if (pg->zpass_pixel_count_enable) { - GLuint gl_query; - glGenQueries(1, &gl_query); - pg->gl_zpass_pixel_count_query_count++; - pg->gl_zpass_pixel_count_queries = (GLuint*)g_realloc( - pg->gl_zpass_pixel_count_queries, - sizeof(GLuint) * pg->gl_zpass_pixel_count_query_count); - pg->gl_zpass_pixel_count_queries[ - pg->gl_zpass_pixel_count_query_count - 1] = gl_query; - glBeginQuery(GL_SAMPLES_PASSED, gl_query); - } -} - -void OpenGL_draw_end(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - - /* End of visibility testing */ - if (pg->zpass_pixel_count_enable) { - glEndQuery(GL_SAMPLES_PASSED); - } - - NV2A_GL_DGROUP_END(); -} - -void OpenGL_draw_clear(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - - NV2A_DPRINTF("---------PRE CLEAR ------\n"); - GLbitfield gl_mask = 0; - - bool write_color = (pg->clear_surface & NV097_CLEAR_SURFACE_COLOR); - bool write_zeta = - (pg->clear_surface & (NV097_CLEAR_SURFACE_Z | NV097_CLEAR_SURFACE_STENCIL)); - - if (write_zeta) { - uint32_t clear_zstencil = - d->pgraph.regs[NV_PGRAPH_ZSTENCILCLEARVALUE]; - GLint gl_clear_stencil; - GLfloat gl_clear_depth; - - /* FIXME: Put these in some lookup table */ - const float f16_max = 511.9375f; - /* FIXME: 7 bits of mantissa unused. maybe use full buffer? */ - const float f24_max = 3.4027977E38f; - - switch (pg->surface_shape.zeta_format) { - case NV097_SET_SURFACE_FORMAT_ZETA_Z16: { - if (pg->clear_surface & NV097_CLEAR_SURFACE_Z) { - gl_mask |= GL_DEPTH_BUFFER_BIT; - uint16_t z = clear_zstencil & 0xFFFF; - if (pg->surface_shape.z_format) { - gl_clear_depth = convert_f16_to_float(z) / f16_max; - assert(false); /* FIXME: Untested */ - } - else { - gl_clear_depth = z / (float)0xFFFF; - } - } - break; - } - case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: { - if (pg->clear_surface & NV097_CLEAR_SURFACE_STENCIL) { - gl_mask |= GL_STENCIL_BUFFER_BIT; - gl_clear_stencil = clear_zstencil & 0xFF; - } - if (pg->clear_surface & NV097_CLEAR_SURFACE_Z) { - gl_mask |= GL_DEPTH_BUFFER_BIT; - uint32_t z = clear_zstencil >> 8; - if (pg->surface_shape.z_format) { - gl_clear_depth = convert_f24_to_float(z) / f24_max; - assert(false); /* FIXME: Untested */ - } - else { - gl_clear_depth = z / (float)0xFFFFFF; - } - } - break; - } - default: - fprintf(stderr, "Unknown zeta surface format: 0x%x\n", pg->surface_shape.zeta_format); - assert(false); - break; - } - - if (gl_mask & GL_DEPTH_BUFFER_BIT) { - glDepthMask(GL_TRUE); - glClearDepth(gl_clear_depth); - } - - if (gl_mask & GL_STENCIL_BUFFER_BIT) { - glStencilMask(0xff); - glClearStencil(gl_clear_stencil); - } - } - - if (write_color) { - gl_mask |= GL_COLOR_BUFFER_BIT; - glColorMask((pg->clear_surface & NV097_CLEAR_SURFACE_R) - ? GL_TRUE : GL_FALSE, - (pg->clear_surface & NV097_CLEAR_SURFACE_G) - ? GL_TRUE : GL_FALSE, - (pg->clear_surface & NV097_CLEAR_SURFACE_B) - ? GL_TRUE : GL_FALSE, - (pg->clear_surface & NV097_CLEAR_SURFACE_A) - ? GL_TRUE : GL_FALSE); - uint32_t clear_color = d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]; - - /* Handle RGB */ - GLfloat red, green, blue; - switch (pg->surface_shape.color_format) { - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5: - red = ((clear_color >> 10) & 0x1F) / 31.0f; - green = ((clear_color >> 5) & 0x1F) / 31.0f; - blue = (clear_color & 0x1F) / 31.0f; - assert(false); /* Untested */ - break; - case NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5: - red = ((clear_color >> 11) & 0x1F) / 31.0f; - green = ((clear_color >> 5) & 0x3F) / 63.0f; - blue = (clear_color & 0x1F) / 31.0f; - break; - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: - red = ((clear_color >> 16) & 0xFF) / 255.0f; - green = ((clear_color >> 8) & 0xFF) / 255.0f; - blue = (clear_color & 0xFF) / 255.0f; - break; - case NV097_SET_SURFACE_FORMAT_COLOR_LE_B8: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8: - /* Xbox D3D doesn't support clearing those */ - default: - red = 1.0f; - green = 0.0f; - blue = 1.0f; - fprintf(stderr, "CLEAR_SURFACE for color_format 0x%x unsupported", - pg->surface_shape.color_format); - assert(false); - break; - } - - /* Handle alpha */ - GLfloat alpha; - switch (pg->surface_shape.color_format) { - /* FIXME: CLEAR_SURFACE seems to work like memset, so maybe we - * also have to clear non-alpha bits with alpha value? - * As GL doesn't own those pixels we'd have to do this on - * our own in xbox memory. - */ - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: - case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: - alpha = ((clear_color >> 24) & 0x7F) / 127.0f; - assert(false); /* Untested */ - break; - case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: - alpha = ((clear_color >> 24) & 0xFF) / 255.0f; - break; - default: - alpha = 1.0f; - break; - } - - glClearColor(red, green, blue, alpha); - } - - if (gl_mask) { - pgraph_update_surface(d, true, write_color, write_zeta); - - glEnable(GL_SCISSOR_TEST); - - unsigned int xmin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX], - NV_PGRAPH_CLEARRECTX_XMIN); - unsigned int xmax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX], - NV_PGRAPH_CLEARRECTX_XMAX); - unsigned int ymin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY], - NV_PGRAPH_CLEARRECTY_YMIN); - unsigned int ymax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY], - NV_PGRAPH_CLEARRECTY_YMAX); - - unsigned int scissor_x = xmin; - unsigned int scissor_y = pg->surface_shape.clip_height - ymax - 1; - - unsigned int scissor_width = xmax - xmin + 1; - unsigned int scissor_height = ymax - ymin + 1; - - pgraph_apply_anti_aliasing_factor(pg, &scissor_x, &scissor_y); - pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height); - - /* FIXME: Should this really be inverted instead of ymin? */ - glScissor(scissor_x, scissor_y, scissor_width, scissor_height); - - /* FIXME: Respect window clip?!?! */ - - NV2A_DPRINTF("------------------CLEAR 0x%x %d,%d - %d,%d %x---------------\n", - parameter, xmin, ymin, xmax, ymax, d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]); - - /* Dither */ - /* FIXME: Maybe also disable it here? + GL implementation dependent */ - if (pg->regs[NV_PGRAPH_CONTROL_0] & - NV_PGRAPH_CONTROL_0_DITHERENABLE) { - glEnable(GL_DITHER); - } - else { - glDisable(GL_DITHER); - } - - glClear(gl_mask); - - glDisable(GL_SCISSOR_TEST); - } - - pgraph_set_surface_dirty(pg, write_color, write_zeta); -} - -void OpenGL_init_pgraph_plugins() -{ - pgraph_draw_arrays = OpenGL_draw_arrays; - pgraph_draw_inline_buffer = OpenGL_draw_inline_buffer; - pgraph_draw_inline_array = OpenGL_draw_inline_array; - pgraph_draw_inline_elements = OpenGL_draw_inline_elements; - pgraph_draw_state_update = OpenGL_draw_state_update; - pgraph_draw_clear = OpenGL_draw_clear; -} - -void pgraph_handle_method(NV2AState *d, - unsigned int subchannel, - unsigned int method, - uint32_t parameter) -{ - unsigned int i; - unsigned int slot; - - PGRAPHState *pg = &d->pgraph; - - bool channel_valid = - d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] & NV_PGRAPH_CTX_CONTROL_CHID; - assert(channel_valid); - - unsigned channel_id = GET_MASK(pg->regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID); - - ContextSurfaces2DState *context_surfaces_2d = &pg->context_surfaces_2d; - ImageBlitState *image_blit = &pg->image_blit; - KelvinState *kelvin = &pg->kelvin; - - assert(subchannel < 8); - - if (method == NV_SET_OBJECT) { - assert(parameter < d->pramin.ramin_size); - uint8_t *obj_ptr = d->pramin.ramin_ptr + parameter; - - uint32_t ctx_1 = ldl_le_p((uint32_t*)obj_ptr); - uint32_t ctx_2 = ldl_le_p((uint32_t*)(obj_ptr+4)); - uint32_t ctx_3 = ldl_le_p((uint32_t*)(obj_ptr+8)); - uint32_t ctx_4 = ldl_le_p((uint32_t*)(obj_ptr+12)); - uint32_t ctx_5 = parameter; - - pg->regs[NV_PGRAPH_CTX_CACHE1 + subchannel * 4] = ctx_1; - pg->regs[NV_PGRAPH_CTX_CACHE2 + subchannel * 4] = ctx_2; - pg->regs[NV_PGRAPH_CTX_CACHE3 + subchannel * 4] = ctx_3; - pg->regs[NV_PGRAPH_CTX_CACHE4 + subchannel * 4] = ctx_4; - pg->regs[NV_PGRAPH_CTX_CACHE5 + subchannel * 4] = ctx_5; - } - - // is this right? - pg->regs[NV_PGRAPH_CTX_SWITCH1] = pg->regs[NV_PGRAPH_CTX_CACHE1 + subchannel * 4]; - pg->regs[NV_PGRAPH_CTX_SWITCH2] = pg->regs[NV_PGRAPH_CTX_CACHE2 + subchannel * 4]; - pg->regs[NV_PGRAPH_CTX_SWITCH3] = pg->regs[NV_PGRAPH_CTX_CACHE3 + subchannel * 4]; - pg->regs[NV_PGRAPH_CTX_SWITCH4] = pg->regs[NV_PGRAPH_CTX_CACHE4 + subchannel * 4]; - pg->regs[NV_PGRAPH_CTX_SWITCH5] = pg->regs[NV_PGRAPH_CTX_CACHE5 + subchannel * 4]; - - uint32_t graphics_class = GET_MASK(pg->regs[NV_PGRAPH_CTX_SWITCH1], - NV_PGRAPH_CTX_SWITCH1_GRCLASS); - - // Logging is slow.. disable for now.. - //pgraph_log_method(subchannel, graphics_class, method, parameter); - - if (subchannel != 0) { - // catches context switching issues on xbox d3d - assert(graphics_class != 0x97); - } - - /* ugly switch for now */ - switch (graphics_class) { - - case NV_CONTEXT_PATTERN: { - switch (method) { - case NV044_SET_MONOCHROME_COLOR0: - pg->regs[NV_PGRAPH_PATT_COLOR0] = parameter; - break; - } - - break; - } - - case NV_CONTEXT_SURFACES_2D: { - switch (method) { - case NV062_SET_OBJECT: - context_surfaces_2d->object_instance = parameter; - break; - case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE: - context_surfaces_2d->dma_image_source = parameter; - break; - case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN: - context_surfaces_2d->dma_image_dest = parameter; - break; - case NV062_SET_COLOR_FORMAT: - context_surfaces_2d->color_format = parameter; - break; - case NV062_SET_PITCH: - context_surfaces_2d->source_pitch = parameter & 0xFFFF; - context_surfaces_2d->dest_pitch = parameter >> 16; - break; - case NV062_SET_OFFSET_SOURCE: - context_surfaces_2d->source_offset = parameter & 0x07FFFFFF; - break; - case NV062_SET_OFFSET_DESTIN: - context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF; - break; - default: - EmuLog(LOG_LEVEL::WARNING, "Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X", method); - } - - break; - } - - case NV_IMAGE_BLIT: { - switch (method) { - case NV09F_SET_OBJECT: - image_blit->object_instance = parameter; - break; - case NV09F_SET_CONTEXT_SURFACES: - image_blit->context_surfaces = parameter; - break; - case NV09F_SET_OPERATION: - image_blit->operation = parameter; - break; - case NV09F_CONTROL_POINT_IN: - image_blit->in_x = parameter & 0xFFFF; - image_blit->in_y = parameter >> 16; - break; - case NV09F_CONTROL_POINT_OUT: - image_blit->out_x = parameter & 0xFFFF; - image_blit->out_y = parameter >> 16; - break; - case NV09F_SIZE: - image_blit->width = parameter & 0xFFFF; - image_blit->height = parameter >> 16; - - /* I guess this kicks it off? */ - if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { - - NV2A_GL_DPRINTF(true, "NV09F_SET_OPERATION_SRCCOPY"); - - ContextSurfaces2DState *context_surfaces = context_surfaces_2d; - assert(context_surfaces->object_instance - == image_blit->context_surfaces); - - unsigned int bytes_per_pixel; - switch (context_surfaces->color_format) { - case NV062_SET_COLOR_FORMAT_LE_Y8: - bytes_per_pixel = 1; - break; - case NV062_SET_COLOR_FORMAT_LE_R5G6B5: - bytes_per_pixel = 2; - break; - case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8: - bytes_per_pixel = 4; - break; - default: - printf("Unknown blit surface format: 0x%x\n", context_surfaces->color_format); - assert(false); - break; - } - - xbaddr source_dma_len, dest_dma_len; - uint8_t *source, *dest; - - source = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_source, - &source_dma_len); - assert(context_surfaces->source_offset < source_dma_len); - source += context_surfaces->source_offset; - - dest = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_dest, - &dest_dma_len); - assert(context_surfaces->dest_offset < dest_dma_len); - dest += context_surfaces->dest_offset; - - NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source - d->vram_ptr, - dest - d->vram_ptr); - - unsigned int y; - for (y = 0; yheight; y++) { - uint8_t *source_row = source - + (image_blit->in_y + y) * context_surfaces->source_pitch - + image_blit->in_x * bytes_per_pixel; - - uint8_t *dest_row = dest - + (image_blit->out_y + y) * context_surfaces->dest_pitch - + image_blit->out_x * bytes_per_pixel; - - memmove(dest_row, source_row, - image_blit->width * bytes_per_pixel); - } - - } else { - assert(false); - } - - break; - default: - EmuLog(LOG_LEVEL::WARNING, "Unknown NV_IMAGE_BLIT Method: 0x%08X", method); - } - break; - } - - case NV_KELVIN_PRIMITIVE: { - switch (method) { - case NV097_SET_OBJECT: - kelvin->object_instance = parameter; - break; - - case NV097_NO_OPERATION: - /* The bios uses nop as a software method call - - * it seems to expect a notify interrupt if the parameter isn't 0. - * According to a nouveau guy it should still be a nop regardless - * of the parameter. It's possible a debug register enables this, - * but nothing obvious sticks out. Weird. - */ - if (parameter != 0) { - assert(!(pg->pending_interrupts & NV_PGRAPH_INTR_ERROR)); - - SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR], - NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id); - SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR], - NV_PGRAPH_TRAPPED_ADDR_SUBCH, subchannel); - SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR], - NV_PGRAPH_TRAPPED_ADDR_MTHD, method); - pg->regs[NV_PGRAPH_TRAPPED_DATA_LOW] = parameter; - pg->regs[NV_PGRAPH_NSOURCE] = NV_PGRAPH_NSOURCE_NOTIFICATION; /* TODO: check this */ - pg->pending_interrupts |= NV_PGRAPH_INTR_ERROR; - - qemu_mutex_unlock(&pg->pgraph_lock); - qemu_mutex_lock_iothread(); - update_irq(d); - qemu_mutex_lock(&pg->pgraph_lock); - qemu_mutex_unlock_iothread(); - - while (pg->pending_interrupts & NV_PGRAPH_INTR_ERROR) { - qemu_cond_wait(&pg->interrupt_cond, &pg->pgraph_lock); - } - } - break; - - case NV097_WAIT_FOR_IDLE: - pgraph_update_surface(d, false, true, true); - break; - - - case NV097_SET_FLIP_READ: - SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D, - parameter); - break; - case NV097_SET_FLIP_WRITE: - SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D, - parameter); - break; - case NV097_SET_FLIP_MODULO: - SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D, - parameter); - break; - case NV097_FLIP_INCREMENT_WRITE: { - NV2A_DPRINTF("flip increment write %d -> ", - GET_MASK(pg->regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_WRITE_3D)); - SET_MASK(pg->regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_WRITE_3D, - (GET_MASK(pg->regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_WRITE_3D) + 1) - % GET_MASK(pg->regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_MODULO_3D)); - NV2A_DPRINTF("%d\n", - GET_MASK(pg->regs[NV_PGRAPH_SURFACE], - NV_PGRAPH_SURFACE_WRITE_3D)); - -#ifdef __APPLE__ - if (glFrameTerminatorGREMEDY) { - glFrameTerminatorGREMEDY(); - } -#endif // __APPLE__ - break; - } - case NV097_FLIP_STALL: - pgraph_update_surface(d, false, true, true); - - - // TODO: Fix this (why does it hang?) - /* while (true) */ { - uint32_t surface = pg->regs[NV_PGRAPH_SURFACE]; - NV2A_DPRINTF("flip stall read: %d, write: %d, modulo: %d\n", - GET_MASK(surface, NV_PGRAPH_SURFACE_READ_3D), - GET_MASK(surface, NV_PGRAPH_SURFACE_WRITE_3D), - GET_MASK(surface, NV_PGRAPH_SURFACE_MODULO_3D)); - - if (GET_MASK(surface, NV_PGRAPH_SURFACE_READ_3D) - != GET_MASK(surface, NV_PGRAPH_SURFACE_WRITE_3D)) { - break; - } - - //qemu_cond_wait(&pg->flip_3d, &pg->lock); - } - - // TODO: Remove this when the AMD crash is solved in vblank_thread - NV2ADevice::UpdateHostDisplay(d); - NV2A_DPRINTF("flip stall done\n"); - break; - - case NV097_SET_CONTEXT_DMA_NOTIFIES: - pg->dma_notifies = parameter; - break; - case NV097_SET_CONTEXT_DMA_A: - pg->dma_a = parameter; - break; - case NV097_SET_CONTEXT_DMA_B: - pg->dma_b = parameter; - break; - case NV097_SET_CONTEXT_DMA_STATE: - pg->dma_state = parameter; - break; - case NV097_SET_CONTEXT_DMA_COLOR: - /* try to get any straggling draws in before the surface's changed :/ */ - pgraph_update_surface(d, false, true, true); - - pg->dma_color = parameter; - break; - case NV097_SET_CONTEXT_DMA_ZETA: - pg->dma_zeta = parameter; - break; - case NV097_SET_CONTEXT_DMA_VERTEX_A: - pg->dma_vertex_a = parameter; - break; - case NV097_SET_CONTEXT_DMA_VERTEX_B: - pg->dma_vertex_b = parameter; - break; - case NV097_SET_CONTEXT_DMA_SEMAPHORE: - pg->dma_semaphore = parameter; - break; - case NV097_SET_CONTEXT_DMA_REPORT: - pg->dma_report = parameter; - break; - - case NV097_SET_SURFACE_CLIP_HORIZONTAL: - pgraph_update_surface(d, false, true, true); - - pg->surface_shape.clip_x = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X); - pg->surface_shape.clip_width = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH); - break; - case NV097_SET_SURFACE_CLIP_VERTICAL: - pgraph_update_surface(d, false, true, true); - - pg->surface_shape.clip_y = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y); - pg->surface_shape.clip_height = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT); - break; - case NV097_SET_SURFACE_FORMAT: - pgraph_update_surface(d, false, true, true); - - pg->surface_shape.color_format = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR); - pg->surface_shape.zeta_format = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA); - pg->surface_type = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE); - pg->surface_shape.anti_aliasing = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING); - pg->surface_shape.log_width = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH); - pg->surface_shape.log_height = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT); - break; - case NV097_SET_SURFACE_PITCH: - pgraph_update_surface(d, false, true, true); - - pg->surface_color.pitch = - GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR); - pg->surface_zeta.pitch = - GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA); - break; - case NV097_SET_SURFACE_COLOR_OFFSET: - pgraph_update_surface(d, false, true, true); - - pg->surface_color.offset = parameter; - break; - case NV097_SET_SURFACE_ZETA_OFFSET: - pgraph_update_surface(d, false, true, true); - - pg->surface_zeta.offset = parameter; - break; - - CASE_8(NV097_SET_COMBINER_ALPHA_ICW, 4) : - slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; - pg->regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter; - break; - - case NV097_SET_COMBINER_SPECULAR_FOG_CW0: - pg->regs[NV_PGRAPH_COMBINESPECFOG0] = parameter; - break; - - case NV097_SET_COMBINER_SPECULAR_FOG_CW1: - pg->regs[NV_PGRAPH_COMBINESPECFOG1] = parameter; - break; - - CASE_4(NV097_SET_TEXTURE_ADDRESS, 64): - slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64; - pg->regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter; - break; - case NV097_SET_CONTROL0: { - pgraph_update_surface(d, false, true, true); - - bool stencil_write_enable = - parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE; - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE, - stencil_write_enable); - - uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT); - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format); - - bool z_perspective = - parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE; - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, - z_perspective); - - int color_space_convert = - GET_MASK(parameter, NV097_SET_CONTROL0_COLOR_SPACE_CONVERT); - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_CSCONVERT, - color_space_convert); - break; - } - - case NV097_SET_FOG_MODE: { - /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */ - unsigned int mode; - switch (parameter) { - case NV097_SET_FOG_MODE_V_LINEAR: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break; - case NV097_SET_FOG_MODE_V_EXP: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break; - case NV097_SET_FOG_MODE_V_EXP2: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break; - case NV097_SET_FOG_MODE_V_EXP_ABS: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break; - case NV097_SET_FOG_MODE_V_EXP2_ABS: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break; - case NV097_SET_FOG_MODE_V_LINEAR_ABS: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break; - default: - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOG_MODE, - mode); - break; - } - case NV097_SET_FOG_GEN_MODE: { - unsigned int mode; - switch (parameter) { - case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break; - case NV097_SET_FOG_GEN_MODE_V_RADIAL: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break; - case NV097_SET_FOG_GEN_MODE_V_PLANAR: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break; - case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break; - case NV097_SET_FOG_GEN_MODE_V_FOG_X: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break; - default: - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGGENMODE, mode); - break; - } - case NV097_SET_FOG_ENABLE: - /* - FIXME: There is also: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGENABLE, - parameter); - */ - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOGENABLE, - parameter); - break; - case NV097_SET_FOG_COLOR: { - /* parameter channels are ABGR, PGRAPH channels are ARGB */ - uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA); - uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE); - uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN); - uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED); - SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_ALPHA, alpha); - SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_RED, red); - SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_GREEN, green); - SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue); - break; - } - case NV097_SET_WINDOW_CLIP_TYPE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE, parameter); - break; - CASE_8(NV097_SET_WINDOW_CLIP_HORIZONTAL, 4): - slot = (method - NV097_SET_WINDOW_CLIP_HORIZONTAL) / 4; - pg->regs[NV_PGRAPH_WINDOWCLIPX0 + slot * 4] = parameter; - break; - CASE_8(NV097_SET_WINDOW_CLIP_VERTICAL, 4): - slot = (method - NV097_SET_WINDOW_CLIP_VERTICAL) / 4; - pg->regs[NV_PGRAPH_WINDOWCLIPY0 + slot * 4] = parameter; - break; - case NV097_SET_ALPHA_TEST_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter); - break; - case NV097_SET_BLEND_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EN, parameter); - break; - case NV097_SET_CULL_FACE_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_CULLENABLE, - parameter); - break; - case NV097_SET_DEPTH_TEST_ENABLE: - // Test-case : Whiplash - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE, - parameter); - break; - case NV097_SET_DITHER_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter); - break; - case NV097_SET_LIGHTING_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING, - parameter); - break; - case NV097_SET_SKIN_MODE: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN, - parameter); - break; - case NV097_SET_STENCIL_TEST_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter); - break; - case NV097_SET_POLY_OFFSET_POINT_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter); - break; - case NV097_SET_POLY_OFFSET_LINE_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter); - break; - case NV097_SET_POLY_OFFSET_FILL_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter); - break; - case NV097_SET_ALPHA_FUNC: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF); - break; - case NV097_SET_ALPHA_REF: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHAREF, parameter); - break; - case NV097_SET_BLEND_FUNC_SFACTOR: { - unsigned int factor; - switch (parameter) { - case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO: - factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE: - factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; - default: - fprintf(stderr, "Unknown blend source factor: 0x%x\n", parameter); - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_SFACTOR, factor); - - break; - } - - case NV097_SET_BLEND_FUNC_DFACTOR: { - unsigned int factor; - switch (parameter) { - case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO: - factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE: - factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; - default: - fprintf(stderr, "Unknown blend destination factor: 0x%x\n", parameter); - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_DFACTOR, factor); - - break; - } - - case NV097_SET_BLEND_COLOR: - pg->regs[NV_PGRAPH_BLENDCOLOR] = parameter; - break; - - case NV097_SET_BLEND_EQUATION: { - unsigned int equation; - switch (parameter) { - case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT: - equation = 0; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT: - equation = 1; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_ADD: - equation = 2; break; - case NV097_SET_BLEND_EQUATION_V_MIN: - equation = 3; break; - case NV097_SET_BLEND_EQUATION_V_MAX: - equation = 4; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED: - equation = 5; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED: - equation = 6; break; - default: - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EQN, equation); - - break; - } - - case NV097_SET_DEPTH_FUNC: - // Test-case : Whiplash - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC, - parameter & 0xF); - break; - - case NV097_SET_COLOR_MASK: { - pg->surface_color.write_enabled_cache |= pgraph_get_color_write_enabled(pg); - - bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE; - bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE; - bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE; - bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE; - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha); - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red); - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green); - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue); - break; - } - case NV097_SET_DEPTH_MASK: - pg->surface_zeta.write_enabled_cache |= pgraph_get_zeta_write_enabled(pg); - - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter); - break; - case NV097_SET_STENCIL_MASK: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter); - break; - case NV097_SET_STENCIL_FUNC: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF); - break; - case NV097_SET_STENCIL_FUNC_REF: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter); - break; - case NV097_SET_STENCIL_FUNC_MASK: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter); - break; - case NV097_SET_STENCIL_OP_FAIL: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], - NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL, - kelvin_map_stencil_op(parameter)); - break; - case NV097_SET_STENCIL_OP_ZFAIL: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], - NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL, - kelvin_map_stencil_op(parameter)); - break; - case NV097_SET_STENCIL_OP_ZPASS: - SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], - NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS, - kelvin_map_stencil_op(parameter)); - break; - - case NV097_SET_POLYGON_OFFSET_SCALE_FACTOR: - pg->regs[NV_PGRAPH_ZOFFSETFACTOR] = parameter; - break; - case NV097_SET_POLYGON_OFFSET_BIAS: - pg->regs[NV_PGRAPH_ZOFFSETBIAS] = parameter; - break; - case NV097_SET_FRONT_POLYGON_MODE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_FRONTFACEMODE, - kelvin_map_polygon_mode(parameter)); - break; - case NV097_SET_BACK_POLYGON_MODE: - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_BACKFACEMODE, - kelvin_map_polygon_mode(parameter)); - break; - case NV097_SET_CLIP_MIN: - pg->regs[NV_PGRAPH_ZCLIPMIN] = parameter; - break; - case NV097_SET_CLIP_MAX: - pg->regs[NV_PGRAPH_ZCLIPMAX] = parameter; - break; - case NV097_SET_CULL_FACE: { - unsigned int face; - switch (parameter) { - case NV097_SET_CULL_FACE_V_FRONT: - face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break; - case NV097_SET_CULL_FACE_V_BACK: - face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break; - case NV097_SET_CULL_FACE_V_FRONT_AND_BACK: - face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break; - default: - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_CULLCTRL, - face); - break; - } - case NV097_SET_FRONT_FACE: { - bool ccw; - switch (parameter) { - case NV097_SET_FRONT_FACE_V_CW: - ccw = false; break; - case NV097_SET_FRONT_FACE_V_CCW: - ccw = true; break; - default: - fprintf(stderr, "Unknown front face: 0x%x\n", parameter); - assert(false); - break; - } - SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_FRONTFACE, - ccw ? 1 : 0); - break; - } - case NV097_SET_NORMALIZATION_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], - NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, - parameter); - break; - - case NV097_SET_LIGHT_ENABLE_MASK: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], - NV_PGRAPH_CSV0_D_LIGHTS, - parameter); - break; - - CASE_4(NV097_SET_TEXGEN_S, 16) : { - slot = (method - NV097_SET_TEXGEN_S) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S - : NV_PGRAPH_CSV1_A_T0_S; - SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 0)); - break; - } - CASE_4(NV097_SET_TEXGEN_T, 16) : { - slot = (method - NV097_SET_TEXGEN_T) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T - : NV_PGRAPH_CSV1_A_T0_T; - SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 1)); - break; - } - CASE_4(NV097_SET_TEXGEN_R, 16) : { - slot = (method - NV097_SET_TEXGEN_R) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R - : NV_PGRAPH_CSV1_A_T0_R; - SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 2)); - break; - } - CASE_4(NV097_SET_TEXGEN_Q, 16) : { - slot = (method - NV097_SET_TEXGEN_Q) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q - : NV_PGRAPH_CSV1_A_T0_Q; - SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 3)); - break; - } - CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) : - slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4; - pg->texture_matrix_enable[slot] = parameter; - break; - - CASE_16(NV097_SET_PROJECTION_MATRIX, 4) : { - slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; - // pg->projection_matrix[slot] = *(float*)¶meter; - unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4; - pg->vsh_constants[row][slot % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } - - CASE_64(NV097_SET_MODEL_VIEW_MATRIX, 4) : { - slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; - unsigned int matnum = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } - - CASE_64(NV097_SET_INVERSE_MODEL_VIEW_MATRIX, 4) : { - slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; - unsigned int matnum = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } - - CASE_16(NV097_SET_COMPOSITE_MATRIX, 4) : { - slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; - unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4; - pg->vsh_constants[row][slot % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } - - CASE_64(NV097_SET_TEXTURE_MATRIX, 4) : { - slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; - unsigned int tex = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } - - CASE_3(NV097_SET_FOG_PARAMS, 4) : - slot = (method - NV097_SET_FOG_PARAMS) / 4; - pg->regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter; - /* Cxbx note: slot = 2 is right after slot = 1 */ - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; - break; - - /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ - CASE_64(NV097_SET_TEXGEN_PLANE_S, 4) : { - slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; - unsigned int tex = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4; - pg->vsh_constants[row][entry % 4] = parameter; - pg->vsh_constants_dirty[row] = true; - break; - } - - case NV097_SET_TEXGEN_VIEW_MODEL: - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, - parameter); - break; - - CASE_4(NV097_SET_FOG_PLANE, 4): - slot = (method - NV097_SET_FOG_PLANE) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; - break; - - CASE_3(NV097_SET_SCENE_AMBIENT_COLOR, 4): - slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; - // ?? - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; - break; - - CASE_4(NV097_SET_VIEWPORT_OFFSET, 4): - slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; - break; - - CASE_4(NV097_SET_EYE_POSITION, 4): - slot = (method - NV097_SET_EYE_POSITION) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; - break; - - CASE_8(NV097_SET_COMBINER_FACTOR0, 4): - slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; - pg->regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter; - break; - - CASE_8(NV097_SET_COMBINER_FACTOR1, 4): - slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; - pg->regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter; - break; - - CASE_8(NV097_SET_COMBINER_ALPHA_OCW, 4): - slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; - pg->regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter; - break; - - CASE_8(NV097_SET_COMBINER_COLOR_ICW, 4): - slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; - pg->regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter; - break; - - CASE_4(NV097_SET_VIEWPORT_SCALE, 4): - slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; - pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; - pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; - break; - - CASE_32(NV097_SET_TRANSFORM_PROGRAM, 4) : { - - slot = (method - NV097_SET_TRANSFORM_PROGRAM) / 4; - - int program_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], - NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR); - - assert(program_load < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); - pg->program_data[program_load][slot % 4] = parameter; - - if (slot % 4 == 3) { - SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], - NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, program_load + 1); - } - - break; - } - - CASE_32(NV097_SET_TRANSFORM_CONSTANT, 4): { - - slot = (method - NV097_SET_TRANSFORM_CONSTANT) / 4; - - int const_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], - NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR); - - assert(const_load < NV2A_VERTEXSHADER_CONSTANTS); - // VertexShaderConstant *vsh_constant = &pg->vsh_constants[const_load]; - pg->vsh_constants_dirty[const_load] |= - (parameter != pg->vsh_constants[const_load][slot%4]); - pg->vsh_constants[const_load][slot%4] = parameter; - - if (slot % 4 == 3) { - SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], - NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, const_load+1); - } - break; - } - - CASE_3(NV097_SET_VERTEX3F, 4) : { - slot = (method - NV097_SET_VERTEX3F) / 4; - VertexAttribute *vertex_attribute = - &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; - pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); - vertex_attribute->inline_value[slot] = *(float*)¶meter; - vertex_attribute->inline_value[3] = 1.0f; - if (slot == 2) { - pgraph_finish_inline_buffer_vertex(pg); - } - break; - } - - /* Handles NV097_SET_BACK_LIGHT_* */ - CASE_128(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): { - slot = (method - NV097_SET_BACK_LIGHT_AMBIENT_COLOR) / 4; - unsigned int part = NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4 + slot % 16; - slot /= 16; /* [Light index] */ - assert(slot < 8); - switch(part * 4) { - CASE_3(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): - part -= NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4; - pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6][part] = parameter; - pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6] = true; - break; - CASE_3(NV097_SET_BACK_LIGHT_DIFFUSE_COLOR, 4): - part -= NV097_SET_BACK_LIGHT_DIFFUSE_COLOR / 4; - pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6][part] = parameter; - pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6] = true; - break; - CASE_3(NV097_SET_BACK_LIGHT_SPECULAR_COLOR, 4): - part -= NV097_SET_BACK_LIGHT_SPECULAR_COLOR / 4; - pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6][part] = parameter; - pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6] = true; - break; - default: - assert(false); - break; - } - break; - } - /* Handles all the light source props except for NV097_SET_BACK_LIGHT_* */ - CASE_256(NV097_SET_LIGHT_AMBIENT_COLOR, 4): { - slot = (method - NV097_SET_LIGHT_AMBIENT_COLOR) / 4; - unsigned int part = NV097_SET_LIGHT_AMBIENT_COLOR / 4 + slot % 32; - slot /= 32; /* [Light index] */ - assert(slot < 8); - switch(part * 4) { - CASE_3(NV097_SET_LIGHT_AMBIENT_COLOR, 4): - part -= NV097_SET_LIGHT_AMBIENT_COLOR / 4; - pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6][part] = parameter; - pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6] = true; - break; - CASE_3(NV097_SET_LIGHT_DIFFUSE_COLOR, 4): - part -= NV097_SET_LIGHT_DIFFUSE_COLOR / 4; - pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6][part] = parameter; - pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6] = true; - break; - CASE_3(NV097_SET_LIGHT_SPECULAR_COLOR, 4): - part -= NV097_SET_LIGHT_SPECULAR_COLOR / 4; - pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6][part] = parameter; - pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6] = true; - break; - case NV097_SET_LIGHT_LOCAL_RANGE: - pg->ltc1[NV_IGRAPH_XF_LTC1_r0 + slot][0] = parameter; - pg->ltc1_dirty[NV_IGRAPH_XF_LTC1_r0 + slot] = true; - break; - CASE_3(NV097_SET_LIGHT_INFINITE_HALF_VECTOR, 4): - part -= NV097_SET_LIGHT_INFINITE_HALF_VECTOR / 4; - pg->light_infinite_half_vector[slot][part] = *(float*)¶meter; - break; - CASE_3(NV097_SET_LIGHT_INFINITE_DIRECTION, 4): - part -= NV097_SET_LIGHT_INFINITE_DIRECTION / 4; - pg->light_infinite_direction[slot][part] = *(float*)¶meter; - break; - CASE_3(NV097_SET_LIGHT_SPOT_FALLOFF, 4): - part -= NV097_SET_LIGHT_SPOT_FALLOFF / 4; - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2][part] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2] = true; - break; - CASE_4(NV097_SET_LIGHT_SPOT_DIRECTION, 4): - part -= NV097_SET_LIGHT_SPOT_DIRECTION / 4; - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2][part] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2] = true; - break; - CASE_3(NV097_SET_LIGHT_LOCAL_POSITION, 4): - part -= NV097_SET_LIGHT_LOCAL_POSITION / 4; - pg->light_local_position[slot][part] = *(float*)¶meter; - break; - CASE_3(NV097_SET_LIGHT_LOCAL_ATTENUATION, 4): - part -= NV097_SET_LIGHT_LOCAL_ATTENUATION / 4; - pg->light_local_attenuation[slot][part] = *(float*)¶meter; - break; - default: - assert(false); - break; - } - break; - } - - CASE_4(NV097_SET_VERTEX4F, 4): { - slot = (method - NV097_SET_VERTEX4F) / 4; - VertexAttribute *vertex_attribute = - &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; - pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); - vertex_attribute->inline_value[slot] = *(float*)¶meter; - if (slot == 3) { - pgraph_finish_inline_buffer_vertex(pg); - } - break; - } - - CASE_16(NV097_SET_VERTEX_DATA_ARRAY_FORMAT, 4): { - - slot = (method - NV097_SET_VERTEX_DATA_ARRAY_FORMAT) / 4; - VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; - - vertex_attribute->format = - GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE); - vertex_attribute->count = - GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE); - vertex_attribute->stride = - GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE); - - NV2A_DPRINTF("vertex data array format=%d, count=%d, stride=%d\n", - vertex_attribute->format, - vertex_attribute->count, - vertex_attribute->stride); - - vertex_attribute->gl_count = vertex_attribute->count; - - switch (vertex_attribute->format) { - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D: - vertex_attribute->gl_type = GL_UNSIGNED_BYTE; - vertex_attribute->gl_normalize = GL_TRUE; - vertex_attribute->size = 1; - assert(vertex_attribute->count == 4); - // https://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt - vertex_attribute->gl_count = GL_BGRA; - vertex_attribute->needs_conversion = false; - break; - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL: - vertex_attribute->gl_type = GL_UNSIGNED_BYTE; - vertex_attribute->gl_normalize = GL_TRUE; - vertex_attribute->size = 1; - vertex_attribute->needs_conversion = false; - break; - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1: - vertex_attribute->gl_type = GL_SHORT; - vertex_attribute->gl_normalize = GL_TRUE; - vertex_attribute->size = 2; - vertex_attribute->needs_conversion = false; - break; - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F: - vertex_attribute->gl_type = GL_FLOAT; - vertex_attribute->gl_normalize = GL_FALSE; - vertex_attribute->size = 4; - vertex_attribute->needs_conversion = false; - break; - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K: - vertex_attribute->gl_type = GL_SHORT; - vertex_attribute->gl_normalize = GL_FALSE; - vertex_attribute->size = 2; - vertex_attribute->needs_conversion = false; - break; - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: - /* 3 signed, normalized components packed in 32-bits. (11,11,10) */ - vertex_attribute->size = 4; - vertex_attribute->gl_type = GL_FLOAT; - vertex_attribute->gl_normalize = GL_FALSE; - vertex_attribute->needs_conversion = true; - vertex_attribute->converted_size = sizeof(float); - vertex_attribute->converted_count = 3 * vertex_attribute->count; - break; - default: - fprintf(stderr, "Unknown vertex type: 0x%x\n", vertex_attribute->format); - assert(false); - break; - } - - if (vertex_attribute->needs_conversion) { - vertex_attribute->converted_elements = 0; - } else { - if (vertex_attribute->converted_buffer) { - g_free(vertex_attribute->converted_buffer); - vertex_attribute->converted_buffer = NULL; - } - } - - break; - } - - CASE_16(NV097_SET_VERTEX_DATA_ARRAY_OFFSET, 4): { - - slot = (method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4; - - pg->vertex_attributes[slot].dma_select = - parameter & 0x80000000; - pg->vertex_attributes[slot].offset = - parameter & 0x7fffffff; - - pg->vertex_attributes[slot].converted_elements = 0; - - break; - } - - case NV097_SET_LOGIC_OP_ENABLE: - SET_MASK(pg->regs[NV_PGRAPH_BLEND], - NV_PGRAPH_BLEND_LOGICOP_ENABLE, parameter); - break; - - case NV097_SET_LOGIC_OP: - SET_MASK(pg->regs[NV_PGRAPH_BLEND], - NV_PGRAPH_BLEND_LOGICOP, parameter & 0xF); - break; - - case NV097_CLEAR_REPORT_VALUE: - - /* FIXME: Does this have a value in parameter? Also does this (also?) modify - * the report memory block? - */ - if (pg->gl_zpass_pixel_count_query_count) { - if (pg->opengl_enabled) { - glDeleteQueries(pg->gl_zpass_pixel_count_query_count, - pg->gl_zpass_pixel_count_queries); - } - pg->gl_zpass_pixel_count_query_count = 0; - } - pg->zpass_pixel_count_result = 0; - - break; - - case NV097_SET_ZPASS_PIXEL_COUNT_ENABLE: - pg->zpass_pixel_count_enable = parameter; - break; - - case NV097_GET_REPORT: { - /* FIXME: This was first intended to be watchpoint-based. However, - * qemu / kvm only supports virtual-address watchpoints. - * This'll do for now, but accuracy and performance with other - * approaches could be better - */ - uint8_t type = GET_MASK(parameter, NV097_GET_REPORT_TYPE); - assert(type == NV097_GET_REPORT_TYPE_ZPASS_PIXEL_CNT); - hwaddr offset = GET_MASK(parameter, NV097_GET_REPORT_OFFSET); - - uint64_t timestamp = 0x0011223344556677; /* FIXME: Update timestamp?! */ - uint32_t done = 0; - - if (pg->opengl_enabled) { - /* FIXME: Multisampling affects this (both: OGL and Xbox GPU), - * not sure if CLEARs also count - */ - /* FIXME: What about clipping regions etc? */ - for(i = 0; i < pg->gl_zpass_pixel_count_query_count; i++) { - GLuint gl_query_result; - glGetQueryObjectuiv(pg->gl_zpass_pixel_count_queries[i], - GL_QUERY_RESULT, - &gl_query_result); - pg->zpass_pixel_count_result += gl_query_result; - } - if (pg->gl_zpass_pixel_count_query_count) { - glDeleteQueries(pg->gl_zpass_pixel_count_query_count, - pg->gl_zpass_pixel_count_queries); - } - pg->gl_zpass_pixel_count_query_count = 0; - - hwaddr report_dma_len; - uint8_t *report_data = (uint8_t*)nv_dma_map(d, pg->dma_report, - &report_dma_len); - assert(offset < report_dma_len); - report_data += offset; - - stq_le_p((uint64_t*)&report_data[0], timestamp); - stl_le_p((uint32_t*)&report_data[8], pg->zpass_pixel_count_result); - stl_le_p((uint32_t*)&report_data[12], done); - } - - break; - } - - CASE_3(NV097_SET_EYE_DIRECTION, 4): - slot = (method - NV097_SET_EYE_DIRECTION) / 4; - pg->ltctxa[NV_IGRAPH_XF_LTCTXA_EYED][slot] = parameter; - pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_EYED] = true; - break; - - case NV097_SET_BEGIN_END: { - uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0]; - uint32_t control_1 = pg->regs[NV_PGRAPH_CONTROL_1]; - - bool depth_test = control_0 - & NV_PGRAPH_CONTROL_0_ZENABLE; - bool stencil_test = control_1 - & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE; - - if (parameter == NV097_SET_BEGIN_END_OP_END) { - - if (pg->draw_arrays_length) { - - NV2A_GL_DPRINTF(false, "Draw Arrays"); - - assert(pg->inline_buffer_length == 0); - assert(pg->inline_array_length == 0); - assert(pg->inline_elements_length == 0); - - if (pgraph_draw_arrays != nullptr) { - pgraph_draw_arrays(d); - } - } else if (pg->inline_buffer_length) { - - NV2A_GL_DPRINTF(false, "Inline Buffer"); - - assert(pg->draw_arrays_length == 0); - assert(pg->inline_array_length == 0); - assert(pg->inline_elements_length == 0); - - if (pgraph_draw_inline_buffer != nullptr) { - pgraph_draw_inline_buffer(d); - } - } else if (pg->inline_array_length) { - - NV2A_GL_DPRINTF(false, "Inline Array"); - - assert(pg->draw_arrays_length == 0); - assert(pg->inline_buffer_length == 0); - assert(pg->inline_elements_length == 0); - - if (pgraph_draw_inline_array != nullptr) { - pgraph_draw_inline_array(d); - } - } else if (pg->inline_elements_length) { - - NV2A_GL_DPRINTF(false, "Inline Elements"); - - assert(pg->draw_arrays_length == 0); - assert(pg->inline_buffer_length == 0); - assert(pg->inline_array_length == 0); - - if (pgraph_draw_inline_elements != nullptr) { - pgraph_draw_inline_elements(d); - } - } else { - NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END"); - assert(false); - } - } else { - - assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON); - - pg->primitive_mode = parameter; - - if (pgraph_draw_state_update != nullptr) { - pgraph_draw_state_update(d); - } - - pg->inline_elements_length = 0; - pg->inline_array_length = 0; - pg->inline_buffer_length = 0; - pg->draw_arrays_length = 0; - pg->draw_arrays_max_count = 0; - } - - pgraph_set_surface_dirty(pg, true, depth_test || stencil_test); - break; - } - CASE_4(NV097_SET_TEXTURE_OFFSET, 64): - slot = (method - NV097_SET_TEXTURE_OFFSET) / 64; - pg->regs[NV_PGRAPH_TEXOFFSET0 + slot * 4] = parameter; - pg->texture_dirty[slot] = true; - break; - CASE_4(NV097_SET_TEXTURE_FORMAT, 64): { - slot = (method - NV097_SET_TEXTURE_FORMAT) / 64; - - bool dma_select = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CONTEXT_DMA) == 2; - bool cubemap = - parameter & NV097_SET_TEXTURE_FORMAT_CUBEMAP_ENABLE; - bool border_source = - parameter & NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE; - unsigned int dimensionality = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_DIMENSIONALITY); - unsigned int color_format = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_COLOR); - unsigned int levels = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_MIPMAP_LEVELS); - unsigned int log_width = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_U); - unsigned int log_height = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_V); - unsigned int log_depth = - GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P); - - uint32_t *reg = &pg->regs[NV_PGRAPH_TEXFMT0 + slot * 4]; - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CONTEXT_DMA, dma_select); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE, cubemap); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BORDER_SOURCE, border_source); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_DIMENSIONALITY, dimensionality); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_COLOR, color_format); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS, levels); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_U, log_width); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_V, log_height); - SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_P, log_depth); - - pg->texture_dirty[slot] = true; - break; - } - CASE_4(NV097_SET_TEXTURE_CONTROL0, 64): - slot = (method - NV097_SET_TEXTURE_CONTROL0) / 64; - pg->regs[NV_PGRAPH_TEXCTL0_0 + slot*4] = parameter; - break; - CASE_4(NV097_SET_TEXTURE_CONTROL1, 64): - slot = (method - NV097_SET_TEXTURE_CONTROL1) / 64; - pg->regs[NV_PGRAPH_TEXCTL1_0 + slot*4] = parameter; - break; - CASE_4(NV097_SET_TEXTURE_FILTER, 64): - slot = (method - NV097_SET_TEXTURE_FILTER) / 64; - pg->regs[NV_PGRAPH_TEXFILTER0 + slot * 4] = parameter; - break; - CASE_4(NV097_SET_TEXTURE_IMAGE_RECT, 64): - slot = (method - NV097_SET_TEXTURE_IMAGE_RECT) / 64; - pg->regs[NV_PGRAPH_TEXIMAGERECT0 + slot * 4] = parameter; - pg->texture_dirty[slot] = true; - break; - CASE_4(NV097_SET_TEXTURE_PALETTE, 64): { - slot = (method - NV097_SET_TEXTURE_PALETTE) / 64; - - bool dma_select = - GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_CONTEXT_DMA) == 1; - unsigned int length = - GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_LENGTH); - unsigned int offset = - GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_OFFSET); - - uint32_t *reg = &pg->regs[NV_PGRAPH_TEXPALETTE0 + slot * 4]; - SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA, dma_select); - SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_LENGTH, length); - SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_OFFSET, offset); - - pg->texture_dirty[slot] = true; - break; - } - - CASE_4(NV097_SET_TEXTURE_BORDER_COLOR, 64): - slot = (method - NV097_SET_TEXTURE_BORDER_COLOR) / 64; - pg->regs[NV_PGRAPH_BORDERCOLOR0 + slot * 4] = parameter; - break; - CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x0, 64): - CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x4, 64): - CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x8, 64): - CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0xc, 64): - slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_MAT) / 4; - assert((slot / 16) > 0); - slot -= 16; - pg->bump_env_matrix[slot / 16][slot % 4] = *(float*)¶meter; - break; - - CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE, 64): - slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE) / 64; - assert(slot > 0); - slot--; - pg->regs[NV_PGRAPH_BUMPSCALE1 + slot * 4] = parameter; - break; - CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET, 64): - slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET) / 64; - assert(slot > 0); - slot--; - pg->regs[NV_PGRAPH_BUMPOFFSET1 + slot * 4] = parameter; - break; - - case NV097_ARRAY_ELEMENT16: - //LOG_TEST_CASE("NV2A_VB_ELEMENT_U16"); - // Test-case : Turok (in main menu) - // Test-case : Hunter Redeemer - // Test-case : Otogi (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/pull/1113#issuecomment-385593814) - assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); - pg->inline_elements[ - pg->inline_elements_length++] = parameter & 0xFFFF; - pg->inline_elements[ - pg->inline_elements_length++] = parameter >> 16; - break; - case NV097_ARRAY_ELEMENT32: - //LOG_TEST_CASE("NV2A_VB_ELEMENT_U32"); - // Test-case : Turok (in main menu) - assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); - pg->inline_elements[ - pg->inline_elements_length++] = parameter; - break; - case NV097_DRAW_ARRAYS: { - - unsigned int start = GET_MASK(parameter, NV097_DRAW_ARRAYS_START_INDEX); - unsigned int count = GET_MASK(parameter, NV097_DRAW_ARRAYS_COUNT)+1; - - pg->draw_arrays_max_count = MAX(pg->draw_arrays_max_count, start + count); - - assert(pg->draw_arrays_length < ARRAY_SIZE(pg->gl_draw_arrays_start)); - - /* Attempt to connect primitives */ - if (pg->draw_arrays_length > 0) { - unsigned int last_start = - pg->gl_draw_arrays_start[pg->draw_arrays_length - 1]; - GLsizei* last_count = - &pg->gl_draw_arrays_count[pg->draw_arrays_length - 1]; - if (start == (last_start + *last_count)) { - *last_count += count; - break; - } - } - - pg->gl_draw_arrays_start[pg->draw_arrays_length] = start; - pg->gl_draw_arrays_count[pg->draw_arrays_length] = count; - pg->draw_arrays_length++; - break; - } - case NV097_INLINE_ARRAY: - assert(pg->inline_array_length < NV2A_MAX_BATCH_LENGTH); - pg->inline_array[ - pg->inline_array_length++] = parameter; - break; - CASE_3(NV097_SET_EYE_VECTOR, 4): - slot = (method - NV097_SET_EYE_VECTOR) / 4; - pg->regs[NV_PGRAPH_EYEVEC0 + slot * 4] = parameter; - break; - - CASE_32(NV097_SET_VERTEX_DATA2F_M, 4): { - slot = (method - NV097_SET_VERTEX_DATA2F_M) / 4; - unsigned int part = slot % 2; - slot /= 2; - VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; - pgraph_allocate_inline_buffer_vertices(pg, slot); - vertex_attribute->inline_value[part] = *(float*)¶meter; - /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ - vertex_attribute->inline_value[2] = 0.0f; - vertex_attribute->inline_value[3] = 1.0f; - if ((slot == 0) && (part == 1)) { - pgraph_finish_inline_buffer_vertex(pg); - } - break; - } - CASE_64(NV097_SET_VERTEX_DATA4F_M, 4): { - slot = (method - NV097_SET_VERTEX_DATA4F_M) / 4; - unsigned int part = slot % 4; - slot /= 4; - VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; - pgraph_allocate_inline_buffer_vertices(pg, slot); - vertex_attribute->inline_value[part] = *(float*)¶meter; - if ((slot == 0) && (part == 3)) { - pgraph_finish_inline_buffer_vertex(pg); - } - break; - } - CASE_16(NV097_SET_VERTEX_DATA2S, 4): { - slot = (method - NV097_SET_VERTEX_DATA2S) / 4; - assert(false); /* FIXME: Untested! */ - VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; - pgraph_allocate_inline_buffer_vertices(pg, slot); - /* FIXME: Is mapping to [-1,+1] correct? */ - vertex_attribute->inline_value[0] = ((int16_t)(parameter & 0xFFFF) * 2.0f + 1) - / 65535.0f; - vertex_attribute->inline_value[1] = ((int16_t)(parameter >> 16) * 2.0f + 1) - / 65535.0f; - /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ - vertex_attribute->inline_value[2] = 0.0f; - vertex_attribute->inline_value[3] = 1.0f; - if (slot == 0) { - pgraph_finish_inline_buffer_vertex(pg); - assert(false); /* FIXME: Untested */ - } - break; - } - CASE_16(NV097_SET_VERTEX_DATA4UB, 4) : { - slot = (method - NV097_SET_VERTEX_DATA4UB) / 4; - VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; - pgraph_allocate_inline_buffer_vertices(pg, slot); - vertex_attribute->inline_value[0] = (parameter & 0xFF) / 255.0f; - vertex_attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f; - vertex_attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f; - vertex_attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f; - if (slot == 0) { - pgraph_finish_inline_buffer_vertex(pg); - assert(false); /* FIXME: Untested */ - } - break; - } - CASE_32(NV097_SET_VERTEX_DATA4S_M, 4) : { - slot = (method - NV097_SET_VERTEX_DATA4S_M) / 4; - unsigned int part = slot % 2; - slot /= 2; - assert(false); /* FIXME: Untested! */ - VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; - pgraph_allocate_inline_buffer_vertices(pg, slot); - /* FIXME: Is mapping to [-1,+1] correct? */ - vertex_attribute->inline_value[part * 2 + 0] = ((int16_t)(parameter & 0xFFFF) - * 2.0f + 1) / 65535.0f; - vertex_attribute->inline_value[part * 2 + 1] = ((int16_t)(parameter >> 16) - * 2.0f + 1) / 65535.0f; - if ((slot == 0) && (part == 1)) { - pgraph_finish_inline_buffer_vertex(pg); - assert(false); /* FIXME: Untested */ - } - break; - } - case NV097_SET_SEMAPHORE_OFFSET: - pg->regs[NV_PGRAPH_SEMAPHOREOFFSET] = parameter; - break; - case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: { - pgraph_update_surface(d, false, true, true); - - //qemu_mutex_unlock(&pg->pgraph_lock); - //qemu_mutex_lock_iothread(); - - uint32_t semaphore_offset = pg->regs[NV_PGRAPH_SEMAPHOREOFFSET]; - - xbaddr semaphore_dma_len; - uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, pg->dma_semaphore, - &semaphore_dma_len); - assert(semaphore_offset < semaphore_dma_len); - semaphore_data += semaphore_offset; - - stl_le_p((uint32_t*)semaphore_data, parameter); - - //qemu_mutex_lock(&pg->pgraph_lock); - //qemu_mutex_unlock_iothread(); - - break; - } - case NV097_SET_ZSTENCIL_CLEAR_VALUE: - pg->regs[NV_PGRAPH_ZSTENCILCLEARVALUE] = parameter; - break; - - case NV097_SET_COLOR_CLEAR_VALUE: - pg->regs[NV_PGRAPH_COLORCLEARVALUE] = parameter; - break; - - case NV097_CLEAR_SURFACE: { - pg->clear_surface = parameter; - if (pgraph_draw_clear != nullptr) { - pgraph_draw_clear(d); - } - break; - } - - case NV097_SET_CLEAR_RECT_HORIZONTAL: - pg->regs[NV_PGRAPH_CLEARRECTX] = parameter; - break; - case NV097_SET_CLEAR_RECT_VERTICAL: - pg->regs[NV_PGRAPH_CLEARRECTY] = parameter; - break; - - CASE_2(NV097_SET_SPECULAR_FOG_FACTOR, 4) : - slot = (method - NV097_SET_SPECULAR_FOG_FACTOR) / 4; - pg->regs[NV_PGRAPH_SPECFOGFACTOR0 + slot * 4] = parameter; - break; - - case NV097_SET_SHADER_CLIP_PLANE_MODE: - pg->regs[NV_PGRAPH_SHADERCLIPMODE] = parameter; - break; - - CASE_8(NV097_SET_COMBINER_COLOR_OCW, 4) : - slot = (method - NV097_SET_COMBINER_COLOR_OCW) / 4; - pg->regs[NV_PGRAPH_COMBINECOLORO0 + slot * 4] = parameter; - break; - - case NV097_SET_COMBINER_CONTROL: - pg->regs[NV_PGRAPH_COMBINECTL] = parameter; - break; - - case NV097_SET_SHADOW_ZSLOPE_THRESHOLD: - pg->regs[NV_PGRAPH_SHADOWZSLOPETHRESHOLD] = parameter; - assert(parameter == 0x7F800000); /* FIXME: Unimplemented */ - break; - - case NV097_SET_SHADER_STAGE_PROGRAM: - pg->regs[NV_PGRAPH_SHADERPROG] = parameter; - break; - - case NV097_SET_SHADER_OTHER_STAGE_INPUT: - pg->regs[NV_PGRAPH_SHADERCTL] = parameter; - break; - - case NV097_SET_TRANSFORM_EXECUTION_MODE: - // Test-case : Whiplash - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_MODE, - GET_MASK(parameter, - NV097_SET_TRANSFORM_EXECUTION_MODE_MODE)); - SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_RANGE_MODE, - GET_MASK(parameter, - NV097_SET_TRANSFORM_EXECUTION_MODE_RANGE_MODE)); - break; - case NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN: - // Test-case : Whiplash - pg->enable_vertex_program_write = parameter; - break; - case NV097_SET_TRANSFORM_PROGRAM_LOAD: - assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); - SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], - NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, parameter); - break; - case NV097_SET_TRANSFORM_PROGRAM_START: - assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); - SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], - NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START, parameter); - break; - case NV097_SET_TRANSFORM_CONSTANT_LOAD: - assert(parameter < NV2A_VERTEXSHADER_CONSTANTS); - SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], - NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, parameter); - NV2A_DPRINTF("load to %d\n", parameter); - break; - - case NV097_SET_FLAT_SHADE_OP: - assert(parameter <= 1); - // TODO : value & 1 = first/last? vertex selection for glShaderMode(GL_FLAT) - break; - default: - NV2A_GL_DPRINTF(true, " unhandled (0x%02x 0x%08x)", - graphics_class, method); - break; - } - break; - } - - default: - NV2A_GL_DPRINTF(true, "Unknown Graphics Class/Method 0x%08X/0x%08X", - graphics_class, method); - break; - } - -} - -static void pgraph_switch_context(NV2AState *d, unsigned int channel_id) -{ - bool channel_valid = - d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] & NV_PGRAPH_CTX_CONTROL_CHID; - unsigned pgraph_channel_id = GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID); - // Cxbx Note : This isn't present in xqemu / OpenXbox : d->pgraph.pgraph_lock.lock(); - bool valid = channel_valid && pgraph_channel_id == channel_id; - if (!valid) { - SET_MASK(d->pgraph.regs[NV_PGRAPH_TRAPPED_ADDR], - NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id); - - NV2A_DPRINTF("pgraph switching to ch %d\n", channel_id); - - /* TODO: hardware context switching */ - assert(!(d->pgraph.regs[NV_PGRAPH_DEBUG_3] - & NV_PGRAPH_DEBUG_3_HW_CONTEXT_SWITCH)); - - qemu_mutex_unlock(&d->pgraph.pgraph_lock); - qemu_mutex_lock_iothread(); - d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; // TODO : Should this be done before unlocking pgraph_lock? - update_irq(d); - - qemu_mutex_lock(&d->pgraph.pgraph_lock); - qemu_mutex_unlock_iothread(); - - // wait for the interrupt to be serviced - while (d->pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) { - qemu_cond_wait(&d->pgraph.interrupt_cond, &d->pgraph.pgraph_lock); - } - } -} - -static void pgraph_wait_fifo_access(NV2AState *d) { - while (!(d->pgraph.regs[NV_PGRAPH_FIFO] & NV_PGRAPH_FIFO_ACCESS)) { - qemu_cond_wait(&d->pgraph.fifo_access_cond, &d->pgraph.pgraph_lock); - } -} - -static void pgraph_log_method(unsigned int subchannel, - unsigned int graphics_class, - unsigned int method, uint32_t parameter) { - static unsigned int last = 0; - static unsigned int count = 0; - - extern const char *NV2AMethodToString(DWORD dwMethod); // implemented in PushBuffer.cpp - - if (last == 0x1800 && method != last) { - const char* method_name = NV2AMethodToString(last); // = 'NV2A_VB_ELEMENT_U16' - NV2A_GL_DPRINTF(true, "d->pgraph method (%d) 0x%08X %s * %d", - subchannel, last, method_name, count); - } - if (method != 0x1800) { - // const char* method_name = NV2AMethodToString(method); - // unsigned int nmethod = 0; - // switch (graphics_class) { - // case NV_KELVIN_PRIMITIVE: - // nmethod = method | (0x5c << 16); - // break; - // case NV_CONTEXT_SURFACES_2D: - // nmethod = method | (0x6d << 16); - // break; - // case NV_CONTEXT_PATTERN: - // nmethod = method | (0x68 << 16); - // break; - // default: - // break; - // } - // if (method_name) { - // NV2A_DPRINTF("d->pgraph method (%d): %s (0x%x)\n", - // subchannel, method_name, parameter); - // } else { - NV2A_DPRINTF("pgraph method (%d): 0x%08X -> 0x%04x (0x%x)\n", - subchannel, graphics_class, method, parameter); - // } - - } - if (method == last) { count++; } - else { count = 0; } - last = method; -} - -static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, - unsigned int attr) -{ - unsigned int i; - VertexAttribute *vertex_attribute = &pg->vertex_attributes[attr]; - - if (vertex_attribute->inline_buffer || pg->inline_buffer_length == 0) { - return; - } - - /* Now upload the previous vertex_attribute value */ - vertex_attribute->inline_buffer = (float*)g_malloc(NV2A_MAX_BATCH_LENGTH - * sizeof(float) * 4); - for (i = 0; i < pg->inline_buffer_length; i++) { - memcpy(&vertex_attribute->inline_buffer[i * 4], - vertex_attribute->inline_value, - sizeof(float) * 4); - } -} - -static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg) -{ - unsigned int i; - - assert(pg->inline_buffer_length < NV2A_MAX_BATCH_LENGTH); - - for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) { - VertexAttribute *vertex_attribute = &pg->vertex_attributes[i]; - if (vertex_attribute->inline_buffer) { - memcpy(&vertex_attribute->inline_buffer[ - pg->inline_buffer_length * 4], - vertex_attribute->inline_value, - sizeof(float) * 4); - } - } - - pg->inline_buffer_length++; -} - -void pgraph_init(NV2AState *d) -{ - int i; - - PGRAPHState *pg = &d->pgraph; - - qemu_mutex_init(&pg->pgraph_lock); - qemu_cond_init(&pg->interrupt_cond); - qemu_cond_init(&pg->fifo_access_cond); - qemu_cond_init(&pg->flip_3d); - - if (!(pg->opengl_enabled)) - return; - - /* attach OpenGL render plugins */ - OpenGL_init_pgraph_plugins(); - - /* fire up opengl */ - - pg->gl_context = glo_context_create(); - assert(pg->gl_context); - -#ifdef DEBUG_NV2A_GL - glEnable(GL_DEBUG_OUTPUT); -#endif - - glextensions_init(); - - /* DXT textures */ - assert(glo_check_extension("GL_EXT_texture_compression_s3tc")); - /* Internal RGB565 texture format */ - assert(glo_check_extension("GL_ARB_ES2_compatibility")); - - GLint max_vertex_attributes; - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attributes); - assert(max_vertex_attributes >= NV2A_VERTEXSHADER_ATTRIBUTES); - - glGenFramebuffers(1, &pg->gl_framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer); - - /* need a valid framebuffer to start with */ - glGenTextures(1, &pg->gl_color_buffer); - glBindTexture(GL_TEXTURE_2D, pg->gl_color_buffer); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 640, 480, - 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, pg->gl_color_buffer, 0); - - assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) - == GL_FRAMEBUFFER_COMPLETE); - - //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - -#ifdef USE_TEXTURE_CACHE - pg->texture_cache = g_lru_cache_new_full( - 0, - NULL, - texture_key_destroy, - 0, - NULL, - texture_binding_destroy, - texture_key_hash, - texture_key_equal, - texture_key_retrieve, - NULL, - NULL - ); - - g_lru_cache_set_max_size(pg->texture_cache, 512); -#endif - -#ifdef USE_SHADER_CACHE - pg->shader_cache = g_hash_table_new(shader_hash, shader_equal); -#endif - - for (i=0; ivertex_attributes[i].gl_converted_buffer); - glGenBuffers(1, &pg->vertex_attributes[i].gl_inline_buffer); - } - glGenBuffers(1, &pg->gl_inline_array_buffer); - glGenBuffers(1, &pg->gl_element_buffer); - - glGenBuffers(1, &pg->gl_memory_buffer); - glBindBuffer(GL_ARRAY_BUFFER, pg->gl_memory_buffer); - glBufferData(GL_ARRAY_BUFFER, - d->vram_size, - NULL, - GL_DYNAMIC_DRAW); - - glGenVertexArrays(1, &pg->gl_vertex_array); - glBindVertexArray(pg->gl_vertex_array); - -// assert(glGetError() == GL_NO_ERROR); - - glo_set_current(NULL); -} - -void pgraph_destroy(PGRAPHState *pg) -{ - - qemu_mutex_destroy(&pg->pgraph_lock); - qemu_cond_destroy(&pg->interrupt_cond); - qemu_cond_destroy(&pg->fifo_access_cond); - qemu_cond_destroy(&pg->flip_3d); - - if (pg->opengl_enabled) { - glo_set_current(pg->gl_context); - - if (pg->gl_color_buffer) { - glDeleteTextures(1, &pg->gl_color_buffer); - } - if (pg->gl_zeta_buffer) { - glDeleteTextures(1, &pg->gl_zeta_buffer); - } - glDeleteFramebuffers(1, &pg->gl_framebuffer); - - // TODO: clear out shader cached - // TODO: clear out texture cache - - glo_set_current(NULL); - - glo_context_destroy(pg->gl_context); - } -} - -static void pgraph_update_shader_constants(PGRAPHState *pg, - ShaderBinding *binding, - bool binding_changed, - bool vertex_program, - bool fixed_function) -{ - assert(pg->opengl_enabled); - - unsigned int i, j; - - /* update combiner constants */ - for (i = 0; i<= 8; i++) { - uint32_t constant[2]; - if (i == 8) { - /* final combiner */ - constant[0] = pg->regs[NV_PGRAPH_SPECFOGFACTOR0]; - constant[1] = pg->regs[NV_PGRAPH_SPECFOGFACTOR1]; - } else { - constant[0] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4]; - constant[1] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4]; - } - - for (j = 0; j < 2; j++) { - GLint loc = binding->psh_constant_loc[i][j]; - if (loc != -1) { - float value[4]; - value[0] = (float) ((constant[j] >> 16) & 0xFF) / 255.0f; - value[1] = (float) ((constant[j] >> 8) & 0xFF) / 255.0f; - value[2] = (float) (constant[j] & 0xFF) / 255.0f; - value[3] = (float) ((constant[j] >> 24) & 0xFF) / 255.0f; - - glUniform4fv(loc, 1, value); - } - } - } - if (binding->alpha_ref_loc != -1) { - float alpha_ref = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHAREF) / 255.0f; - glUniform1f(binding->alpha_ref_loc, alpha_ref); - } - - - /* For each texture stage */ - for (i = 0; i < NV2A_MAX_TEXTURES; i++) { - // char name[32]; - GLint loc; - - /* Bump luminance only during stages 1 - 3 */ - if (i > 0) { - loc = binding->bump_mat_loc[i]; - if (loc != -1) { - glUniformMatrix2fv(loc, 1, GL_FALSE, pg->bump_env_matrix[i - 1]); - } - loc = binding->bump_scale_loc[i]; - if (loc != -1) { - glUniform1f(loc, *(float*)&pg->regs[ - NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4]); - } - loc = binding->bump_offset_loc[i]; - if (loc != -1) { - glUniform1f(loc, *(float*)&pg->regs[ - NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4]); - } - } - - } - - if (binding->fog_color_loc != -1) { - uint32_t fog_color = pg->regs[NV_PGRAPH_FOGCOLOR]; - glUniform4f(binding->fog_color_loc, - GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0f, - GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0f, - GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0f, - GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0f); - } - if (binding->fog_param_loc[0] != -1) { - glUniform1f(binding->fog_param_loc[0], - *(float*)&pg->regs[NV_PGRAPH_FOGPARAM0]); - } - if (binding->fog_param_loc[1] != -1) { - glUniform1f(binding->fog_param_loc[1], - *(float*)&pg->regs[NV_PGRAPH_FOGPARAM1]); - } - - float zclip_max = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMAX]; - float zclip_min = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMIN]; - - if (fixed_function) { - /* update lighting constants */ - struct { - uint32_t* v; - bool* dirty; - GLint* locs; - size_t len; - } lighting_arrays[] = { - // TODO : Change pointers into offset_of(), so this variable can become static - {&pg->ltctxa[0][0], &pg->ltctxa_dirty[0], binding->ltctxa_loc, NV2A_LTCTXA_COUNT}, - {&pg->ltctxb[0][0], &pg->ltctxb_dirty[0], binding->ltctxb_loc, NV2A_LTCTXB_COUNT}, - {&pg->ltc1[0][0], &pg->ltc1_dirty[0], binding->ltc1_loc, NV2A_LTC1_COUNT}, - }; - - for (i=0; ilight_infinite_half_vector_loc[i]; - if (loc != -1) { - glUniform3fv(loc, 1, pg->light_infinite_half_vector[i]); - } - loc = binding->light_infinite_direction_loc[i]; - if (loc != -1) { - glUniform3fv(loc, 1, pg->light_infinite_direction[i]); - } - - loc = binding->light_local_position_loc[i]; - if (loc != -1) { - glUniform3fv(loc, 1, pg->light_local_position[i]); - } - loc = binding->light_local_attenuation_loc[i]; - if (loc != -1) { - glUniform3fv(loc, 1, pg->light_local_attenuation[i]); - } - } - - /* estimate the viewport by assuming it matches the surface ... */ - //FIXME: Get surface dimensions? - float m11 = 0.5f * pg->surface_shape.clip_width; - float m22 = -0.5f * pg->surface_shape.clip_height; - float m33 = zclip_max - zclip_min; - //float m41 = m11; - //float m42 = -m22; - float m43 = zclip_min; - //float m44 = 1.0f; - - if (m33 == 0.0f) { - m33 = 1.0f; - } - float invViewport[16] = { - 1.0f/m11, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f/m22, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f/m33, 0.0f, - -1.0f, 1.0f, -m43/m33, 1.0f - }; - - if (binding->inv_viewport_loc != -1) { - glUniformMatrix4fv(binding->inv_viewport_loc, - 1, GL_FALSE, &invViewport[0]); - } - - } - - /* update vertex program constants */ - for (i=0; ivsh_constants_dirty[i] && !binding_changed) continue; - - GLint loc = binding->vsh_constant_loc[i]; - //assert(loc != -1); - if (loc != -1) { - glUniform4fv(loc, 1, (const GLfloat*)pg->vsh_constants[i]); - } - pg->vsh_constants_dirty[i] = false; - } - - if (binding->surface_size_loc != -1) { - glUniform2f(binding->surface_size_loc, (GLfloat)pg->surface_shape.clip_width, - (GLfloat)pg->surface_shape.clip_height); - } - - if (binding->clip_range_loc != -1) { - glUniform2f(binding->clip_range_loc, zclip_min, zclip_max); - } - -} - -static void pgraph_bind_shaders(PGRAPHState *pg) -{ - assert(pg->opengl_enabled); - - unsigned int i, j; - - uint32_t csv0_d = pg->regs[NV_PGRAPH_CSV0_D]; - bool vertex_program = GET_MASK(csv0_d, - NV_PGRAPH_CSV0_D_MODE) == 2; - - bool fixed_function = GET_MASK(csv0_d, - NV_PGRAPH_CSV0_D_MODE) == 0; - - uint32_t csv0_c = pg->regs[NV_PGRAPH_CSV0_C]; - int program_start = GET_MASK(csv0_c, - NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START); - - NV2A_GL_DGROUP_BEGIN("%s (VP: %s FFP: %s)", __func__, - vertex_program ? "yes" : "no", - fixed_function ? "yes" : "no"); - - ShaderBinding* old_binding = pg->shader_binding; - - ShaderState state; - /* register combiner stuff */ - state.psh.window_clip_exclusive = pg->regs[NV_PGRAPH_SETUPRASTER] - & NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE, - state.psh.combiner_control = pg->regs[NV_PGRAPH_COMBINECTL]; - state.psh.shader_stage_program = pg->regs[NV_PGRAPH_SHADERPROG]; - state.psh.other_stage_input = pg->regs[NV_PGRAPH_SHADERCTL]; - state.psh.final_inputs_0 = pg->regs[NV_PGRAPH_COMBINESPECFOG0]; - state.psh.final_inputs_1 = pg->regs[NV_PGRAPH_COMBINESPECFOG1]; - uint32_t control0 = pg->regs[NV_PGRAPH_CONTROL_0]; - - state.psh.alpha_test = control0 - & NV_PGRAPH_CONTROL_0_ALPHATESTENABLE; - state.psh.alpha_func = (enum PshAlphaFunc)GET_MASK(control0, - NV_PGRAPH_CONTROL_0_ALPHAFUNC); - - /* fixed function stuff */ - state.skinning = (enum VshSkinning)GET_MASK(csv0_d, - NV_PGRAPH_CSV0_D_SKIN); - state.lighting = csv0_c - & NV_PGRAPH_CSV0_C_LIGHTING; - state.normalization = csv0_c - & NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE; - - state.fixed_function = fixed_function; - - /* vertex program stuff */ - state.vertex_program = vertex_program; - state.z_perspective = control0 - & NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE; - - /* geometry shader stuff */ - state.primitive_mode = (enum ShaderPrimitiveMode)pg->primitive_mode; - state.polygon_front_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_FRONTFACEMODE); - state.polygon_back_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_BACKFACEMODE); - - state.program_length = 0; - memset(state.program_data, 0, sizeof(state.program_data)); - - if (vertex_program) { - // copy in vertex program tokens - for (i = program_start; i < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH; i++) { - uint32_t *cur_token = (uint32_t*)&pg->program_data[i]; - memcpy(&state.program_data[state.program_length], - cur_token, - VSH_TOKEN_SIZE * sizeof(uint32_t)); - state.program_length++; - - if (vsh_get_field(cur_token, FLD_FINAL)) { - break; - } - } - } - - /* Texgen */ - for (i = 0; i < 4; i++) { - unsigned int reg = (i < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; - for (j = 0; j < 4; j++) { - unsigned int masks[] = { - // NOTE: For some reason, Visual Studio thinks NV_PGRAPH_xxxx is signed integer. (possible bug?) - (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_S : (unsigned int)NV_PGRAPH_CSV1_A_T0_S, - (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_T : (unsigned int)NV_PGRAPH_CSV1_A_T0_T, - (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_R : (unsigned int)NV_PGRAPH_CSV1_A_T0_R, - (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_Q : (unsigned int)NV_PGRAPH_CSV1_A_T0_Q - }; - state.texgen[i][j] = (enum VshTexgen)GET_MASK(pg->regs[reg], masks[j]); - } - } - - /* Fog */ - state.fog_enable = pg->regs[NV_PGRAPH_CONTROL_3] - & NV_PGRAPH_CONTROL_3_FOGENABLE; - if (state.fog_enable) { - /*FIXME: Use CSV0_D? */ - state.fog_mode = (enum VshFogMode)GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], - NV_PGRAPH_CONTROL_3_FOG_MODE); - state.foggen = (enum VshFoggen)GET_MASK(csv0_d, - NV_PGRAPH_CSV0_D_FOGGENMODE); - } else { - /* FIXME: Do we still pass the fogmode? */ - state.fog_mode = (enum VshFogMode)0; - state.foggen = (enum VshFoggen)0; - } - - /* Texture matrices */ - for (i = 0; i < 4; i++) { - state.texture_matrix_enable[i] = pg->texture_matrix_enable[i]; - } - - /* Lighting */ - if (state.lighting) { - for (i = 0; i < NV2A_MAX_LIGHTS; i++) { - state.light[i] = (enum VshLight)GET_MASK(csv0_d, - NV_PGRAPH_CSV0_D_LIGHT0 << (i * 2)); - } - } - - /* Window clip - * - * Optimization note: very quickly check to ignore any repeated or zero-size - * clipping regions. Note that if region number 7 is valid, but the rest are - * not, we will still add all of them. Clip regions seem to be typically - * front-loaded (meaning the first one or two regions are populated, and the - * following are zeroed-out), so let's avoid adding any more complicated - * masking or copying logic here for now unless we discover a valid case. - */ - assert(!state.psh.window_clip_exclusive); /* FIXME: Untested */ - state.psh.window_clip_count = 0; - uint32_t last_x = 0, last_y = 0; - - for (i = 0; i < 8; i++) { - const uint32_t x = pg->regs[NV_PGRAPH_WINDOWCLIPX0 + i * 4]; - const uint32_t y = pg->regs[NV_PGRAPH_WINDOWCLIPY0 + i * 4]; - const uint32_t x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN); - const uint32_t x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX); - const uint32_t y_min = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN); - const uint32_t y_max = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX); - - /* Check for zero width or height clipping region */ - if ((x_min == x_max) || (y_min == y_max)) { - continue; - } - - /* Check for in-order duplicate regions */ - if ((x == last_x) && (y == last_y)) { - continue; - } - - NV2A_DPRINTF("Clipping Region %d: min=(%d, %d) max=(%d, %d)\n", - i, x_min, y_min, x_max, y_max); - - state.psh.window_clip_count = i + 1; - last_x = x; - last_y = y; - } - - for (i = 0; i < 8; i++) { - state.psh.rgb_inputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORI0 + i * 4]; - state.psh.rgb_outputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORO0 + i * 4]; - state.psh.alpha_inputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAI0 + i * 4]; - state.psh.alpha_outputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAO0 + i * 4]; - //constant_0[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4]; - //constant_1[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4]; - } - - for (i = 0; i < 4; i++) { - state.psh.rect_tex[i] = false; - bool enabled = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4] - & NV_PGRAPH_TEXCTL0_0_ENABLE; - unsigned int color_format = - GET_MASK(pg->regs[NV_PGRAPH_TEXFMT0 + i*4], - NV_PGRAPH_TEXFMT0_COLOR); - - if (enabled && kelvin_color_format_map[color_format].encoding == linear) { - state.psh.rect_tex[i] = true; - } - - for (j = 0; j < 4; j++) { - state.psh.compare_mode[i][j] = - (pg->regs[NV_PGRAPH_SHADERCLIPMODE] >> (4 * i + j)) & 1; - } - state.psh.alphakill[i] = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4] - & NV_PGRAPH_TEXCTL0_0_ALPHAKILLEN; - } - -#ifdef USE_SHADER_CACHE - ShaderBinding* cached_shader = (ShaderBinding*)g_hash_table_lookup(pg->shader_cache, &state); - - if (cached_shader) { - pg->shader_binding = cached_shader; - } else { -#endif - pg->shader_binding = generate_shaders(state); - -#ifdef USE_SHADER_CACHE - /* cache it */ - ShaderState *cache_state = (ShaderState *)g_malloc(sizeof(*cache_state)); - memcpy(cache_state, &state, sizeof(*cache_state)); - g_hash_table_insert(pg->shader_cache, cache_state, - (gpointer)pg->shader_binding); - } -#endif - - bool binding_changed = (pg->shader_binding != old_binding); - - glUseProgram(pg->shader_binding->gl_program); - - /* Clipping regions */ - for (i = 0; i < state.psh.window_clip_count; i++) { - if (pg->shader_binding->clip_region_loc[i] == -1) { - continue; - } - - uint32_t x = pg->regs[NV_PGRAPH_WINDOWCLIPX0 + i * 4]; - GLuint x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN); - GLuint x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX); - - /* Adjust y-coordinates for the OpenGL viewport: translate coordinates - * to have the origin at the bottom-left of the surface (as opposed to - * top-left), and flip y-min and y-max accordingly. - */ - uint32_t y = pg->regs[NV_PGRAPH_WINDOWCLIPY0 + i * 4]; - GLuint y_min = (pg->surface_shape.clip_height - 1) - - GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX); - GLuint y_max = (pg->surface_shape.clip_height - 1) - - GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN); - - pgraph_apply_anti_aliasing_factor(pg, &x_min, &y_min); - pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max); - - glUniform4i(pg->shader_binding->clip_region_loc[i], - x_min, y_min, x_max, y_max); - } - - pgraph_update_shader_constants(pg, pg->shader_binding, binding_changed, - vertex_program, fixed_function); - - NV2A_GL_DGROUP_END(); -} - -static bool pgraph_get_framebuffer_dirty(PGRAPHState *pg) -{ - bool shape_changed = memcmp(&pg->surface_shape, &pg->last_surface_shape, - sizeof(SurfaceShape)) != 0; - if (!shape_changed || (!pg->surface_shape.color_format - && !pg->surface_shape.zeta_format)) { - return false; - } - return true; -} - -static bool pgraph_get_color_write_enabled(PGRAPHState *pg) -{ - return pg->regs[NV_PGRAPH_CONTROL_0] & ( - NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE - | NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE - | NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE - | NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE); -} - -static bool pgraph_get_zeta_write_enabled(PGRAPHState *pg) -{ - return pg->regs[NV_PGRAPH_CONTROL_0] & ( - NV_PGRAPH_CONTROL_0_ZWRITEENABLE - | NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE); -} - -static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta) -{ - NV2A_DPRINTF("pgraph_set_surface_dirty(%d, %d) -- %d %d\n", - color, zeta, - pgraph_get_color_write_enabled(pg), pgraph_get_zeta_write_enabled(pg)); - /* FIXME: Does this apply to CLEARs too? */ - color = color && pgraph_get_color_write_enabled(pg); - zeta = zeta && pgraph_get_zeta_write_enabled(pg); - pg->surface_color.draw_dirty |= color; - pg->surface_zeta.draw_dirty |= zeta; -} - -static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) { - PGRAPHState *pg = &d->pgraph; - - unsigned int width, height; - pgraph_get_surface_dimensions(pg, &width, &height); - pgraph_apply_anti_aliasing_factor(pg, &width, &height); - - Surface *surface; - hwaddr dma_address; - GLuint *gl_buffer; - unsigned int bytes_per_pixel; - GLint gl_internal_format; - GLenum gl_format, gl_type, gl_attachment; - - if (color) { - surface = &pg->surface_color; - dma_address = pg->dma_color; - gl_buffer = &pg->gl_color_buffer; - - assert(pg->surface_shape.color_format != 0); - assert(pg->surface_shape.color_format - < ARRAY_SIZE(kelvin_surface_color_format_map)); - SurfaceColorFormatInfo f = - kelvin_surface_color_format_map[pg->surface_shape.color_format]; - if (f.bytes_per_pixel == 0) { - fprintf(stderr, "nv2a: unimplemented color surface format 0x%x\n", - pg->surface_shape.color_format); - abort(); - } - - bytes_per_pixel = f.bytes_per_pixel; - gl_internal_format = f.gl_internal_format; - gl_format = f.gl_format; - gl_type = f.gl_type; - gl_attachment = GL_COLOR_ATTACHMENT0; - - } else { - surface = &pg->surface_zeta; - dma_address = pg->dma_zeta; - gl_buffer = &pg->gl_zeta_buffer; - - assert(pg->surface_shape.zeta_format != 0); - switch (pg->surface_shape.zeta_format) { - case NV097_SET_SURFACE_FORMAT_ZETA_Z16: - bytes_per_pixel = 2; - gl_format = GL_DEPTH_COMPONENT; - gl_attachment = GL_DEPTH_ATTACHMENT; - if (pg->surface_shape.z_format) { - gl_type = GL_HALF_FLOAT; - gl_internal_format = GL_DEPTH_COMPONENT32F; - } else { - gl_type = GL_UNSIGNED_SHORT; - gl_internal_format = GL_DEPTH_COMPONENT16; - } - break; - case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: - bytes_per_pixel = 4; - gl_format = GL_DEPTH_STENCIL; - gl_attachment = GL_DEPTH_STENCIL_ATTACHMENT; - if (pg->surface_shape.z_format) { - assert(false); - gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; - gl_internal_format = GL_DEPTH32F_STENCIL8; - } else { - gl_type = GL_UNSIGNED_INT_24_8; - gl_internal_format = GL_DEPTH24_STENCIL8; - } - break; - default: - assert(false); - break; - } - } - - - DMAObject dma = nv_dma_load(d, dma_address); - /* There's a bunch of bugs that could cause us to hit this function - * at the wrong time and get a invalid dma object. - * Check that it's sane. */ - assert(dma.dma_class == NV_DMA_IN_MEMORY_CLASS); - - assert(dma.address + surface->offset != 0); - assert(surface->offset <= dma.limit); - assert(surface->offset + surface->pitch * height <= dma.limit + 1); - - hwaddr data_len; - uint8_t *data = (uint8_t*)nv_dma_map(d, dma_address, &data_len); - - /* TODO */ - // assert(pg->surface_clip_x == 0 && pg->surface_clip_y == 0); - - bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE); - - uint8_t *buf = data + surface->offset; - if (swizzle) { - buf = (uint8_t*)g_malloc(height * surface->pitch); - } - - bool dirty = surface->buffer_dirty; - if (color) { -#if 1 - // HACK: Always mark as dirty - dirty |= 1; -#else - dirty |= memory_region_test_and_clear_dirty(d->vram, - dma.address + surface->offset, - surface->pitch * height, - DIRTY_MEMORY_NV2A); -#endif - } - if (upload && dirty) { - /* surface modified (or moved) by the cpu. - * copy it into the opengl renderbuffer */ - // TODO: Why does this assert? - //assert(!surface->draw_dirty); - assert(surface->pitch % bytes_per_pixel == 0); - - if (swizzle) { - unswizzle_rect(data + surface->offset, - width, height, - buf, - surface->pitch, - bytes_per_pixel); - } - - if (pg->opengl_enabled) { - if (!color) { - /* need to clear the depth_stencil and depth attachment for zeta */ - glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D, - 0, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_DEPTH_STENCIL_ATTACHMENT, - GL_TEXTURE_2D, - 0, 0); - } - - glFramebufferTexture2D(GL_FRAMEBUFFER, - gl_attachment, - GL_TEXTURE_2D, - 0, 0); - - if (*gl_buffer) { - glDeleteTextures(1, gl_buffer); - *gl_buffer = 0; - } - - glGenTextures(1, gl_buffer); - glBindTexture(GL_TEXTURE_2D, *gl_buffer); - - /* This is VRAM so we can't do this inplace! */ - uint8_t *flipped_buf = (uint8_t*)g_malloc(width * height * bytes_per_pixel); - unsigned int irow; - for (irow = 0; irow < height; irow++) { - memcpy(&flipped_buf[width * (height - irow - 1) - * bytes_per_pixel], - &buf[surface->pitch * irow], - width * bytes_per_pixel); - } - - glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, - width, height, 0, - gl_format, gl_type, - flipped_buf); - - g_free(flipped_buf); - - glFramebufferTexture2D(GL_FRAMEBUFFER, - gl_attachment, - GL_TEXTURE_2D, - *gl_buffer, 0); - - assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) - == GL_FRAMEBUFFER_COMPLETE); - } - - if (color) { - pgraph_update_memory_buffer(d, dma.address + surface->offset, - surface->pitch * height, true); - } - surface->buffer_dirty = false; - -#ifdef DEBUG_NV2A - uint8_t *out = data + surface->offset + 64; - NV2A_DPRINTF("upload_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " - "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " - "%d %d, %d %d, %d) - %x %x %x %x\n", - color ? "color" : "zeta", - dma.address, dma.address + dma.limit, - dma.address + surface->offset, - dma.address + surface->pitch * height, - pg->surface_shape.clip_x, pg->surface_shape.clip_y, - pg->surface_shape.clip_width, - pg->surface_shape.clip_height, - surface->pitch, - out[0], out[1], out[2], out[3]); -#endif - } - - if (!upload && surface->draw_dirty) { - if (pg->opengl_enabled) { - /* read the opengl framebuffer into the surface */ - - glo_readpixels(gl_format, gl_type, - bytes_per_pixel, surface->pitch, - width, height, - buf); - -// assert(glGetError() == GL_NO_ERROR); - } - - if (swizzle) { - swizzle_rect(buf, - width, height, - data + surface->offset, - surface->pitch, - bytes_per_pixel); - } - - // memory_region_set_client_dirty(d->vram, - // dma.address + surface->offset, - // surface->pitch * height, - // DIRTY_MEMORY_VGA); - - if (color) { - pgraph_update_memory_buffer(d, dma.address + surface->offset, - surface->pitch * height, true); - } - - surface->draw_dirty = false; - surface->write_enabled_cache = false; - -#ifdef DEBUG_NV2A - uint8_t *out = data + surface->offset + 64; - NV2A_DPRINTF("read_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " - "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " - "%d %d, %d %d, %d) - %x %x %x %x\n", - color ? "color" : "zeta", - dma.address, dma.address + dma.limit, - dma.address + surface->offset, - dma.address + surface->pitch * pg->surface_shape.clip_height, - pg->surface_shape.clip_x, pg->surface_shape.clip_y, - pg->surface_shape.clip_width, pg->surface_shape.clip_height, - surface->pitch, - out[0], out[1], out[2], out[3]); -#endif - } - - if (swizzle) { - g_free(buf); - } -} - -static void pgraph_update_surface(NV2AState *d, bool upload, - bool color_write, bool zeta_write) -{ - PGRAPHState *pg = &d->pgraph; - - if (!pg->opengl_enabled) { - return; - } - - pg->surface_shape.z_format = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_Z_FORMAT); - - /* FIXME: Does this apply to CLEARs too? */ - color_write = color_write && pgraph_get_color_write_enabled(pg); - zeta_write = zeta_write && pgraph_get_zeta_write_enabled(pg); - - if (upload && pgraph_get_framebuffer_dirty(pg)) { - assert(!pg->surface_color.draw_dirty); - assert(!pg->surface_zeta.draw_dirty); - - pg->surface_color.buffer_dirty = true; - pg->surface_zeta.buffer_dirty = true; - - glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - 0, 0); - - if (pg->gl_color_buffer) { - glDeleteTextures(1, &pg->gl_color_buffer); - pg->gl_color_buffer = 0; - } - - glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D, - 0, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_DEPTH_STENCIL_ATTACHMENT, - GL_TEXTURE_2D, - 0, 0); - - if (pg->gl_zeta_buffer) { - glDeleteTextures(1, &pg->gl_zeta_buffer); - pg->gl_zeta_buffer = 0; - } - } - - memcpy(&pg->last_surface_shape, &pg->surface_shape, - sizeof(SurfaceShape)); - - if ((color_write || (!upload && pg->surface_color.write_enabled_cache)) - && (upload || pg->surface_color.draw_dirty)) { - pgraph_update_surface_part(d, upload, true); - } - - - if ((zeta_write || (!upload && pg->surface_zeta.write_enabled_cache)) - && (upload || pg->surface_zeta.draw_dirty)) { - pgraph_update_surface_part(d, upload, false); - } - -} - -static void pgraph_bind_textures(NV2AState *d) -{ - int i; - PGRAPHState *pg = &d->pgraph; - - if (!(pg->opengl_enabled)) - return; - - NV2A_GL_DGROUP_BEGIN("%s", __func__); - - for (i=0; iregs[NV_PGRAPH_TEXCTL0_0 + i*4]; - uint32_t ctl_1 = pg->regs[NV_PGRAPH_TEXCTL1_0 + i*4]; - uint32_t fmt = pg->regs[NV_PGRAPH_TEXFMT0 + i*4]; - uint32_t filter = pg->regs[NV_PGRAPH_TEXFILTER0 + i*4]; - uint32_t address = pg->regs[NV_PGRAPH_TEXADDRESS0 + i*4]; - uint32_t palette = pg->regs[NV_PGRAPH_TEXPALETTE0 + i*4]; - - bool enabled = ctl_0 & NV_PGRAPH_TEXCTL0_0_ENABLE; - unsigned int min_mipmap_level = - GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MIN_LOD_CLAMP); - unsigned int max_mipmap_level = - GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MAX_LOD_CLAMP); - - unsigned int pitch = - GET_MASK(ctl_1, NV_PGRAPH_TEXCTL1_0_IMAGE_PITCH); - - bool dma_select = - fmt & NV_PGRAPH_TEXFMT0_CONTEXT_DMA; - bool cubemap = - fmt & NV_PGRAPH_TEXFMT0_CUBEMAPENABLE; - unsigned int dimensionality = - GET_MASK(fmt, NV_PGRAPH_TEXFMT0_DIMENSIONALITY); - unsigned int color_format = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_COLOR); - unsigned int levels = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS); - unsigned int log_width = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_U); - unsigned int log_height = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_V); - unsigned int log_depth = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_P); - - unsigned int rect_width = - GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4], - NV_PGRAPH_TEXIMAGERECT0_WIDTH); - unsigned int rect_height = - GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4], - NV_PGRAPH_TEXIMAGERECT0_HEIGHT); -#ifdef DEBUG_NV2A - unsigned int lod_bias = - GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS); -#endif - unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN); - unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG); - - unsigned int addru = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRU); - unsigned int addrv = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV); - unsigned int addrp = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP); - - bool border_source_color = (fmt & NV_PGRAPH_TEXFMT0_BORDER_SOURCE); // != NV_PGRAPH_TEXFMT0_BORDER_SOURCE_TEXTURE; - uint32_t border_color = pg->regs[NV_PGRAPH_BORDERCOLOR0 + i*4]; - - unsigned int offset = pg->regs[NV_PGRAPH_TEXOFFSET0 + i*4]; - - bool palette_dma_select = - palette & NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA; - unsigned int palette_length_index = - GET_MASK(palette, NV_PGRAPH_TEXPALETTE0_LENGTH); - unsigned int palette_offset = - palette & NV_PGRAPH_TEXPALETTE0_OFFSET; - - unsigned int palette_length = 0; - switch (palette_length_index) { - case NV_PGRAPH_TEXPALETTE0_LENGTH_256: palette_length = 256; break; - case NV_PGRAPH_TEXPALETTE0_LENGTH_128: palette_length = 128; break; - case NV_PGRAPH_TEXPALETTE0_LENGTH_64: palette_length = 64; break; - case NV_PGRAPH_TEXPALETTE0_LENGTH_32: palette_length = 32; break; - default: assert(false); break; - } - - /* Check for unsupported features */ - assert(!(filter & NV_PGRAPH_TEXFILTER0_ASIGNED)); - assert(!(filter & NV_PGRAPH_TEXFILTER0_RSIGNED)); - assert(!(filter & NV_PGRAPH_TEXFILTER0_GSIGNED)); - assert(!(filter & NV_PGRAPH_TEXFILTER0_BSIGNED)); - - glActiveTexture(GL_TEXTURE0 + i); - if (!enabled) { - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - glBindTexture(GL_TEXTURE_RECTANGLE, 0); - glBindTexture(GL_TEXTURE_1D, 0); - glBindTexture(GL_TEXTURE_2D, 0); - glBindTexture(GL_TEXTURE_3D, 0); - continue; - } - - if (!pg->texture_dirty[i] && pg->texture_binding[i]) { - glBindTexture(pg->texture_binding[i]->gl_target, - pg->texture_binding[i]->gl_texture); - continue; - } - - NV2A_DPRINTF(" texture %d is format 0x%x, off 0x%x (r %d, %d or %d, %d, %d; %d%s)," - " filter %x %x, levels %d-%d %d bias %d\n", - i, color_format, offset, - rect_width, rect_height, - 1 << log_width, 1 << log_height, 1 << log_depth, - pitch, - cubemap ? "; cubemap" : "", - min_filter, mag_filter, - min_mipmap_level, max_mipmap_level, levels, - lod_bias); - - assert(color_format < ARRAY_SIZE(kelvin_color_format_map)); - ColorFormatInfo f = kelvin_color_format_map[color_format]; - if (f.bytes_per_pixel == 0) { - fprintf(stderr, "nv2a: unimplemented texture color format 0x%x\n", - color_format); - abort(); - } - - unsigned int width, height, depth; - if (f.encoding == linear) { - assert(dimensionality == 2); - width = rect_width; - height = rect_height; - depth = 1; - } else { - width = 1 << log_width; - height = 1 << log_height; - depth = 1 << log_depth; - - /* FIXME: What about 3D mipmaps? */ - levels = MIN(levels, max_mipmap_level + 1); - if (f.encoding == swizzled) { - /* Discard mipmap levels that would be smaller than 1x1. - * FIXME: Is this actually needed? - * - * >> Level 0: 32 x 4 - * Level 1: 16 x 2 - * Level 2: 8 x 1 - * Level 3: 4 x 1 - * Level 4: 2 x 1 - * Level 5: 1 x 1 - */ - levels = MIN(levels, MAX(log_width, log_height) + 1); - } else { - /* OpenGL requires DXT textures to always have a width and - * height a multiple of 4. The Xbox and DirectX handles DXT - * textures smaller than 4 by padding the reset of the block. - * - * See: - * https://msdn.microsoft.com/en-us/library/windows/desktop/bb204843(v=vs.85).aspx - * https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531%28v=vs.85%29.aspx#Virtual_Size - * - * Work around this for now by discarding mipmap levels that - * would result in too-small textures. A correct solution - * will be to decompress these levels manually, or add texture - * sampling logic. - * - * >> Level 0: 64 x 8 - * Level 1: 32 x 4 - * Level 2: 16 x 2 << Ignored - * >> Level 0: 16 x 16 - * Level 1: 8 x 8 - * Level 2: 4 x 4 << OK! - */ - if (log_width < 2 || log_height < 2) { - /* Base level is smaller than 4x4... */ - levels = 1; - } else { - levels = MIN(levels, MIN(log_width, log_height) - 1); - } - } - assert(levels > 0); - } - - hwaddr dma_len; - uint8_t *texture_data; - if (dma_select) { - texture_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &dma_len); - } else { - texture_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &dma_len); - } - assert(offset < dma_len); - texture_data += offset; - - hwaddr palette_dma_len; - uint8_t *palette_data; - if (palette_dma_select) { - palette_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &palette_dma_len); - } else { - palette_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &palette_dma_len); - } - assert(palette_offset < palette_dma_len); - palette_data += palette_offset; - - NV2A_DPRINTF(" - 0x%tx\n", texture_data - d->vram_ptr); - - size_t length = 0; - if (f.encoding == linear) { - assert(cubemap == false); - assert(dimensionality == 2); - length = height * pitch; - } else { - if (dimensionality >= 2) { - unsigned int w = width, h = height; - unsigned int level; - if (f.encoding == swizzled) { - for (level = 0; level < levels; level++) { - w = MAX(w, 1); h = MAX(h, 1); - length += w * h * f.bytes_per_pixel; - w /= 2; - h /= 2; - } - } else { - /* Compressed textures are a bit different */ - unsigned int block_size; - if (f.gl_internal_format == - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { - block_size = 8; - } else { - block_size = 16; - } - - for (level = 0; level < levels; level++) { - w = MAX(w, 4); h = MAX(h, 4); - length += w/4 * h/4 * block_size; - w /= 2; h /= 2; - } - } - if (cubemap) { - assert(dimensionality == 2); - length *= 6; - } - if (dimensionality >= 3) { - length *= depth; - } - } - } - - TextureShape state; - state.cubemap = cubemap; - state.dimensionality = dimensionality; - state.color_format = color_format; - state.levels = levels; - state.width = width; - state.height = height; - state.depth = depth; - state.min_mipmap_level = min_mipmap_level; - state.max_mipmap_level = max_mipmap_level; - state.pitch = pitch; - -#ifdef USE_TEXTURE_CACHE - TextureKey key; - key.state = state; - key.data_hash = fast_hash(texture_data, length, 5003) - ^ fnv_hash(palette_data, palette_length); - key.texture_data = texture_data; - key.palette_data = palette_data; - - gpointer cache_key = g_malloc(sizeof(TextureKey)); - memcpy(cache_key, &key, sizeof(TextureKey)); - - GError *err; - TextureBinding *binding = (TextureBinding *)g_lru_cache_get(pg->texture_cache, cache_key, &err); - assert(binding); - binding->refcnt++; -#else - TextureBinding *binding = generate_texture(state, - texture_data, palette_data); -#endif - - glBindTexture(binding->gl_target, binding->gl_texture); - - - if (f.encoding == linear) { - /* sometimes games try to set mipmap min filters on linear textures. - * this could indicate a bug... */ - switch (min_filter) { - case NV_PGRAPH_TEXFILTER0_MIN_BOX_NEARESTLOD: - case NV_PGRAPH_TEXFILTER0_MIN_BOX_TENT_LOD: - min_filter = NV_PGRAPH_TEXFILTER0_MIN_BOX_LOD0; - break; - case NV_PGRAPH_TEXFILTER0_MIN_TENT_NEARESTLOD: - case NV_PGRAPH_TEXFILTER0_MIN_TENT_TENT_LOD: - min_filter = NV_PGRAPH_TEXFILTER0_MIN_TENT_LOD0; - break; - } - } - - glTexParameteri(binding->gl_target, GL_TEXTURE_MIN_FILTER, - pgraph_texture_min_filter_map[min_filter]); - glTexParameteri(binding->gl_target, GL_TEXTURE_MAG_FILTER, - pgraph_texture_mag_filter_map[mag_filter]); - - /* Texture wrapping */ - assert(addru < ARRAY_SIZE(pgraph_texture_addr_map)); - glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_S, - pgraph_texture_addr_map[addru]); - if (dimensionality > 1) { - assert(addrv < ARRAY_SIZE(pgraph_texture_addr_map)); - glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_T, - pgraph_texture_addr_map[addrv]); - } - if (dimensionality > 2) { - assert(addrp < ARRAY_SIZE(pgraph_texture_addr_map)); - glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_R, - pgraph_texture_addr_map[addrp]); - } - - /* FIXME: Only upload if necessary? [s, t or r = GL_CLAMP_TO_BORDER] */ - if (border_source_color) { - GLfloat gl_border_color[] = { - /* FIXME: Color channels might be wrong order */ - ((border_color >> 16) & 0xFF) / 255.0f, /* red */ - ((border_color >> 8) & 0xFF) / 255.0f, /* green */ - (border_color & 0xFF) / 255.0f, /* blue */ - ((border_color >> 24) & 0xFF) / 255.0f /* alpha */ - }; - glTexParameterfv(binding->gl_target, GL_TEXTURE_BORDER_COLOR, - gl_border_color); - } - - if (pg->texture_binding[i]) { - texture_binding_destroy(pg->texture_binding[i]); - } - pg->texture_binding[i] = binding; - pg->texture_dirty[i] = false; - } - NV2A_GL_DGROUP_END(); -} - -static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, - unsigned int *width, - unsigned int *height) -{ - switch (pg->surface_shape.anti_aliasing) { - case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_1: - break; - case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_CORNER_2: - if (width) { *width *= 2; } - break; - case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_SQUARE_OFFSET_4: - if (width) { *width *= 2; } - if (height) { *height *= 2; } - break; - default: - assert(false); - break; - } -} - -static void pgraph_get_surface_dimensions(PGRAPHState *pg, - unsigned int *width, - unsigned int *height) -{ - bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE); - if (swizzle) { - *width = 1 << pg->surface_shape.log_width; - *height = 1 << pg->surface_shape.log_height; - } else { - *width = pg->surface_shape.clip_width; - *height = pg->surface_shape.clip_height; - } -} - -static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, - bool f) -{ - glBindBuffer(GL_ARRAY_BUFFER, d->pgraph.gl_memory_buffer); - - hwaddr end = TARGET_PAGE_ALIGN(addr + size); - addr &= TARGET_PAGE_MASK; - - assert(end < d->vram_size); - - // if (f || memory_region_test_and_clear_dirty(d->vram, - // addr, - // end - addr, - // DIRTY_MEMORY_NV2A)) { - glBufferSubData(GL_ARRAY_BUFFER, addr, end - addr, d->vram_ptr + addr); - // } - -// auto error = glGetError(); -// assert(error == GL_NO_ERROR); -} - -static void pgraph_bind_vertex_attributes(NV2AState *d, - unsigned int num_elements, - bool inline_data, - unsigned int inline_stride) -{ - unsigned int i, j; - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - - if (inline_data) { - NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d inline stride: %d)", - __func__, num_elements, inline_stride); - } else { - NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d)", __func__, num_elements); - } - - for (i=0; ivertex_attributes[i]; - if (vertex_attribute->count) { - uint8_t *data; - unsigned int in_stride; - if (inline_data && vertex_attribute->needs_conversion) { - data = (uint8_t*)pg->inline_array - + vertex_attribute->inline_array_offset; - in_stride = inline_stride; - } else { - hwaddr dma_len; - if (vertex_attribute->dma_select) { - data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_b, &dma_len); - } else { - data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_a, &dma_len); - } - - assert(vertex_attribute->offset < dma_len); - data += vertex_attribute->offset; - - in_stride = vertex_attribute->stride; - } - - if (vertex_attribute->needs_conversion) { - NV2A_DPRINTF("converted %d\n", i); - - unsigned int out_stride = vertex_attribute->converted_size - * vertex_attribute->converted_count; - - if (num_elements > vertex_attribute->converted_elements) { - vertex_attribute->converted_buffer = (uint8_t*)g_realloc( - vertex_attribute->converted_buffer, - num_elements * out_stride); - } - - for (j=vertex_attribute->converted_elements; jconverted_buffer + j * out_stride; - - switch (vertex_attribute->format) { - case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: { - uint32_t p = ldl_le_p((uint32_t*)in); - float *xyz = (float*)out; - xyz[0] = ((int32_t)(((p >> 0) & 0x7FF) << 21) >> 21) - / 1023.0f; - xyz[1] = ((int32_t)(((p >> 11) & 0x7FF) << 21) >> 21) - / 1023.0f; - xyz[2] = ((int32_t)(((p >> 22) & 0x3FF) << 22) >> 22) - / 511.0f; - break; - } - default: - assert(false); - break; - } - } - - - glBindBuffer(GL_ARRAY_BUFFER, vertex_attribute->gl_converted_buffer); - if (num_elements != vertex_attribute->converted_elements) { - glBufferData(GL_ARRAY_BUFFER, - num_elements * out_stride, - vertex_attribute->converted_buffer, - GL_DYNAMIC_DRAW); - vertex_attribute->converted_elements = num_elements; - } - - - glVertexAttribPointer(i, - vertex_attribute->converted_count, - vertex_attribute->gl_type, - vertex_attribute->gl_normalize, - out_stride, - 0); - } else if (inline_data) { - glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); - glVertexAttribPointer(i, - vertex_attribute->gl_count, - vertex_attribute->gl_type, - vertex_attribute->gl_normalize, - inline_stride, - (void*)(uintptr_t)vertex_attribute->inline_array_offset); - } else { - hwaddr addr = data - d->vram_ptr; - pgraph_update_memory_buffer(d, addr, - num_elements * vertex_attribute->stride, - false); - glVertexAttribPointer(i, - vertex_attribute->gl_count, - vertex_attribute->gl_type, - vertex_attribute->gl_normalize, - vertex_attribute->stride, - (void*)(uint64_t)(addr)); - } - glEnableVertexAttribArray(i); - } else { - glDisableVertexAttribArray(i); - - glVertexAttrib4fv(i, vertex_attribute->inline_value); - } - } - NV2A_GL_DGROUP_END(); -} - -static unsigned int pgraph_bind_inline_array(NV2AState *d) -{ - int i; - - PGRAPHState *pg = &d->pgraph; - - assert(pg->opengl_enabled); - - unsigned int offset = 0; - for (i=0; ivertex_attributes[i]; - if (vertex_attribute->count) { - vertex_attribute->inline_array_offset = offset; - - NV2A_DPRINTF("bind inline vertex_attribute %d size=%d, count=%d\n", - i, vertex_attribute->size, vertex_attribute->count); - offset += vertex_attribute->size * vertex_attribute->count; - assert(offset % 4 == 0); - } - } - - unsigned int vertex_size = offset; - - - unsigned int index_count = pg->inline_array_length*4 / vertex_size; - - NV2A_DPRINTF("draw inline array %d, %d\n", vertex_size, index_count); - - glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); - glBufferData(GL_ARRAY_BUFFER, pg->inline_array_length*4, pg->inline_array, - GL_DYNAMIC_DRAW); - - pgraph_bind_vertex_attributes(d, index_count, true, vertex_size); - - return index_count; -} - -/* 16 bit to [0.0, F16_MAX = 511.9375] */ -static float convert_f16_to_float(uint16_t f16) { - if (f16 == 0x0000) { return 0.0f; } - uint32_t i = (f16 << 11) + 0x3C000000; - return *(float*)&i; -} - -/* 24 bit to [0.0, F24_MAX] */ -static float convert_f24_to_float(uint32_t f24) { - assert(!(f24 >> 24)); - f24 &= 0xFFFFFF; - if (f24 == 0x000000) { return 0.0f; } - uint32_t i = f24 << 7; - return *(float*)&i; -} - -extern void __R6G5B5ToARGBRow_C(const uint8_t* src_r6g5b5, uint8_t* dst_argb, int width); -extern void ____YUY2ToARGBRow_C(const uint8_t* src_yuy2, uint8_t* rgb_buf, int width); -extern void ____UYVYToARGBRow_C(const uint8_t* src_uyvy, uint8_t* rgb_buf, int width); - -/* 'converted_format' indicates the format that results when convert_texture_data() returns non-NULL converted_data. */ -static const int converted_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8; - -static uint8_t* convert_texture_data(const unsigned int color_format, - const uint8_t *data, - const uint8_t *palette_data, - const unsigned int width, - const unsigned int height, - const unsigned int depth, - const unsigned int row_pitch, - const unsigned int slice_pitch) -{ - // Note : Unswizzle is already done when entering here - switch (color_format) { - case NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8: { - // Test-case : WWE RAW2 - assert(depth == 1); /* FIXME */ - uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4); - unsigned int x, y; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - uint8_t index = data[y * row_pitch + x]; - uint32_t color = *(uint32_t*)(palette_data + index * 4); - *(uint32_t*)(converted_data + y * width * 4 + x * 4) = color; - } - } - return converted_data; - } - case NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X7SY9: { - assert(false); /* FIXME */ - return NULL; - } - case NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8: { - // Test-case : WWE RAW2 - assert(depth == 1); /* FIXME */ - uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4); - unsigned int y; - for (y = 0; y < height; y++) { - const uint8_t* line = &data[y * width * 2]; - uint8_t* pixel = &converted_data[(y * width) * 4]; - ____YUY2ToARGBRow_C(line, pixel, width); - // Note : LC_IMAGE_CR8YB8CB8YA8 suggests UYVY format, - // but for an unknown reason, the actual encoding is YUY2 - } - return converted_data; - } - case NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_YB8CR8YA8CB8: { - assert(depth == 1); /* FIXME */ - uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4); - unsigned int y; - for (y = 0; y < height; y++) { - const uint8_t* line = &data[y * width * 2]; - uint8_t* pixel = &converted_data[(y * width) * 4]; - ____UYVYToARGBRow_C(line, pixel, width); // TODO : Validate LC_IMAGE_YB8CR8YA8CB8 indeed requires ____UYVYToARGBRow_C() - } - return converted_data; - } - case NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_A4V6YB6A4U6YA6: { - assert(false); /* FIXME */ - return NULL; - } - case NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8CR8CB8Y8: { - assert(false); /* FIXME */ - return NULL; - } - case NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5: - case NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R6G5B5: { - assert(depth == 1); /* FIXME */ - uint8_t *converted_data = (uint8_t*)g_malloc(width * height * 4); - unsigned int y; - for (y = 0; y < height; y++) { - uint16_t rgb655 = *(uint16_t*)(data + y * row_pitch); - int8_t *pixel = (int8_t*)&converted_data[(y * width) * 4]; - __R6G5B5ToARGBRow_C((const uint8_t*)rgb655, (uint8_t*)pixel, width); - } - return converted_data; - } - default: - return NULL; - } -} - -/* returns the format of the output, either identical to the input format, or the converted format - see converted_format */ -static int upload_gl_texture(GLenum gl_target, - const TextureShape s, - const uint8_t *texture_data, - const uint8_t *palette_data) -{ - //assert(pg->opengl_enabled); - int resulting_format = s.color_format; - ColorFormatInfo f = kelvin_color_format_map[s.color_format]; - - switch(gl_target) { - case GL_TEXTURE_1D: - assert(false); - break; - case GL_TEXTURE_RECTANGLE: { - /* Can't handle strides unaligned to pixels */ - assert(s.pitch % f.bytes_per_pixel == 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, - s.pitch / f.bytes_per_pixel); - - uint8_t *unswizzled = NULL; - if (f.encoding == swizzled) { // TODO : Verify this works correctly - unswizzled = (uint8_t*)g_malloc(s.height * s.pitch); - unswizzle_rect(texture_data, s.width, s.height, - unswizzled, s.pitch, f.bytes_per_pixel); - } - uint8_t *converted = convert_texture_data(s.color_format, unswizzled ? unswizzled : texture_data, - palette_data, - s.width, s.height, 1, - s.pitch, 0); - - resulting_format = converted ? converted_format : s.color_format; - ColorFormatInfo cf = kelvin_color_format_map[resulting_format]; - glTexImage2D(gl_target, 0, cf.gl_internal_format, - s.width, s.height, 0, - cf.gl_format, cf.gl_type, - converted ? converted : unswizzled ? unswizzled : texture_data); - - if (converted) { - g_free(converted); - } - if (unswizzled) { - g_free(unswizzled); - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - break; - } - case GL_TEXTURE_2D: - case GL_TEXTURE_CUBE_MAP_POSITIVE_X: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: { - - unsigned int width = s.width, height = s.height; - - unsigned int level; - for (level = 0; level < s.levels; level++) { - if (f.encoding == compressed) { - - width = MAX(width, 4); height = MAX(height, 4); - - unsigned int block_size; - if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { - block_size = 8; - } else { - block_size = 16; - } - - glCompressedTexImage2D(gl_target, level, f.gl_internal_format, - width, height, 0, - width/4 * height/4 * block_size, - texture_data); - - texture_data += width/4 * height/4 * block_size; - } else { - - width = MAX(width, 1); height = MAX(height, 1); - - unsigned int pitch = width * f.bytes_per_pixel; - uint8_t *unswizzled = NULL; - if (f.encoding == swizzled) { - unswizzled = (uint8_t*)g_malloc(height * pitch); - unswizzle_rect(texture_data, width, height, - unswizzled, pitch, f.bytes_per_pixel); - } - - uint8_t *converted = convert_texture_data(s.color_format, unswizzled ? unswizzled : texture_data, - palette_data, - width, height, 1, - pitch, 0); - - resulting_format = converted ? converted_format : s.color_format; - ColorFormatInfo cf = kelvin_color_format_map[resulting_format]; - glTexImage2D(gl_target, level, cf.gl_internal_format, - width, height, 0, - cf.gl_format, cf.gl_type, - converted ? converted : unswizzled ? unswizzled : texture_data); - - if (converted) { - g_free(converted); - } - if (unswizzled) { - g_free(unswizzled); - } - - texture_data += pitch * height; - } - - width /= 2; - height /= 2; - } - - break; - } - case GL_TEXTURE_3D: { - - unsigned int width = s.width, height = s.height, depth = s.depth; - - unsigned int level; - for (level = 0; level < s.levels; level++) { - - unsigned int row_pitch = width * f.bytes_per_pixel; - unsigned int slice_pitch = row_pitch * height; - uint8_t *unswizzled = NULL; - if (f.encoding == swizzled) { - unswizzled = (uint8_t*)g_malloc(slice_pitch * depth); - unswizzle_box(texture_data, width, height, depth, unswizzled, - row_pitch, slice_pitch, f.bytes_per_pixel); - } - uint8_t *converted = convert_texture_data(s.color_format, unswizzled ? unswizzled : texture_data, - palette_data, - width, height, depth, - row_pitch, slice_pitch); - - resulting_format = converted ? converted_format : s.color_format; - ColorFormatInfo cf = kelvin_color_format_map[resulting_format]; - glTexImage3D(gl_target, level, cf.gl_internal_format, - width, height, depth, 0, - cf.gl_format, cf.gl_type, - converted ? converted : unswizzled ? unswizzled : texture_data); - - if (converted) { - g_free(converted); - } - if (unswizzled) { - g_free(unswizzled); - } - - texture_data += width * height * depth * f.bytes_per_pixel; - - width /= 2; - height /= 2; - depth /= 2; - } - break; - } - default: - assert(false); - break; - } - return resulting_format; -} - -static TextureBinding* generate_texture(const TextureShape s, - const uint8_t *texture_data, - const uint8_t *palette_data) -{ - // assert(pg->opengl_enabled); - - ColorFormatInfo f = kelvin_color_format_map[s.color_format]; - - /* Create a new opengl texture */ - GLuint gl_texture; - glGenTextures(1, &gl_texture); - - GLenum gl_target; - if (s.cubemap) { - assert(f.encoding != linear); - assert(s.dimensionality == 2); - gl_target = GL_TEXTURE_CUBE_MAP; - } else { - if (f.encoding == linear) { /* FIXME : Include compressed too? (!= swizzled) */ - /* linear textures use unnormalised texcoords. - * GL_TEXTURE_RECTANGLE_ARB conveniently also does, but - * does not allow repeat and mirror wrap modes. - * (or mipmapping, but xbox d3d says 'Non swizzled and non - * compressed textures cannot be mip mapped.') - * Not sure if that'll be an issue. */ - - /* FIXME: GLSL 330 provides us with textureSize()! Use that? */ - gl_target = GL_TEXTURE_RECTANGLE; - assert(s.dimensionality == 2); - } else { - switch(s.dimensionality) { - case 1: gl_target = GL_TEXTURE_1D; break; - case 2: gl_target = GL_TEXTURE_2D; break; - case 3: gl_target = GL_TEXTURE_3D; break; - default: - assert(false); - break; - } - } - } - - glBindTexture(gl_target, gl_texture); - - NV2A_GL_DLABEL(GL_TEXTURE, gl_texture, - "format: 0x%02X%s, %d dimensions%s, width: %d, height: %d, depth: %d", - s.color_format, (f.encoding == linear) ? "" : (f.encoding == swizzled) ? " (SZ)" : " (DXT)", // compressed - s.dimensionality, s.cubemap ? " (Cubemap)" : "", - s.width, s.height, s.depth); - - /* Linear textures don't support mipmapping */ - if (f.encoding != linear) { - glTexParameteri(gl_target, GL_TEXTURE_BASE_LEVEL, - s.min_mipmap_level); - glTexParameteri(gl_target, GL_TEXTURE_MAX_LEVEL, - s.levels - 1); - } - - /* Set this before calling upload_gl_texture() to prevent potential conversions */ - if (f.gl_swizzle_mask) { - glTexParameteriv(gl_target, GL_TEXTURE_SWIZZLE_RGBA, - f.gl_swizzle_mask); - } - - if (gl_target == GL_TEXTURE_CUBE_MAP) { - - size_t length = 0; - unsigned int w = s.width, h = s.height; - unsigned int level; - for (level = 0; level < s.levels; level++) { - /* FIXME: This is wrong for compressed textures and textures with 1x? non-square mipmaps */ - length += w * h * f.bytes_per_pixel; - w /= 2; - h /= 2; - } - - upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_X, - s, texture_data + 0 * length, palette_data); - upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - s, texture_data + 1 * length, palette_data); - upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, - s, texture_data + 2 * length, palette_data); - upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - s, texture_data + 3 * length, palette_data); - upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, - s, texture_data + 4 * length, palette_data); - upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, - s, texture_data + 5 * length, palette_data); - } else { - upload_gl_texture(gl_target, s, texture_data, palette_data); - } - - TextureBinding* ret = (TextureBinding *)g_malloc(sizeof(TextureBinding)); - ret->gl_target = gl_target; - ret->gl_texture = gl_texture; - ret->refcnt = 1; - return ret; -} - -// NOTE: Might want to change guint to guint64 for return. -/* functions for texture LRU cache */ -static guint texture_key_hash(gconstpointer key) -{ - const TextureKey *k = (const TextureKey *)key; - uint64_t state_hash = fnv_hash( - (const uint8_t*)&k->state, sizeof(TextureShape)); - return guint(state_hash ^ k->data_hash); -} -static gboolean texture_key_equal(gconstpointer a, gconstpointer b) -{ - const TextureKey *ak = (const TextureKey *)a, *bk = (const TextureKey *)b; - return memcmp(&ak->state, &bk->state, sizeof(TextureShape)) == 0 - && ak->data_hash == bk->data_hash; -} -static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error) -{ - const TextureKey *k = (const TextureKey *)key; - TextureBinding *v = generate_texture(k->state, - k->texture_data, - k->palette_data); - if (error != NULL) { - *error = NULL; - } - return v; -} -static void texture_key_destroy(gpointer data) -{ - g_free(data); -} -static void texture_binding_destroy(gpointer data) -{ - TextureBinding *binding = (TextureBinding *)data; - - // assert(pg->opengl_enabled); - - assert(binding->refcnt > 0); - binding->refcnt--; - if (binding->refcnt == 0) { - glDeleteTextures(1, &binding->gl_texture); - g_free(binding); - } -} - -// NOTE: Might want to change guint to guint64 for return. -/* hash and equality for shader cache hash table */ -static guint shader_hash(gconstpointer key) -{ - return (guint)fnv_hash((const uint8_t *)key, sizeof(ShaderState)); -} -static gboolean shader_equal(gconstpointer a, gconstpointer b) -{ - const ShaderState *as = (const ShaderState *)a, *bs = (const ShaderState *)b; - return memcmp(as, bs, sizeof(ShaderState)) == 0; -} - -static unsigned int kelvin_map_stencil_op(uint32_t parameter) -{ - unsigned int op; - switch (parameter) { - case NV097_SET_STENCIL_OP_V_KEEP: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_KEEP; break; - case NV097_SET_STENCIL_OP_V_ZERO: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_ZERO; break; - case NV097_SET_STENCIL_OP_V_REPLACE: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_REPLACE; break; - case NV097_SET_STENCIL_OP_V_INCRSAT: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCRSAT; break; - case NV097_SET_STENCIL_OP_V_DECRSAT: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECRSAT; break; - case NV097_SET_STENCIL_OP_V_INVERT: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INVERT; break; - case NV097_SET_STENCIL_OP_V_INCR: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCR; break; - case NV097_SET_STENCIL_OP_V_DECR: - op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECR; break; - default: - assert(false); - break; - } - return op; -} - -static unsigned int kelvin_map_polygon_mode(uint32_t parameter) -{ - unsigned int mode; - switch (parameter) { - case NV097_SET_FRONT_POLYGON_MODE_V_POINT: - mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_POINT; break; - case NV097_SET_FRONT_POLYGON_MODE_V_LINE: - mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_LINE; break; - case NV097_SET_FRONT_POLYGON_MODE_V_FILL: - mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_FILL; break; - default: - assert(false); - break; - } - return mode; -} - -static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel) -{ - assert(channel < 4); - unsigned int texgen; - switch (parameter) { - case NV097_SET_TEXGEN_S_DISABLE: - texgen = NV_PGRAPH_CSV1_A_T0_S_DISABLE; break; - case NV097_SET_TEXGEN_S_EYE_LINEAR: - texgen = NV_PGRAPH_CSV1_A_T0_S_EYE_LINEAR; break; - case NV097_SET_TEXGEN_S_OBJECT_LINEAR: - texgen = NV_PGRAPH_CSV1_A_T0_S_OBJECT_LINEAR; break; - case NV097_SET_TEXGEN_S_SPHERE_MAP: - assert(channel < 2); - texgen = NV_PGRAPH_CSV1_A_T0_S_SPHERE_MAP; break; - case NV097_SET_TEXGEN_S_REFLECTION_MAP: - assert(channel < 3); - texgen = NV_PGRAPH_CSV1_A_T0_S_REFLECTION_MAP; break; - case NV097_SET_TEXGEN_S_NORMAL_MAP: - assert(channel < 3); - texgen = NV_PGRAPH_CSV1_A_T0_S_NORMAL_MAP; break; - default: - assert(false); - break; - } - return texgen; -} - -static uint64_t fnv_hash(const uint8_t *data, size_t len) -{ - /* 64 bit Fowler/Noll/Vo FNV-1a hash code */ - uint64_t hval = 0xcbf29ce484222325ULL; - const uint8_t *dp = data; - const uint8_t *de = data + len; - while (dp < de) { - hval ^= (uint64_t) *dp++; - hval += (hval << 1) + (hval << 4) + (hval << 5) + - (hval << 7) + (hval << 8) + (hval << 40); - } - - return hval; -} - -static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples) -{ -#ifdef __SSE4_2__ - uint64_t h[4] = {len, 0, 0, 0}; - assert(samples > 0); - - if (len < 8 || len % 8) { - return fnv_hash(data, len); - } - - assert(len >= 8 && len % 8 == 0); - const uint64_t *dp = (const uint64_t*)data; - const uint64_t *de = dp + (len / 8); - size_t step = len / 8 / samples; - if (step == 0) step = 1; - - while (dp < de - step * 3) { - h[0] = __builtin_ia32_crc32di(h[0], dp[step * 0]); - h[1] = __builtin_ia32_crc32di(h[1], dp[step * 1]); - h[2] = __builtin_ia32_crc32di(h[2], dp[step * 2]); - h[3] = __builtin_ia32_crc32di(h[3], dp[step * 3]); - dp += step * 4; - } - if (dp < de - step * 0) - h[0] = __builtin_ia32_crc32di(h[0], dp[step * 0]); - if (dp < de - step * 1) - h[1] = __builtin_ia32_crc32di(h[1], dp[step * 1]); - if (dp < de - step * 2) - h[2] = __builtin_ia32_crc32di(h[2], dp[step * 2]); - - return h[0] + (h[1] << 10) + (h[2] << 21) + (h[3] << 32); -#else - return fnv_hash(data, len); -#endif -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * This file is heavily based on code from XQEMU +// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a_pgraph.c +// * Copyright (c) 2012 espes +// * Copyright (c) 2015 Jannik Vogel +// * Copyright (c) 2018 Matt Borgerson +// * +// * Contributions for Cxbx-Reloaded +// * Copyright (c) 2017-2018 Luke Usher +// * Copyright (c) 2018 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +// FIXME +#define qemu_mutex_lock_iothread() +#define qemu_mutex_unlock_iothread() + +// Xbox uses 4 KiB pages +#define TARGET_PAGE_BITS 12 +#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) +#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) +#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK) + +static const GLenum pgraph_texture_min_filter_map[] = { + 0, + GL_NEAREST, + GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, + GL_LINEAR_MIPMAP_LINEAR, + GL_LINEAR, /* TODO: Convolution filter... */ +}; + +static const GLenum pgraph_texture_mag_filter_map[] = { + 0, + GL_NEAREST, + GL_LINEAR, + 0, + GL_LINEAR /* TODO: Convolution filter... */ +}; + +static const GLenum pgraph_texture_addr_map[] = { + 0, + GL_REPEAT, + GL_MIRRORED_REPEAT, + GL_CLAMP_TO_EDGE, + GL_CLAMP_TO_BORDER, + // GL_CLAMP +}; + +static const GLenum pgraph_blend_factor_map[] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA_SATURATE, + 0, + GL_CONSTANT_COLOR, + GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, + GL_ONE_MINUS_CONSTANT_ALPHA, +}; + +static const GLenum pgraph_blend_equation_map[] = { + GL_FUNC_SUBTRACT, + GL_FUNC_REVERSE_SUBTRACT, + GL_FUNC_ADD, + GL_MIN, + GL_MAX, + GL_FUNC_REVERSE_SUBTRACT, + GL_FUNC_ADD, +}; + +static const GLenum pgraph_blend_logicop_map[] = { + GL_CLEAR, + GL_AND, + GL_AND_REVERSE, + GL_COPY, + GL_AND_INVERTED, + GL_NOOP, + GL_XOR, + GL_OR, + GL_NOR, + GL_EQUIV, + GL_INVERT, + GL_OR_REVERSE, + GL_COPY_INVERTED, + GL_OR_INVERTED, + GL_NAND, + GL_SET, +}; + +static const GLenum pgraph_cull_face_map[] = { + 0, + GL_FRONT, + GL_BACK, + GL_FRONT_AND_BACK +}; + +static const GLenum pgraph_depth_func_map[] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, +}; + +static const GLenum pgraph_stencil_func_map[] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, +}; + +static const GLenum pgraph_stencil_op_map[] = { + 0, + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR, + GL_DECR, + GL_INVERT, + GL_INCR_WRAP, + GL_DECR_WRAP, +}; + +enum FormatEncoding { + linear = 0, + swizzled, // for all NV097_SET_TEXTURE_FORMAT_*_SZ_* + compressed // for all NV097_SET_TEXTURE_FORMAT_*_DXT* +}; + +typedef struct ColorFormatInfo { + unsigned int bytes_per_pixel; // Derived from the total number of channel bits + FormatEncoding encoding; + GLint gl_internal_format; + GLenum gl_format; // == 0 for compressed formats + GLenum gl_type; + GLint *gl_swizzle_mask; // == nullptr when gl_internal_format, gl_format and gl_type are sufficient +} ColorFormatInfo; + +// Resulting gl_internal_format, gl_format and gl_type values, for formats handled by convert_texture_data() +#define GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV + +static GLint gl_swizzle_mask_0RG1[4] = { GL_ZERO, GL_RED, GL_GREEN, GL_ONE }; +static GLint gl_swizzle_mask_111R[4] = { GL_ONE, GL_ONE, GL_ONE, GL_RED }; +static GLint gl_swizzle_mask_ARGB[4] = { GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE }; +static GLint gl_swizzle_mask_BGRA[4] = { GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA }; +static GLint gl_swizzle_mask_GGGR[4] = { GL_GREEN, GL_GREEN, GL_GREEN, GL_RED }; +static GLint gl_swizzle_mask_R0G1[4] = { GL_RED, GL_ZERO, GL_GREEN, GL_ONE }; +static GLint gl_swizzle_mask_RRR1[4] = { GL_RED, GL_RED, GL_RED, GL_ONE }; +static GLint gl_swizzle_mask_RRRG[4] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; +static GLint gl_swizzle_mask_RRRR[4] = { GL_RED, GL_RED, GL_RED, GL_RED }; + +// Note : Avoid designated initializers to facilitate C++ builds +static const ColorFormatInfo kelvin_color_format_map[256] = { + //0x00 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y8] = + {1, swizzled, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + gl_swizzle_mask_RRR1}, + //0x01 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_AY8] = + {1, swizzled, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + gl_swizzle_mask_RRRR}, + //0x02 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A1R5G5B5] = + {2, swizzled, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x03 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X1R5G5B5] = + {2, swizzled, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x04 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A4R4G4B4] = + {2, swizzled, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, + //0x05 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G6B5] = + {2, swizzled, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + //0x06 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8] = + {4, swizzled, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x07 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X8R8G8B8] = + {4, swizzled, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x08 [?] = + {}, + //0x09 [?] = + {}, + //0x0A [?] = + {}, + + /* paletted texture */ + //0x0B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8] = // See convert_texture_data + {1, swizzled, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, + + //0x0C [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT1_A1R5G5B5] = + {4, compressed, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_RGBA}, + //0x0D [?] = + {}, + //0x0E [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT23_A8R8G8B8] = + {4, compressed, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_RGBA}, + //0x0F [NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT45_A8R8G8B8] = + {4, compressed, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_RGBA}, + //0x10 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A1R5G5B5] = + {2, linear, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x11 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5] = + {2, linear, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + //0x12 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8] = + {4, linear, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x13 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y8] = + {1, linear, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + gl_swizzle_mask_RRR1}, + //0x14 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_SY8] = + {1, linear, GL_R8_SNORM, GL_RED, GL_BYTE, + gl_swizzle_mask_RRR1}, // TODO : Verify + //0x15 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X7SY9] = // See convert_texture_data FIXME + {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + //0x16 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8B8] = + {2, linear, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + gl_swizzle_mask_R0G1}, // TODO : Verify + //0x17 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_G8B8] = + {2, linear, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + gl_swizzle_mask_0RG1}, // TODO : Verify + //0x18 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_SG8SB8] = + {2, linear, GL_RG8_SNORM, GL_RG, GL_BYTE, + gl_swizzle_mask_0RG1}, // TODO : Verify + + //0x19 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8] = + {1, swizzled, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + gl_swizzle_mask_111R}, + //0x1A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8Y8] = + {2, swizzled, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + gl_swizzle_mask_GGGR}, + //0x1B [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_AY8] = + {1, linear, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + gl_swizzle_mask_RRRR}, + //0x1C [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5] = + {2, linear, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x1D [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A4R4G4B4] = + {2, linear, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // TODO : Verify this is truely linear + //0x1E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8] = + {4, linear, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x1F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8] = + {1, linear, GL_R8, GL_RED, GL_UNSIGNED_BYTE, + gl_swizzle_mask_111R}, + //0x20 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8Y8] = + {2, linear, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + gl_swizzle_mask_GGGR}, + //0x21 [?] = + {}, + //0x22 [?] = + {}, + //0x23 [?] = + {}, + //0x24 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8] = // See convert_texture_data calling ____UYVYToARGBRow_C + {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + //0x25 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_YB8CR8YA8CB8] = // See convert_texture_data calling ____YUY2ToARGBRow_C + {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + //0x26 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8CR8CB8Y8] = // See convert_texture_data FIXME + {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + + //0x27 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5] = // See convert_texture_data calling __R6G5B5ToARGBRow_C + {2, swizzled, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + //0x28 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_G8B8] = + {2, swizzled, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + gl_swizzle_mask_0RG1}, + //0x29 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8B8] = + {2, swizzled, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, + gl_swizzle_mask_R0G1}, + //0x2A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_X8_Y24_FIXED] = + {4, swizzled, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // TODO : Verify + //0x2B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_X8_Y24_FLOAT] = + {4, swizzled, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // TODO : Verify + //0x2C [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_Y16_FIXED] = + {2, swizzled, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // TODO : Verify + //0x2D [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_Y16_FLOAT] = + {2, swizzled, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT}, // TODO : Verify + + + /* TODO: format conversion */ + //0x2E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] = + {4, linear, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, + //0x2F [?] = + {}, + //0x30 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FIXED] = + {2, linear, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, + //0x31 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FLOAT] = + {2, linear, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT}, // TODO : Verify + //0x32 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y16] = + {2, swizzled, GL_R16, GL_RED, GL_UNSIGNED_SHORT, // TODO : Verify + gl_swizzle_mask_RRR1}, + //0x33 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_YB16YA16] = + {4, swizzled, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, // TODO : Verify + gl_swizzle_mask_RRRG}, + //0x34 [NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_A4V6YB6A4U6YA6] = // TODO : handle in convert_texture_data + {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + //0x35 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y16] = + {2, linear, GL_R16, GL_RED, GL_UNSIGNED_SHORT, + gl_swizzle_mask_RRR1}, + //0x36 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_YB16YA16] = + {4, linear, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, // TODO : Verify + gl_swizzle_mask_RRRG}, + //0x37 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R6G5B5] = // See convert_texture_data calling __R6G5B5ToARGBRow_C + {2, linear, GL_CONVERT_TEXTURE_DATA_RESULTING_FORMAT}, // TODO : Verify + //0x38 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G5B5A1] = + {2, swizzled, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // TODO : Verify + //0x39 [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R4G4B4A4] = + {2, swizzled, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // TODO : Verify + //0x3A [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8B8G8R8] = + {4, swizzled, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // TODO : Verify + //0x3B [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_B8G8R8A8] = + {4, swizzled, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, // TODO : Verify + + //0x3C [NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8G8B8A8] = + {4, swizzled, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, + //0x3D [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G5B5A1] = + {2, linear, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // TODO : Verify + //0x3E [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R4G4B4A4] = + {2, linear, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // TODO : Verify + + //0x3F [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8B8G8R8] = + {4, linear, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // TODO : Verify + //0x40 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_B8G8R8A8] = + {4, linear, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, // TODO : Verify + //0x41 [NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8G8B8A8] = + {4, linear, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // TODO : Verify +}; + +typedef struct SurfaceColorFormatInfo { + unsigned int bytes_per_pixel; + GLint gl_internal_format; + GLenum gl_format; + GLenum gl_type; +} SurfaceColorFormatInfo; + +// Note : Avoid designated initializers to facilitate C++ builds +static const SurfaceColorFormatInfo kelvin_surface_color_format_map[16] = { + //0x00 [?] = + {}, + //0x01 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5] = + {2, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, + //0x02 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5] = + {}, + //0x03 [NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] = + {2, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, + //0x04 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8] = + {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x05 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8] = + {}, + //0x06 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8] = + {}, + //0x07 [NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8] = + {}, + //0x08 [NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] = + {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, + //0x09 [NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] = + {}, // TODO : {1, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // PatrickvL guesstimate + //0x0A [NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] = + {}, // TODO : {2, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // PatrickvL guesstimate + //0x0B [?] = + {}, + //0x0C [?] = + {}, + //0x0D [?] = + {}, + //0x0E [?] = + {}, + //0x0F [?] = + {} +}; + +void (*pgraph_draw_arrays)(NV2AState *d); +void (*pgraph_draw_inline_buffer)(NV2AState *d); +void (*pgraph_draw_inline_array)(NV2AState *d); +void (*pgraph_draw_inline_elements)(NV2AState *d); +void (*pgraph_draw_state_update)(NV2AState *d); +void (*pgraph_draw_clear)(NV2AState *d); + +//static void pgraph_set_context_user(NV2AState *d, uint32_t value); +void pgraph_handle_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter); +static void pgraph_log_method(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter); +static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr); +static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg); +static void pgraph_update_shader_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function); +static void pgraph_bind_shaders(PGRAPHState *pg); +static bool pgraph_get_framebuffer_dirty(PGRAPHState *pg); +static bool pgraph_get_color_write_enabled(PGRAPHState *pg); +static bool pgraph_get_zeta_write_enabled(PGRAPHState *pg); +static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta); +static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color); +static void pgraph_update_surface(NV2AState *d, bool upload, bool color_write, bool zeta_write); +static void pgraph_bind_textures(NV2AState *d); +static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height); +static void pgraph_get_surface_dimensions(PGRAPHState *pg, unsigned int *width, unsigned int *height); +static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool f); +static void pgraph_bind_vertex_attributes(NV2AState *d, unsigned int num_elements, bool inline_data, unsigned int inline_stride); +static unsigned int pgraph_bind_inline_array(NV2AState *d); + +static float convert_f16_to_float(uint16_t f16); +static float convert_f24_to_float(uint32_t f24); +static uint8_t* convert_texture_data(const unsigned int color_format, const uint8_t *data, const uint8_t *palette_data, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int row_pitch, const unsigned int slice_pitch); +static int upload_gl_texture(GLenum gl_target, const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); +static TextureBinding* generate_texture(const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); +static guint texture_key_hash(gconstpointer key); +static gboolean texture_key_equal(gconstpointer a, gconstpointer b); +static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error); +static void texture_key_destroy(gpointer data); +static void texture_binding_destroy(gpointer data); +static guint shader_hash(gconstpointer key); +static gboolean shader_equal(gconstpointer a, gconstpointer b); +static unsigned int kelvin_map_stencil_op(uint32_t parameter); +static unsigned int kelvin_map_polygon_mode(uint32_t parameter); +static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel); +static uint64_t fnv_hash(const uint8_t *data, size_t len); +static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples); + +/* PGRAPH - accelerated 2d/3d drawing engine */ +DEVICE_READ32(PGRAPH) +{ + qemu_mutex_lock(&d->pgraph.pgraph_lock); + + DEVICE_READ32_SWITCH() { + case NV_PGRAPH_INTR: + result = d->pgraph.pending_interrupts; + break; + case NV_PGRAPH_INTR_EN: + result = d->pgraph.enabled_interrupts; + break; + default: + DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH); + } + + qemu_mutex_unlock(&d->pgraph.pgraph_lock); + +// reg_log_read(NV_PGRAPH, addr, r); + + DEVICE_READ32_END(PGRAPH); +} + +DEVICE_WRITE32(PGRAPH) +{ +// reg_log_write(NV_PGRAPH, addr, val); + + qemu_mutex_lock(&d->pgraph.pgraph_lock); + + switch (addr) { + case NV_PGRAPH_INTR: + d->pgraph.pending_interrupts &= ~value; + qemu_cond_broadcast(&d->pgraph.interrupt_cond); + break; + case NV_PGRAPH_INTR_EN: + d->pgraph.enabled_interrupts = value; + break; + case NV_PGRAPH_INCREMENT: + if (value & NV_PGRAPH_INCREMENT_READ_3D) { + SET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_READ_3D, + (GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_READ_3D) + 1) + % GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_MODULO_3D)); + qemu_cond_broadcast(&d->pgraph.flip_3d); + } + break; + case NV_PGRAPH_CHANNEL_CTX_TRIGGER: { + xbaddr context_address = + GET_MASK(d->pgraph.regs[NV_PGRAPH_CHANNEL_CTX_POINTER], NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4; + + if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) { + unsigned pgraph_channel_id = + GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID); + + NV2A_DPRINTF("PGRAPH: read channel %d context from %" HWADDR_PRIx "\n", + pgraph_channel_id, context_address); + + uint8_t *context_ptr = d->pramin.ramin_ptr + context_address; + uint32_t context_user = ldl_le_p((uint32_t*)context_ptr); + + NV2A_DPRINTF(" - CTX_USER = 0x%08X\n", context_user); + + d->pgraph.regs[NV_PGRAPH_CTX_USER] = context_user; + // pgraph_set_context_user(d, context_user); + } + if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) { + /* do stuff ... */ + } + + break; + } + default: + DEVICE_WRITE32_REG(pgraph); // Was : DEBUG_WRITE32_UNHANDLED(PGRAPH); + break; + } + + // events + switch (addr) { + case NV_PGRAPH_FIFO: + qemu_cond_broadcast(&d->pgraph.fifo_access_cond); + break; + } + + qemu_mutex_unlock(&d->pgraph.pgraph_lock); + + DEVICE_WRITE32_END(PGRAPH); +} + +void OpenGL_draw_end(NV2AState *d); // forward declaration + +void OpenGL_draw_arrays(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + assert(pg->shader_binding); + + pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count, + false, 0); + glMultiDrawArrays(pg->shader_binding->gl_primitive_mode, + pg->gl_draw_arrays_start, + pg->gl_draw_arrays_count, + pg->draw_arrays_length); + + OpenGL_draw_end(d); +} + +void OpenGL_draw_inline_buffer(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + assert(pg->shader_binding); + + for (unsigned int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) { + VertexAttribute *vertex_attribute = &pg->vertex_attributes[i]; + + if (vertex_attribute->inline_buffer) { + + glBindBuffer(GL_ARRAY_BUFFER, + vertex_attribute->gl_inline_buffer); + glBufferData(GL_ARRAY_BUFFER, + pg->inline_buffer_length + * sizeof(float) * 4, + vertex_attribute->inline_buffer, + GL_DYNAMIC_DRAW); + + /* Clear buffer for next batch */ + g_free(vertex_attribute->inline_buffer); + vertex_attribute->inline_buffer = NULL; + + glVertexAttribPointer(i, 4, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(i); + } + else { + glDisableVertexAttribArray(i); + glVertexAttrib4fv(i, vertex_attribute->inline_value); + } + } + + glDrawArrays(pg->shader_binding->gl_primitive_mode, + 0, pg->inline_buffer_length); + + OpenGL_draw_end(d); +} + +void OpenGL_draw_inline_array(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + assert(pg->shader_binding); + + unsigned int index_count = pgraph_bind_inline_array(d); + glDrawArrays(pg->shader_binding->gl_primitive_mode, + 0, index_count); + + OpenGL_draw_end(d); +} + +void OpenGL_draw_inline_elements(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + assert(pg->shader_binding); + + uint32_t max_element = 0; + uint32_t min_element = (uint32_t)-1; + for (unsigned int i = 0; i < pg->inline_elements_length; i++) { + max_element = MAX(pg->inline_elements[i], max_element); + min_element = MIN(pg->inline_elements[i], min_element); + } + pgraph_bind_vertex_attributes(d, max_element + 1, false, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pg->gl_element_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + pg->inline_elements_length * sizeof(pg->inline_elements[0]), + pg->inline_elements, + GL_DYNAMIC_DRAW); + + glDrawRangeElements(pg->shader_binding->gl_primitive_mode, + min_element, max_element, + pg->inline_elements_length, + GL_UNSIGNED_SHORT, // Cxbx-Reloaded TODO : Restore GL_UNSIGNED_INT once HLE_draw_inline_elements can draw using uint32_t + (void*)0); + + OpenGL_draw_end(d); +} + +void OpenGL_draw_state_update(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + + NV2A_GL_DGROUP_BEGIN("NV097_SET_BEGIN_END: 0x%x", pg->primitive_mode); + + uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0]; + uint32_t control_1 = pg->regs[NV_PGRAPH_CONTROL_1]; + + bool depth_test = control_0 + & NV_PGRAPH_CONTROL_0_ZENABLE; + bool stencil_test = control_1 + & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE; + + pgraph_update_surface(d, true, true, depth_test || stencil_test); + + bool alpha = control_0 & NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE; + bool red = control_0 & NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE; + bool green = control_0 & NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE; + bool blue = control_0 & NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE; + glColorMask(red, green, blue, alpha); + glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE)); + glStencilMask(GET_MASK(control_1, + NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE)); + + uint32_t blend = pg->regs[NV_PGRAPH_BLEND]; + if (blend & NV_PGRAPH_BLEND_EN) { + glEnable(GL_BLEND); + uint32_t sfactor = GET_MASK(blend, + NV_PGRAPH_BLEND_SFACTOR); + uint32_t dfactor = GET_MASK(blend, + NV_PGRAPH_BLEND_DFACTOR); + assert(sfactor < ARRAY_SIZE(pgraph_blend_factor_map)); + assert(dfactor < ARRAY_SIZE(pgraph_blend_factor_map)); + glBlendFunc(pgraph_blend_factor_map[sfactor], + pgraph_blend_factor_map[dfactor]); + + uint32_t equation = GET_MASK(blend, + NV_PGRAPH_BLEND_EQN); + assert(equation < ARRAY_SIZE(pgraph_blend_equation_map)); + glBlendEquation(pgraph_blend_equation_map[equation]); + + uint32_t blend_color = pg->regs[NV_PGRAPH_BLENDCOLOR]; + glBlendColor(((blend_color >> 16) & 0xFF) / 255.0f, /* red */ + ((blend_color >> 8) & 0xFF) / 255.0f, /* green */ + (blend_color & 0xFF) / 255.0f, /* blue */ + ((blend_color >> 24) & 0xFF) / 255.0f);/* alpha */ + } + else { + glDisable(GL_BLEND); + } + + /* Face culling */ + uint32_t setupraster = pg->regs[NV_PGRAPH_SETUPRASTER]; + if (setupraster + & NV_PGRAPH_SETUPRASTER_CULLENABLE) { + uint32_t cull_face = GET_MASK(setupraster, + NV_PGRAPH_SETUPRASTER_CULLCTRL); + assert(cull_face < ARRAY_SIZE(pgraph_cull_face_map)); + glCullFace(pgraph_cull_face_map[cull_face]); + glEnable(GL_CULL_FACE); + } + else { + glDisable(GL_CULL_FACE); + } + + /* Front-face select */ + glFrontFace(setupraster + & NV_PGRAPH_SETUPRASTER_FRONTFACE + ? GL_CCW : GL_CW); + + /* Polygon offset */ + /* FIXME: GL implementation-specific, maybe do this in VS? */ + if (setupraster & + NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE) { + glEnable(GL_POLYGON_OFFSET_FILL); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + if (setupraster & + NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE) { + glEnable(GL_POLYGON_OFFSET_LINE); + } + else { + glDisable(GL_POLYGON_OFFSET_LINE); + } + if (setupraster & + NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE) { + glEnable(GL_POLYGON_OFFSET_POINT); + } + else { + glDisable(GL_POLYGON_OFFSET_POINT); + } + if (setupraster & + (NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE | + NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE | + NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) { + GLfloat zfactor = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETFACTOR]; + GLfloat zbias = *(float*)&pg->regs[NV_PGRAPH_ZOFFSETBIAS]; + glPolygonOffset(zfactor, zbias); + } + + /* Depth testing */ + if (depth_test) { + glEnable(GL_DEPTH_TEST); + + uint32_t depth_func = GET_MASK(control_0, + NV_PGRAPH_CONTROL_0_ZFUNC); + assert(depth_func < ARRAY_SIZE(pgraph_depth_func_map)); + glDepthFunc(pgraph_depth_func_map[depth_func]); + } + else { + glDisable(GL_DEPTH_TEST); + } + + if (stencil_test) { + glEnable(GL_STENCIL_TEST); + + uint32_t stencil_func = GET_MASK(control_1, + NV_PGRAPH_CONTROL_1_STENCIL_FUNC); + uint32_t stencil_ref = GET_MASK(control_1, + NV_PGRAPH_CONTROL_1_STENCIL_REF); + uint32_t func_mask = GET_MASK(control_1, + NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ); + uint32_t control2 = pg->regs[NV_PGRAPH_CONTROL_2]; + uint32_t op_fail = GET_MASK(control2, + NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL); + uint32_t op_zfail = GET_MASK(control2, + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL); + uint32_t op_zpass = GET_MASK(control2, + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS); + + assert(stencil_func < ARRAY_SIZE(pgraph_stencil_func_map)); + assert(op_fail < ARRAY_SIZE(pgraph_stencil_op_map)); + assert(op_zfail < ARRAY_SIZE(pgraph_stencil_op_map)); + assert(op_zpass < ARRAY_SIZE(pgraph_stencil_op_map)); + + glStencilFunc( + pgraph_stencil_func_map[stencil_func], + stencil_ref, + func_mask); + + glStencilOp( + pgraph_stencil_op_map[op_fail], + pgraph_stencil_op_map[op_zfail], + pgraph_stencil_op_map[op_zpass]); + + } + else { + glDisable(GL_STENCIL_TEST); + } + + /* Dither */ + /* FIXME: GL implementation dependent */ + if (control_0 & + NV_PGRAPH_CONTROL_0_DITHERENABLE) { + glEnable(GL_DITHER); + } + else { + glDisable(GL_DITHER); + } + + pgraph_bind_shaders(pg); + pgraph_bind_textures(d); + + //glDisableVertexAttribArray(NV2A_VERTEX_ATTR_DIFFUSE); + //glVertexAttrib4f(NV2A_VERTEX_ATTR_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f); + + unsigned int width, height; + pgraph_get_surface_dimensions(pg, &width, &height); + pgraph_apply_anti_aliasing_factor(pg, &width, &height); + glViewport(0, 0, width, height); + + /* Visibility testing */ + if (pg->zpass_pixel_count_enable) { + GLuint gl_query; + glGenQueries(1, &gl_query); + pg->gl_zpass_pixel_count_query_count++; + pg->gl_zpass_pixel_count_queries = (GLuint*)g_realloc( + pg->gl_zpass_pixel_count_queries, + sizeof(GLuint) * pg->gl_zpass_pixel_count_query_count); + pg->gl_zpass_pixel_count_queries[ + pg->gl_zpass_pixel_count_query_count - 1] = gl_query; + glBeginQuery(GL_SAMPLES_PASSED, gl_query); + } +} + +void OpenGL_draw_end(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + + /* End of visibility testing */ + if (pg->zpass_pixel_count_enable) { + glEndQuery(GL_SAMPLES_PASSED); + } + + NV2A_GL_DGROUP_END(); +} + +void OpenGL_draw_clear(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + + NV2A_DPRINTF("---------PRE CLEAR ------\n"); + GLbitfield gl_mask = 0; + + bool write_color = (pg->clear_surface & NV097_CLEAR_SURFACE_COLOR); + bool write_zeta = + (pg->clear_surface & (NV097_CLEAR_SURFACE_Z | NV097_CLEAR_SURFACE_STENCIL)); + + if (write_zeta) { + uint32_t clear_zstencil = + d->pgraph.regs[NV_PGRAPH_ZSTENCILCLEARVALUE]; + GLint gl_clear_stencil; + GLfloat gl_clear_depth; + + /* FIXME: Put these in some lookup table */ + const float f16_max = 511.9375f; + /* FIXME: 7 bits of mantissa unused. maybe use full buffer? */ + const float f24_max = 3.4027977E38f; + + switch (pg->surface_shape.zeta_format) { + case NV097_SET_SURFACE_FORMAT_ZETA_Z16: { + if (pg->clear_surface & NV097_CLEAR_SURFACE_Z) { + gl_mask |= GL_DEPTH_BUFFER_BIT; + uint16_t z = clear_zstencil & 0xFFFF; + if (pg->surface_shape.z_format) { + gl_clear_depth = convert_f16_to_float(z) / f16_max; + assert(false); /* FIXME: Untested */ + } + else { + gl_clear_depth = z / (float)0xFFFF; + } + } + break; + } + case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: { + if (pg->clear_surface & NV097_CLEAR_SURFACE_STENCIL) { + gl_mask |= GL_STENCIL_BUFFER_BIT; + gl_clear_stencil = clear_zstencil & 0xFF; + } + if (pg->clear_surface & NV097_CLEAR_SURFACE_Z) { + gl_mask |= GL_DEPTH_BUFFER_BIT; + uint32_t z = clear_zstencil >> 8; + if (pg->surface_shape.z_format) { + gl_clear_depth = convert_f24_to_float(z) / f24_max; + assert(false); /* FIXME: Untested */ + } + else { + gl_clear_depth = z / (float)0xFFFFFF; + } + } + break; + } + default: + fprintf(stderr, "Unknown zeta surface format: 0x%x\n", pg->surface_shape.zeta_format); + assert(false); + break; + } + + if (gl_mask & GL_DEPTH_BUFFER_BIT) { + glDepthMask(GL_TRUE); + glClearDepth(gl_clear_depth); + } + + if (gl_mask & GL_STENCIL_BUFFER_BIT) { + glStencilMask(0xff); + glClearStencil(gl_clear_stencil); + } + } + + if (write_color) { + gl_mask |= GL_COLOR_BUFFER_BIT; + glColorMask((pg->clear_surface & NV097_CLEAR_SURFACE_R) + ? GL_TRUE : GL_FALSE, + (pg->clear_surface & NV097_CLEAR_SURFACE_G) + ? GL_TRUE : GL_FALSE, + (pg->clear_surface & NV097_CLEAR_SURFACE_B) + ? GL_TRUE : GL_FALSE, + (pg->clear_surface & NV097_CLEAR_SURFACE_A) + ? GL_TRUE : GL_FALSE); + uint32_t clear_color = d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]; + + /* Handle RGB */ + GLfloat red, green, blue; + switch (pg->surface_shape.color_format) { + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_O1R5G5B5: + red = ((clear_color >> 10) & 0x1F) / 31.0f; + green = ((clear_color >> 5) & 0x1F) / 31.0f; + blue = (clear_color & 0x1F) / 31.0f; + assert(false); /* Untested */ + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5: + red = ((clear_color >> 11) & 0x1F) / 31.0f; + green = ((clear_color >> 5) & 0x3F) / 63.0f; + blue = (clear_color & 0x1F) / 31.0f; + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_O8R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: + red = ((clear_color >> 16) & 0xFF) / 255.0f; + green = ((clear_color >> 8) & 0xFF) / 255.0f; + blue = (clear_color & 0xFF) / 255.0f; + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8: + /* Xbox D3D doesn't support clearing those */ + default: + red = 1.0f; + green = 0.0f; + blue = 1.0f; + fprintf(stderr, "CLEAR_SURFACE for color_format 0x%x unsupported", + pg->surface_shape.color_format); + assert(false); + break; + } + + /* Handle alpha */ + GLfloat alpha; + switch (pg->surface_shape.color_format) { + /* FIXME: CLEAR_SURFACE seems to work like memset, so maybe we + * also have to clear non-alpha bits with alpha value? + * As GL doesn't own those pixels we'd have to do this on + * our own in xbox memory. + */ + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_Z1A7R8G8B8: + case NV097_SET_SURFACE_FORMAT_COLOR_LE_X1A7R8G8B8_O1A7R8G8B8: + alpha = ((clear_color >> 24) & 0x7F) / 127.0f; + assert(false); /* Untested */ + break; + case NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8: + alpha = ((clear_color >> 24) & 0xFF) / 255.0f; + break; + default: + alpha = 1.0f; + break; + } + + glClearColor(red, green, blue, alpha); + } + + if (gl_mask) { + pgraph_update_surface(d, true, write_color, write_zeta); + + glEnable(GL_SCISSOR_TEST); + + unsigned int xmin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX], + NV_PGRAPH_CLEARRECTX_XMIN); + unsigned int xmax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTX], + NV_PGRAPH_CLEARRECTX_XMAX); + unsigned int ymin = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY], + NV_PGRAPH_CLEARRECTY_YMIN); + unsigned int ymax = GET_MASK(pg->regs[NV_PGRAPH_CLEARRECTY], + NV_PGRAPH_CLEARRECTY_YMAX); + + unsigned int scissor_x = xmin; + unsigned int scissor_y = pg->surface_shape.clip_height - ymax - 1; + + unsigned int scissor_width = xmax - xmin + 1; + unsigned int scissor_height = ymax - ymin + 1; + + pgraph_apply_anti_aliasing_factor(pg, &scissor_x, &scissor_y); + pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height); + + /* FIXME: Should this really be inverted instead of ymin? */ + glScissor(scissor_x, scissor_y, scissor_width, scissor_height); + + /* FIXME: Respect window clip?!?! */ + + NV2A_DPRINTF("------------------CLEAR 0x%x %d,%d - %d,%d %x---------------\n", + parameter, xmin, ymin, xmax, ymax, d->pgraph.regs[NV_PGRAPH_COLORCLEARVALUE]); + + /* Dither */ + /* FIXME: Maybe also disable it here? + GL implementation dependent */ + if (pg->regs[NV_PGRAPH_CONTROL_0] & + NV_PGRAPH_CONTROL_0_DITHERENABLE) { + glEnable(GL_DITHER); + } + else { + glDisable(GL_DITHER); + } + + glClear(gl_mask); + + glDisable(GL_SCISSOR_TEST); + } + + pgraph_set_surface_dirty(pg, write_color, write_zeta); +} + +void OpenGL_init_pgraph_plugins() +{ + pgraph_draw_arrays = OpenGL_draw_arrays; + pgraph_draw_inline_buffer = OpenGL_draw_inline_buffer; + pgraph_draw_inline_array = OpenGL_draw_inline_array; + pgraph_draw_inline_elements = OpenGL_draw_inline_elements; + pgraph_draw_state_update = OpenGL_draw_state_update; + pgraph_draw_clear = OpenGL_draw_clear; +} + +void pgraph_handle_method(NV2AState *d, + unsigned int subchannel, + unsigned int method, + uint32_t parameter) +{ + unsigned int i; + unsigned int slot; + + PGRAPHState *pg = &d->pgraph; + + bool channel_valid = + d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] & NV_PGRAPH_CTX_CONTROL_CHID; + assert(channel_valid); + + unsigned channel_id = GET_MASK(pg->regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID); + + ContextSurfaces2DState *context_surfaces_2d = &pg->context_surfaces_2d; + ImageBlitState *image_blit = &pg->image_blit; + KelvinState *kelvin = &pg->kelvin; + + assert(subchannel < 8); + + if (method == NV_SET_OBJECT) { + assert(parameter < d->pramin.ramin_size); + uint8_t *obj_ptr = d->pramin.ramin_ptr + parameter; + + uint32_t ctx_1 = ldl_le_p((uint32_t*)obj_ptr); + uint32_t ctx_2 = ldl_le_p((uint32_t*)(obj_ptr+4)); + uint32_t ctx_3 = ldl_le_p((uint32_t*)(obj_ptr+8)); + uint32_t ctx_4 = ldl_le_p((uint32_t*)(obj_ptr+12)); + uint32_t ctx_5 = parameter; + + pg->regs[NV_PGRAPH_CTX_CACHE1 + subchannel * 4] = ctx_1; + pg->regs[NV_PGRAPH_CTX_CACHE2 + subchannel * 4] = ctx_2; + pg->regs[NV_PGRAPH_CTX_CACHE3 + subchannel * 4] = ctx_3; + pg->regs[NV_PGRAPH_CTX_CACHE4 + subchannel * 4] = ctx_4; + pg->regs[NV_PGRAPH_CTX_CACHE5 + subchannel * 4] = ctx_5; + } + + // is this right? + pg->regs[NV_PGRAPH_CTX_SWITCH1] = pg->regs[NV_PGRAPH_CTX_CACHE1 + subchannel * 4]; + pg->regs[NV_PGRAPH_CTX_SWITCH2] = pg->regs[NV_PGRAPH_CTX_CACHE2 + subchannel * 4]; + pg->regs[NV_PGRAPH_CTX_SWITCH3] = pg->regs[NV_PGRAPH_CTX_CACHE3 + subchannel * 4]; + pg->regs[NV_PGRAPH_CTX_SWITCH4] = pg->regs[NV_PGRAPH_CTX_CACHE4 + subchannel * 4]; + pg->regs[NV_PGRAPH_CTX_SWITCH5] = pg->regs[NV_PGRAPH_CTX_CACHE5 + subchannel * 4]; + + uint32_t graphics_class = GET_MASK(pg->regs[NV_PGRAPH_CTX_SWITCH1], + NV_PGRAPH_CTX_SWITCH1_GRCLASS); + + // Logging is slow.. disable for now.. + //pgraph_log_method(subchannel, graphics_class, method, parameter); + + if (subchannel != 0) { + // catches context switching issues on xbox d3d + assert(graphics_class != 0x97); + } + + /* ugly switch for now */ + switch (graphics_class) { + + case NV_CONTEXT_PATTERN: { + switch (method) { + case NV044_SET_MONOCHROME_COLOR0: + pg->regs[NV_PGRAPH_PATT_COLOR0] = parameter; + break; + } + + break; + } + + case NV_CONTEXT_SURFACES_2D: { + switch (method) { + case NV062_SET_OBJECT: + context_surfaces_2d->object_instance = parameter; + break; + case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE: + context_surfaces_2d->dma_image_source = parameter; + break; + case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN: + context_surfaces_2d->dma_image_dest = parameter; + break; + case NV062_SET_COLOR_FORMAT: + context_surfaces_2d->color_format = parameter; + break; + case NV062_SET_PITCH: + context_surfaces_2d->source_pitch = parameter & 0xFFFF; + context_surfaces_2d->dest_pitch = parameter >> 16; + break; + case NV062_SET_OFFSET_SOURCE: + context_surfaces_2d->source_offset = parameter & 0x07FFFFFF; + break; + case NV062_SET_OFFSET_DESTIN: + context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF; + break; + default: + EmuLog(LOG_LEVEL::WARNING, "Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X", method); + } + + break; + } + + case NV_IMAGE_BLIT: { + switch (method) { + case NV09F_SET_OBJECT: + image_blit->object_instance = parameter; + break; + case NV09F_SET_CONTEXT_SURFACES: + image_blit->context_surfaces = parameter; + break; + case NV09F_SET_OPERATION: + image_blit->operation = parameter; + break; + case NV09F_CONTROL_POINT_IN: + image_blit->in_x = parameter & 0xFFFF; + image_blit->in_y = parameter >> 16; + break; + case NV09F_CONTROL_POINT_OUT: + image_blit->out_x = parameter & 0xFFFF; + image_blit->out_y = parameter >> 16; + break; + case NV09F_SIZE: + image_blit->width = parameter & 0xFFFF; + image_blit->height = parameter >> 16; + + /* I guess this kicks it off? */ + if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { + + NV2A_GL_DPRINTF(true, "NV09F_SET_OPERATION_SRCCOPY"); + + ContextSurfaces2DState *context_surfaces = context_surfaces_2d; + assert(context_surfaces->object_instance + == image_blit->context_surfaces); + + unsigned int bytes_per_pixel; + switch (context_surfaces->color_format) { + case NV062_SET_COLOR_FORMAT_LE_Y8: + bytes_per_pixel = 1; + break; + case NV062_SET_COLOR_FORMAT_LE_R5G6B5: + bytes_per_pixel = 2; + break; + case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8: + bytes_per_pixel = 4; + break; + default: + printf("Unknown blit surface format: 0x%x\n", context_surfaces->color_format); + assert(false); + break; + } + + xbaddr source_dma_len, dest_dma_len; + uint8_t *source, *dest; + + source = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_source, + &source_dma_len); + assert(context_surfaces->source_offset < source_dma_len); + source += context_surfaces->source_offset; + + dest = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_dest, + &dest_dma_len); + assert(context_surfaces->dest_offset < dest_dma_len); + dest += context_surfaces->dest_offset; + + NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source - d->vram_ptr, + dest - d->vram_ptr); + + unsigned int y; + for (y = 0; yheight; y++) { + uint8_t *source_row = source + + (image_blit->in_y + y) * context_surfaces->source_pitch + + image_blit->in_x * bytes_per_pixel; + + uint8_t *dest_row = dest + + (image_blit->out_y + y) * context_surfaces->dest_pitch + + image_blit->out_x * bytes_per_pixel; + + memmove(dest_row, source_row, + image_blit->width * bytes_per_pixel); + } + + } else { + assert(false); + } + + break; + default: + EmuLog(LOG_LEVEL::WARNING, "Unknown NV_IMAGE_BLIT Method: 0x%08X", method); + } + break; + } + + case NV_KELVIN_PRIMITIVE: { + switch (method) { + case NV097_SET_OBJECT: + kelvin->object_instance = parameter; + break; + + case NV097_NO_OPERATION: + /* The bios uses nop as a software method call - + * it seems to expect a notify interrupt if the parameter isn't 0. + * According to a nouveau guy it should still be a nop regardless + * of the parameter. It's possible a debug register enables this, + * but nothing obvious sticks out. Weird. + */ + if (parameter != 0) { + assert(!(pg->pending_interrupts & NV_PGRAPH_INTR_ERROR)); + + SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR], + NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id); + SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR], + NV_PGRAPH_TRAPPED_ADDR_SUBCH, subchannel); + SET_MASK(pg->regs[NV_PGRAPH_TRAPPED_ADDR], + NV_PGRAPH_TRAPPED_ADDR_MTHD, method); + pg->regs[NV_PGRAPH_TRAPPED_DATA_LOW] = parameter; + pg->regs[NV_PGRAPH_NSOURCE] = NV_PGRAPH_NSOURCE_NOTIFICATION; /* TODO: check this */ + pg->pending_interrupts |= NV_PGRAPH_INTR_ERROR; + + qemu_mutex_unlock(&pg->pgraph_lock); + qemu_mutex_lock_iothread(); + update_irq(d); + qemu_mutex_lock(&pg->pgraph_lock); + qemu_mutex_unlock_iothread(); + + while (pg->pending_interrupts & NV_PGRAPH_INTR_ERROR) { + qemu_cond_wait(&pg->interrupt_cond, &pg->pgraph_lock); + } + } + break; + + case NV097_WAIT_FOR_IDLE: + pgraph_update_surface(d, false, true, true); + break; + + + case NV097_SET_FLIP_READ: + SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D, + parameter); + break; + case NV097_SET_FLIP_WRITE: + SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D, + parameter); + break; + case NV097_SET_FLIP_MODULO: + SET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D, + parameter); + break; + case NV097_FLIP_INCREMENT_WRITE: { + NV2A_DPRINTF("flip increment write %d -> ", + GET_MASK(pg->regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_WRITE_3D)); + SET_MASK(pg->regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_WRITE_3D, + (GET_MASK(pg->regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_WRITE_3D) + 1) + % GET_MASK(pg->regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_MODULO_3D)); + NV2A_DPRINTF("%d\n", + GET_MASK(pg->regs[NV_PGRAPH_SURFACE], + NV_PGRAPH_SURFACE_WRITE_3D)); + +#ifdef __APPLE__ + if (glFrameTerminatorGREMEDY) { + glFrameTerminatorGREMEDY(); + } +#endif // __APPLE__ + break; + } + case NV097_FLIP_STALL: + pgraph_update_surface(d, false, true, true); + + + // TODO: Fix this (why does it hang?) + /* while (true) */ { + uint32_t surface = pg->regs[NV_PGRAPH_SURFACE]; + NV2A_DPRINTF("flip stall read: %d, write: %d, modulo: %d\n", + GET_MASK(surface, NV_PGRAPH_SURFACE_READ_3D), + GET_MASK(surface, NV_PGRAPH_SURFACE_WRITE_3D), + GET_MASK(surface, NV_PGRAPH_SURFACE_MODULO_3D)); + + if (GET_MASK(surface, NV_PGRAPH_SURFACE_READ_3D) + != GET_MASK(surface, NV_PGRAPH_SURFACE_WRITE_3D)) { + break; + } + + //qemu_cond_wait(&pg->flip_3d, &pg->lock); + } + + // TODO: Remove this when the AMD crash is solved in vblank_thread + NV2ADevice::UpdateHostDisplay(d); + NV2A_DPRINTF("flip stall done\n"); + break; + + case NV097_SET_CONTEXT_DMA_NOTIFIES: + pg->dma_notifies = parameter; + break; + case NV097_SET_CONTEXT_DMA_A: + pg->dma_a = parameter; + break; + case NV097_SET_CONTEXT_DMA_B: + pg->dma_b = parameter; + break; + case NV097_SET_CONTEXT_DMA_STATE: + pg->dma_state = parameter; + break; + case NV097_SET_CONTEXT_DMA_COLOR: + /* try to get any straggling draws in before the surface's changed :/ */ + pgraph_update_surface(d, false, true, true); + + pg->dma_color = parameter; + break; + case NV097_SET_CONTEXT_DMA_ZETA: + pg->dma_zeta = parameter; + break; + case NV097_SET_CONTEXT_DMA_VERTEX_A: + pg->dma_vertex_a = parameter; + break; + case NV097_SET_CONTEXT_DMA_VERTEX_B: + pg->dma_vertex_b = parameter; + break; + case NV097_SET_CONTEXT_DMA_SEMAPHORE: + pg->dma_semaphore = parameter; + break; + case NV097_SET_CONTEXT_DMA_REPORT: + pg->dma_report = parameter; + break; + + case NV097_SET_SURFACE_CLIP_HORIZONTAL: + pgraph_update_surface(d, false, true, true); + + pg->surface_shape.clip_x = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X); + pg->surface_shape.clip_width = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH); + break; + case NV097_SET_SURFACE_CLIP_VERTICAL: + pgraph_update_surface(d, false, true, true); + + pg->surface_shape.clip_y = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y); + pg->surface_shape.clip_height = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT); + break; + case NV097_SET_SURFACE_FORMAT: + pgraph_update_surface(d, false, true, true); + + pg->surface_shape.color_format = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR); + pg->surface_shape.zeta_format = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA); + pg->surface_type = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE); + pg->surface_shape.anti_aliasing = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING); + pg->surface_shape.log_width = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH); + pg->surface_shape.log_height = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT); + break; + case NV097_SET_SURFACE_PITCH: + pgraph_update_surface(d, false, true, true); + + pg->surface_color.pitch = + GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR); + pg->surface_zeta.pitch = + GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA); + break; + case NV097_SET_SURFACE_COLOR_OFFSET: + pgraph_update_surface(d, false, true, true); + + pg->surface_color.offset = parameter; + break; + case NV097_SET_SURFACE_ZETA_OFFSET: + pgraph_update_surface(d, false, true, true); + + pg->surface_zeta.offset = parameter; + break; + + CASE_8(NV097_SET_COMBINER_ALPHA_ICW, 4) : + slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; + pg->regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter; + break; + + case NV097_SET_COMBINER_SPECULAR_FOG_CW0: + pg->regs[NV_PGRAPH_COMBINESPECFOG0] = parameter; + break; + + case NV097_SET_COMBINER_SPECULAR_FOG_CW1: + pg->regs[NV_PGRAPH_COMBINESPECFOG1] = parameter; + break; + + CASE_4(NV097_SET_TEXTURE_ADDRESS, 64): + slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64; + pg->regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter; + break; + case NV097_SET_CONTROL0: { + pgraph_update_surface(d, false, true, true); + + bool stencil_write_enable = + parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE; + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE, + stencil_write_enable); + + uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT); + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format); + + bool z_perspective = + parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE; + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, + z_perspective); + + int color_space_convert = + GET_MASK(parameter, NV097_SET_CONTROL0_COLOR_SPACE_CONVERT); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_CSCONVERT, + color_space_convert); + break; + } + + case NV097_SET_FOG_MODE: { + /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */ + unsigned int mode; + switch (parameter) { + case NV097_SET_FOG_MODE_V_LINEAR: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break; + case NV097_SET_FOG_MODE_V_EXP: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break; + case NV097_SET_FOG_MODE_V_EXP2: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break; + case NV097_SET_FOG_MODE_V_EXP_ABS: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break; + case NV097_SET_FOG_MODE_V_EXP2_ABS: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break; + case NV097_SET_FOG_MODE_V_LINEAR_ABS: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOG_MODE, + mode); + break; + } + case NV097_SET_FOG_GEN_MODE: { + unsigned int mode; + switch (parameter) { + case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break; + case NV097_SET_FOG_GEN_MODE_V_RADIAL: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break; + case NV097_SET_FOG_GEN_MODE_V_PLANAR: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break; + case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break; + case NV097_SET_FOG_GEN_MODE_V_FOG_X: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGGENMODE, mode); + break; + } + case NV097_SET_FOG_ENABLE: + /* + FIXME: There is also: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGENABLE, + parameter); + */ + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOGENABLE, + parameter); + break; + case NV097_SET_FOG_COLOR: { + /* parameter channels are ABGR, PGRAPH channels are ARGB */ + uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA); + uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE); + uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN); + uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_ALPHA, alpha); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_RED, red); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_GREEN, green); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue); + break; + } + case NV097_SET_WINDOW_CLIP_TYPE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE, parameter); + break; + CASE_8(NV097_SET_WINDOW_CLIP_HORIZONTAL, 4): + slot = (method - NV097_SET_WINDOW_CLIP_HORIZONTAL) / 4; + pg->regs[NV_PGRAPH_WINDOWCLIPX0 + slot * 4] = parameter; + break; + CASE_8(NV097_SET_WINDOW_CLIP_VERTICAL, 4): + slot = (method - NV097_SET_WINDOW_CLIP_VERTICAL) / 4; + pg->regs[NV_PGRAPH_WINDOWCLIPY0 + slot * 4] = parameter; + break; + case NV097_SET_ALPHA_TEST_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter); + break; + case NV097_SET_BLEND_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EN, parameter); + break; + case NV097_SET_CULL_FACE_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_CULLENABLE, + parameter); + break; + case NV097_SET_DEPTH_TEST_ENABLE: + // Test-case : Whiplash + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE, + parameter); + break; + case NV097_SET_DITHER_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter); + break; + case NV097_SET_LIGHTING_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING, + parameter); + break; + case NV097_SET_SKIN_MODE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN, + parameter); + break; + case NV097_SET_STENCIL_TEST_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter); + break; + case NV097_SET_POLY_OFFSET_POINT_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter); + break; + case NV097_SET_POLY_OFFSET_LINE_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter); + break; + case NV097_SET_POLY_OFFSET_FILL_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter); + break; + case NV097_SET_ALPHA_FUNC: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF); + break; + case NV097_SET_ALPHA_REF: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAREF, parameter); + break; + case NV097_SET_BLEND_FUNC_SFACTOR: { + unsigned int factor; + switch (parameter) { + case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO: + factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE: + factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; + default: + fprintf(stderr, "Unknown blend source factor: 0x%x\n", parameter); + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_SFACTOR, factor); + + break; + } + + case NV097_SET_BLEND_FUNC_DFACTOR: { + unsigned int factor; + switch (parameter) { + case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO: + factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE: + factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; + default: + fprintf(stderr, "Unknown blend destination factor: 0x%x\n", parameter); + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_DFACTOR, factor); + + break; + } + + case NV097_SET_BLEND_COLOR: + pg->regs[NV_PGRAPH_BLENDCOLOR] = parameter; + break; + + case NV097_SET_BLEND_EQUATION: { + unsigned int equation; + switch (parameter) { + case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT: + equation = 0; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT: + equation = 1; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_ADD: + equation = 2; break; + case NV097_SET_BLEND_EQUATION_V_MIN: + equation = 3; break; + case NV097_SET_BLEND_EQUATION_V_MAX: + equation = 4; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED: + equation = 5; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED: + equation = 6; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EQN, equation); + + break; + } + + case NV097_SET_DEPTH_FUNC: + // Test-case : Whiplash + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC, + parameter & 0xF); + break; + + case NV097_SET_COLOR_MASK: { + pg->surface_color.write_enabled_cache |= pgraph_get_color_write_enabled(pg); + + bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE; + bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE; + bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE; + bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE; + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue); + break; + } + case NV097_SET_DEPTH_MASK: + pg->surface_zeta.write_enabled_cache |= pgraph_get_zeta_write_enabled(pg); + + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter); + break; + case NV097_SET_STENCIL_MASK: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter); + break; + case NV097_SET_STENCIL_FUNC: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF); + break; + case NV097_SET_STENCIL_FUNC_REF: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter); + break; + case NV097_SET_STENCIL_FUNC_MASK: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter); + break; + case NV097_SET_STENCIL_OP_FAIL: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL, + kelvin_map_stencil_op(parameter)); + break; + case NV097_SET_STENCIL_OP_ZFAIL: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL, + kelvin_map_stencil_op(parameter)); + break; + case NV097_SET_STENCIL_OP_ZPASS: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS, + kelvin_map_stencil_op(parameter)); + break; + + case NV097_SET_POLYGON_OFFSET_SCALE_FACTOR: + pg->regs[NV_PGRAPH_ZOFFSETFACTOR] = parameter; + break; + case NV097_SET_POLYGON_OFFSET_BIAS: + pg->regs[NV_PGRAPH_ZOFFSETBIAS] = parameter; + break; + case NV097_SET_FRONT_POLYGON_MODE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACEMODE, + kelvin_map_polygon_mode(parameter)); + break; + case NV097_SET_BACK_POLYGON_MODE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_BACKFACEMODE, + kelvin_map_polygon_mode(parameter)); + break; + case NV097_SET_CLIP_MIN: + pg->regs[NV_PGRAPH_ZCLIPMIN] = parameter; + break; + case NV097_SET_CLIP_MAX: + pg->regs[NV_PGRAPH_ZCLIPMAX] = parameter; + break; + case NV097_SET_CULL_FACE: { + unsigned int face; + switch (parameter) { + case NV097_SET_CULL_FACE_V_FRONT: + face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break; + case NV097_SET_CULL_FACE_V_BACK: + face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break; + case NV097_SET_CULL_FACE_V_FRONT_AND_BACK: + face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_CULLCTRL, + face); + break; + } + case NV097_SET_FRONT_FACE: { + bool ccw; + switch (parameter) { + case NV097_SET_FRONT_FACE_V_CW: + ccw = false; break; + case NV097_SET_FRONT_FACE_V_CCW: + ccw = true; break; + default: + fprintf(stderr, "Unknown front face: 0x%x\n", parameter); + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACE, + ccw ? 1 : 0); + break; + } + case NV097_SET_NORMALIZATION_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, + parameter); + break; + + case NV097_SET_LIGHT_ENABLE_MASK: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_LIGHTS, + parameter); + break; + + CASE_4(NV097_SET_TEXGEN_S, 16) : { + slot = (method - NV097_SET_TEXGEN_S) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S + : NV_PGRAPH_CSV1_A_T0_S; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 0)); + break; + } + CASE_4(NV097_SET_TEXGEN_T, 16) : { + slot = (method - NV097_SET_TEXGEN_T) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T + : NV_PGRAPH_CSV1_A_T0_T; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 1)); + break; + } + CASE_4(NV097_SET_TEXGEN_R, 16) : { + slot = (method - NV097_SET_TEXGEN_R) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R + : NV_PGRAPH_CSV1_A_T0_R; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 2)); + break; + } + CASE_4(NV097_SET_TEXGEN_Q, 16) : { + slot = (method - NV097_SET_TEXGEN_Q) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q + : NV_PGRAPH_CSV1_A_T0_Q; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 3)); + break; + } + CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) : + slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4; + pg->texture_matrix_enable[slot] = parameter; + break; + + CASE_16(NV097_SET_PROJECTION_MATRIX, 4) : { + slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; + // pg->projection_matrix[slot] = *(float*)¶meter; + unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4; + pg->vsh_constants[row][slot % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_64(NV097_SET_MODEL_VIEW_MATRIX, 4) : { + slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; + unsigned int matnum = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_64(NV097_SET_INVERSE_MODEL_VIEW_MATRIX, 4) : { + slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; + unsigned int matnum = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_16(NV097_SET_COMPOSITE_MATRIX, 4) : { + slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; + unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4; + pg->vsh_constants[row][slot % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_64(NV097_SET_TEXTURE_MATRIX, 4) : { + slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; + unsigned int tex = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + CASE_3(NV097_SET_FOG_PARAMS, 4) : + slot = (method - NV097_SET_FOG_PARAMS) / 4; + pg->regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter; + /* Cxbx note: slot = 2 is right after slot = 1 */ + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; + break; + + /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ + CASE_64(NV097_SET_TEXGEN_PLANE_S, 4) : { + slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; + unsigned int tex = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + case NV097_SET_TEXGEN_VIEW_MODEL: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, + parameter); + break; + + CASE_4(NV097_SET_FOG_PLANE, 4): + slot = (method - NV097_SET_FOG_PLANE) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; + break; + + CASE_3(NV097_SET_SCENE_AMBIENT_COLOR, 4): + slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; + // ?? + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; + break; + + CASE_4(NV097_SET_VIEWPORT_OFFSET, 4): + slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; + break; + + CASE_4(NV097_SET_EYE_POSITION, 4): + slot = (method - NV097_SET_EYE_POSITION) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; + break; + + CASE_8(NV097_SET_COMBINER_FACTOR0, 4): + slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; + pg->regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_FACTOR1, 4): + slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; + pg->regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_ALPHA_OCW, 4): + slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; + pg->regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_COLOR_ICW, 4): + slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; + pg->regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter; + break; + + CASE_4(NV097_SET_VIEWPORT_SCALE, 4): + slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; + break; + + CASE_32(NV097_SET_TRANSFORM_PROGRAM, 4) : { + + slot = (method - NV097_SET_TRANSFORM_PROGRAM) / 4; + + int program_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR); + + assert(program_load < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); + pg->program_data[program_load][slot % 4] = parameter; + + if (slot % 4 == 3) { + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, program_load + 1); + } + + break; + } + + CASE_32(NV097_SET_TRANSFORM_CONSTANT, 4): { + + slot = (method - NV097_SET_TRANSFORM_CONSTANT) / 4; + + int const_load = GET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR); + + assert(const_load < NV2A_VERTEXSHADER_CONSTANTS); + // VertexShaderConstant *vsh_constant = &pg->vsh_constants[const_load]; + pg->vsh_constants_dirty[const_load] |= + (parameter != pg->vsh_constants[const_load][slot%4]); + pg->vsh_constants[const_load][slot%4] = parameter; + + if (slot % 4 == 3) { + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, const_load+1); + } + break; + } + + CASE_3(NV097_SET_VERTEX3F, 4) : { + slot = (method - NV097_SET_VERTEX3F) / 4; + VertexAttribute *vertex_attribute = + &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; + pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); + vertex_attribute->inline_value[slot] = *(float*)¶meter; + vertex_attribute->inline_value[3] = 1.0f; + if (slot == 2) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + + /* Handles NV097_SET_BACK_LIGHT_* */ + CASE_128(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): { + slot = (method - NV097_SET_BACK_LIGHT_AMBIENT_COLOR) / 4; + unsigned int part = NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4 + slot % 16; + slot /= 16; /* [Light index] */ + assert(slot < 8); + switch(part * 4) { + CASE_3(NV097_SET_BACK_LIGHT_AMBIENT_COLOR, 4): + part -= NV097_SET_BACK_LIGHT_AMBIENT_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BAMB + slot*6] = true; + break; + CASE_3(NV097_SET_BACK_LIGHT_DIFFUSE_COLOR, 4): + part -= NV097_SET_BACK_LIGHT_DIFFUSE_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BDIF + slot*6] = true; + break; + CASE_3(NV097_SET_BACK_LIGHT_SPECULAR_COLOR, 4): + part -= NV097_SET_BACK_LIGHT_SPECULAR_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_BSPC + slot*6] = true; + break; + default: + assert(false); + break; + } + break; + } + /* Handles all the light source props except for NV097_SET_BACK_LIGHT_* */ + CASE_256(NV097_SET_LIGHT_AMBIENT_COLOR, 4): { + slot = (method - NV097_SET_LIGHT_AMBIENT_COLOR) / 4; + unsigned int part = NV097_SET_LIGHT_AMBIENT_COLOR / 4 + slot % 32; + slot /= 32; /* [Light index] */ + assert(slot < 8); + switch(part * 4) { + CASE_3(NV097_SET_LIGHT_AMBIENT_COLOR, 4): + part -= NV097_SET_LIGHT_AMBIENT_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_AMB + slot*6] = true; + break; + CASE_3(NV097_SET_LIGHT_DIFFUSE_COLOR, 4): + part -= NV097_SET_LIGHT_DIFFUSE_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_DIF + slot*6] = true; + break; + CASE_3(NV097_SET_LIGHT_SPECULAR_COLOR, 4): + part -= NV097_SET_LIGHT_SPECULAR_COLOR / 4; + pg->ltctxb[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6][part] = parameter; + pg->ltctxb_dirty[NV_IGRAPH_XF_LTCTXB_L0_SPC + slot*6] = true; + break; + case NV097_SET_LIGHT_LOCAL_RANGE: + pg->ltc1[NV_IGRAPH_XF_LTC1_r0 + slot][0] = parameter; + pg->ltc1_dirty[NV_IGRAPH_XF_LTC1_r0 + slot] = true; + break; + CASE_3(NV097_SET_LIGHT_INFINITE_HALF_VECTOR, 4): + part -= NV097_SET_LIGHT_INFINITE_HALF_VECTOR / 4; + pg->light_infinite_half_vector[slot][part] = *(float*)¶meter; + break; + CASE_3(NV097_SET_LIGHT_INFINITE_DIRECTION, 4): + part -= NV097_SET_LIGHT_INFINITE_DIRECTION / 4; + pg->light_infinite_direction[slot][part] = *(float*)¶meter; + break; + CASE_3(NV097_SET_LIGHT_SPOT_FALLOFF, 4): + part -= NV097_SET_LIGHT_SPOT_FALLOFF / 4; + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2][part] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_K + slot*2] = true; + break; + CASE_4(NV097_SET_LIGHT_SPOT_DIRECTION, 4): + part -= NV097_SET_LIGHT_SPOT_DIRECTION / 4; + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2][part] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_L0_SPT + slot*2] = true; + break; + CASE_3(NV097_SET_LIGHT_LOCAL_POSITION, 4): + part -= NV097_SET_LIGHT_LOCAL_POSITION / 4; + pg->light_local_position[slot][part] = *(float*)¶meter; + break; + CASE_3(NV097_SET_LIGHT_LOCAL_ATTENUATION, 4): + part -= NV097_SET_LIGHT_LOCAL_ATTENUATION / 4; + pg->light_local_attenuation[slot][part] = *(float*)¶meter; + break; + default: + assert(false); + break; + } + break; + } + + CASE_4(NV097_SET_VERTEX4F, 4): { + slot = (method - NV097_SET_VERTEX4F) / 4; + VertexAttribute *vertex_attribute = + &pg->vertex_attributes[NV2A_VERTEX_ATTR_POSITION]; + pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_POSITION); + vertex_attribute->inline_value[slot] = *(float*)¶meter; + if (slot == 3) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + + CASE_16(NV097_SET_VERTEX_DATA_ARRAY_FORMAT, 4): { + + slot = (method - NV097_SET_VERTEX_DATA_ARRAY_FORMAT) / 4; + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + + vertex_attribute->format = + GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE); + vertex_attribute->count = + GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE); + vertex_attribute->stride = + GET_MASK(parameter, NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE); + + NV2A_DPRINTF("vertex data array format=%d, count=%d, stride=%d\n", + vertex_attribute->format, + vertex_attribute->count, + vertex_attribute->stride); + + vertex_attribute->gl_count = vertex_attribute->count; + + switch (vertex_attribute->format) { + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D: + vertex_attribute->gl_type = GL_UNSIGNED_BYTE; + vertex_attribute->gl_normalize = GL_TRUE; + vertex_attribute->size = 1; + assert(vertex_attribute->count == 4); + // https://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt + vertex_attribute->gl_count = GL_BGRA; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL: + vertex_attribute->gl_type = GL_UNSIGNED_BYTE; + vertex_attribute->gl_normalize = GL_TRUE; + vertex_attribute->size = 1; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1: + vertex_attribute->gl_type = GL_SHORT; + vertex_attribute->gl_normalize = GL_TRUE; + vertex_attribute->size = 2; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F: + vertex_attribute->gl_type = GL_FLOAT; + vertex_attribute->gl_normalize = GL_FALSE; + vertex_attribute->size = 4; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K: + vertex_attribute->gl_type = GL_SHORT; + vertex_attribute->gl_normalize = GL_FALSE; + vertex_attribute->size = 2; + vertex_attribute->needs_conversion = false; + break; + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: + /* 3 signed, normalized components packed in 32-bits. (11,11,10) */ + vertex_attribute->size = 4; + vertex_attribute->gl_type = GL_FLOAT; + vertex_attribute->gl_normalize = GL_FALSE; + vertex_attribute->needs_conversion = true; + vertex_attribute->converted_size = sizeof(float); + vertex_attribute->converted_count = 3 * vertex_attribute->count; + break; + default: + fprintf(stderr, "Unknown vertex type: 0x%x\n", vertex_attribute->format); + assert(false); + break; + } + + if (vertex_attribute->needs_conversion) { + vertex_attribute->converted_elements = 0; + } else { + if (vertex_attribute->converted_buffer) { + g_free(vertex_attribute->converted_buffer); + vertex_attribute->converted_buffer = NULL; + } + } + + break; + } + + CASE_16(NV097_SET_VERTEX_DATA_ARRAY_OFFSET, 4): { + + slot = (method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4; + + pg->vertex_attributes[slot].dma_select = + parameter & 0x80000000; + pg->vertex_attributes[slot].offset = + parameter & 0x7fffffff; + + pg->vertex_attributes[slot].converted_elements = 0; + + break; + } + + case NV097_SET_LOGIC_OP_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_LOGICOP_ENABLE, parameter); + break; + + case NV097_SET_LOGIC_OP: + SET_MASK(pg->regs[NV_PGRAPH_BLEND], + NV_PGRAPH_BLEND_LOGICOP, parameter & 0xF); + break; + + case NV097_CLEAR_REPORT_VALUE: + + /* FIXME: Does this have a value in parameter? Also does this (also?) modify + * the report memory block? + */ + if (pg->gl_zpass_pixel_count_query_count) { + if (pg->opengl_enabled) { + glDeleteQueries(pg->gl_zpass_pixel_count_query_count, + pg->gl_zpass_pixel_count_queries); + } + pg->gl_zpass_pixel_count_query_count = 0; + } + pg->zpass_pixel_count_result = 0; + + break; + + case NV097_SET_ZPASS_PIXEL_COUNT_ENABLE: + pg->zpass_pixel_count_enable = parameter; + break; + + case NV097_GET_REPORT: { + /* FIXME: This was first intended to be watchpoint-based. However, + * qemu / kvm only supports virtual-address watchpoints. + * This'll do for now, but accuracy and performance with other + * approaches could be better + */ + uint8_t type = GET_MASK(parameter, NV097_GET_REPORT_TYPE); + assert(type == NV097_GET_REPORT_TYPE_ZPASS_PIXEL_CNT); + hwaddr offset = GET_MASK(parameter, NV097_GET_REPORT_OFFSET); + + uint64_t timestamp = 0x0011223344556677; /* FIXME: Update timestamp?! */ + uint32_t done = 0; + + if (pg->opengl_enabled) { + /* FIXME: Multisampling affects this (both: OGL and Xbox GPU), + * not sure if CLEARs also count + */ + /* FIXME: What about clipping regions etc? */ + for(i = 0; i < pg->gl_zpass_pixel_count_query_count; i++) { + GLuint gl_query_result; + glGetQueryObjectuiv(pg->gl_zpass_pixel_count_queries[i], + GL_QUERY_RESULT, + &gl_query_result); + pg->zpass_pixel_count_result += gl_query_result; + } + if (pg->gl_zpass_pixel_count_query_count) { + glDeleteQueries(pg->gl_zpass_pixel_count_query_count, + pg->gl_zpass_pixel_count_queries); + } + pg->gl_zpass_pixel_count_query_count = 0; + + hwaddr report_dma_len; + uint8_t *report_data = (uint8_t*)nv_dma_map(d, pg->dma_report, + &report_dma_len); + assert(offset < report_dma_len); + report_data += offset; + + stq_le_p((uint64_t*)&report_data[0], timestamp); + stl_le_p((uint32_t*)&report_data[8], pg->zpass_pixel_count_result); + stl_le_p((uint32_t*)&report_data[12], done); + } + + break; + } + + CASE_3(NV097_SET_EYE_DIRECTION, 4): + slot = (method - NV097_SET_EYE_DIRECTION) / 4; + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_EYED][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_EYED] = true; + break; + + case NV097_SET_BEGIN_END: { + uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0]; + uint32_t control_1 = pg->regs[NV_PGRAPH_CONTROL_1]; + + bool depth_test = control_0 + & NV_PGRAPH_CONTROL_0_ZENABLE; + bool stencil_test = control_1 + & NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE; + + if (parameter == NV097_SET_BEGIN_END_OP_END) { + + if (pg->draw_arrays_length) { + + NV2A_GL_DPRINTF(false, "Draw Arrays"); + + assert(pg->inline_buffer_length == 0); + assert(pg->inline_array_length == 0); + assert(pg->inline_elements_length == 0); + + if (pgraph_draw_arrays != nullptr) { + pgraph_draw_arrays(d); + } + } else if (pg->inline_buffer_length) { + + NV2A_GL_DPRINTF(false, "Inline Buffer"); + + assert(pg->draw_arrays_length == 0); + assert(pg->inline_array_length == 0); + assert(pg->inline_elements_length == 0); + + if (pgraph_draw_inline_buffer != nullptr) { + pgraph_draw_inline_buffer(d); + } + } else if (pg->inline_array_length) { + + NV2A_GL_DPRINTF(false, "Inline Array"); + + assert(pg->draw_arrays_length == 0); + assert(pg->inline_buffer_length == 0); + assert(pg->inline_elements_length == 0); + + if (pgraph_draw_inline_array != nullptr) { + pgraph_draw_inline_array(d); + } + } else if (pg->inline_elements_length) { + + NV2A_GL_DPRINTF(false, "Inline Elements"); + + assert(pg->draw_arrays_length == 0); + assert(pg->inline_buffer_length == 0); + assert(pg->inline_array_length == 0); + + if (pgraph_draw_inline_elements != nullptr) { + pgraph_draw_inline_elements(d); + } + } else { + NV2A_GL_DPRINTF(true, "EMPTY NV097_SET_BEGIN_END"); + assert(false); + } + } else { + + assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON); + + pg->primitive_mode = parameter; + + if (pgraph_draw_state_update != nullptr) { + pgraph_draw_state_update(d); + } + + pg->inline_elements_length = 0; + pg->inline_array_length = 0; + pg->inline_buffer_length = 0; + pg->draw_arrays_length = 0; + pg->draw_arrays_max_count = 0; + } + + pgraph_set_surface_dirty(pg, true, depth_test || stencil_test); + break; + } + CASE_4(NV097_SET_TEXTURE_OFFSET, 64): + slot = (method - NV097_SET_TEXTURE_OFFSET) / 64; + pg->regs[NV_PGRAPH_TEXOFFSET0 + slot * 4] = parameter; + pg->texture_dirty[slot] = true; + break; + CASE_4(NV097_SET_TEXTURE_FORMAT, 64): { + slot = (method - NV097_SET_TEXTURE_FORMAT) / 64; + + bool dma_select = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_CONTEXT_DMA) == 2; + bool cubemap = + parameter & NV097_SET_TEXTURE_FORMAT_CUBEMAP_ENABLE; + bool border_source = + parameter & NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE; + unsigned int dimensionality = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_DIMENSIONALITY); + unsigned int color_format = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_COLOR); + unsigned int levels = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_MIPMAP_LEVELS); + unsigned int log_width = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_U); + unsigned int log_height = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_V); + unsigned int log_depth = + GET_MASK(parameter, NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P); + + uint32_t *reg = &pg->regs[NV_PGRAPH_TEXFMT0 + slot * 4]; + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CONTEXT_DMA, dma_select); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_CUBEMAPENABLE, cubemap); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BORDER_SOURCE, border_source); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_DIMENSIONALITY, dimensionality); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_COLOR, color_format); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS, levels); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_U, log_width); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_V, log_height); + SET_MASK(*reg, NV_PGRAPH_TEXFMT0_BASE_SIZE_P, log_depth); + + pg->texture_dirty[slot] = true; + break; + } + CASE_4(NV097_SET_TEXTURE_CONTROL0, 64): + slot = (method - NV097_SET_TEXTURE_CONTROL0) / 64; + pg->regs[NV_PGRAPH_TEXCTL0_0 + slot*4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_CONTROL1, 64): + slot = (method - NV097_SET_TEXTURE_CONTROL1) / 64; + pg->regs[NV_PGRAPH_TEXCTL1_0 + slot*4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_FILTER, 64): + slot = (method - NV097_SET_TEXTURE_FILTER) / 64; + pg->regs[NV_PGRAPH_TEXFILTER0 + slot * 4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_IMAGE_RECT, 64): + slot = (method - NV097_SET_TEXTURE_IMAGE_RECT) / 64; + pg->regs[NV_PGRAPH_TEXIMAGERECT0 + slot * 4] = parameter; + pg->texture_dirty[slot] = true; + break; + CASE_4(NV097_SET_TEXTURE_PALETTE, 64): { + slot = (method - NV097_SET_TEXTURE_PALETTE) / 64; + + bool dma_select = + GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_CONTEXT_DMA) == 1; + unsigned int length = + GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_LENGTH); + unsigned int offset = + GET_MASK(parameter, NV097_SET_TEXTURE_PALETTE_OFFSET); + + uint32_t *reg = &pg->regs[NV_PGRAPH_TEXPALETTE0 + slot * 4]; + SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA, dma_select); + SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_LENGTH, length); + SET_MASK(*reg, NV_PGRAPH_TEXPALETTE0_OFFSET, offset); + + pg->texture_dirty[slot] = true; + break; + } + + CASE_4(NV097_SET_TEXTURE_BORDER_COLOR, 64): + slot = (method - NV097_SET_TEXTURE_BORDER_COLOR) / 64; + pg->regs[NV_PGRAPH_BORDERCOLOR0 + slot * 4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x0, 64): + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x4, 64): + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0x8, 64): + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_MAT + 0xc, 64): + slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_MAT) / 4; + assert((slot / 16) > 0); + slot -= 16; + pg->bump_env_matrix[slot / 16][slot % 4] = *(float*)¶meter; + break; + + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE, 64): + slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_SCALE) / 64; + assert(slot > 0); + slot--; + pg->regs[NV_PGRAPH_BUMPSCALE1 + slot * 4] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET, 64): + slot = (method - NV097_SET_TEXTURE_SET_BUMP_ENV_OFFSET) / 64; + assert(slot > 0); + slot--; + pg->regs[NV_PGRAPH_BUMPOFFSET1 + slot * 4] = parameter; + break; + + case NV097_ARRAY_ELEMENT16: + //LOG_TEST_CASE("NV2A_VB_ELEMENT_U16"); + // Test-case : Turok (in main menu) + // Test-case : Hunter Redeemer + // Test-case : Otogi (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/pull/1113#issuecomment-385593814) + assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); + pg->inline_elements[ + pg->inline_elements_length++] = parameter & 0xFFFF; + pg->inline_elements[ + pg->inline_elements_length++] = parameter >> 16; + break; + case NV097_ARRAY_ELEMENT32: + //LOG_TEST_CASE("NV2A_VB_ELEMENT_U32"); + // Test-case : Turok (in main menu) + assert(pg->inline_elements_length < NV2A_MAX_BATCH_LENGTH); + pg->inline_elements[ + pg->inline_elements_length++] = parameter; + break; + case NV097_DRAW_ARRAYS: { + + unsigned int start = GET_MASK(parameter, NV097_DRAW_ARRAYS_START_INDEX); + unsigned int count = GET_MASK(parameter, NV097_DRAW_ARRAYS_COUNT)+1; + + pg->draw_arrays_max_count = MAX(pg->draw_arrays_max_count, start + count); + + assert(pg->draw_arrays_length < ARRAY_SIZE(pg->gl_draw_arrays_start)); + + /* Attempt to connect primitives */ + if (pg->draw_arrays_length > 0) { + unsigned int last_start = + pg->gl_draw_arrays_start[pg->draw_arrays_length - 1]; + GLsizei* last_count = + &pg->gl_draw_arrays_count[pg->draw_arrays_length - 1]; + if (start == (last_start + *last_count)) { + *last_count += count; + break; + } + } + + pg->gl_draw_arrays_start[pg->draw_arrays_length] = start; + pg->gl_draw_arrays_count[pg->draw_arrays_length] = count; + pg->draw_arrays_length++; + break; + } + case NV097_INLINE_ARRAY: + assert(pg->inline_array_length < NV2A_MAX_BATCH_LENGTH); + pg->inline_array[ + pg->inline_array_length++] = parameter; + break; + CASE_3(NV097_SET_EYE_VECTOR, 4): + slot = (method - NV097_SET_EYE_VECTOR) / 4; + pg->regs[NV_PGRAPH_EYEVEC0 + slot * 4] = parameter; + break; + + CASE_32(NV097_SET_VERTEX_DATA2F_M, 4): { + slot = (method - NV097_SET_VERTEX_DATA2F_M) / 4; + unsigned int part = slot % 2; + slot /= 2; + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + vertex_attribute->inline_value[part] = *(float*)¶meter; + /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ + vertex_attribute->inline_value[2] = 0.0f; + vertex_attribute->inline_value[3] = 1.0f; + if ((slot == 0) && (part == 1)) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + CASE_64(NV097_SET_VERTEX_DATA4F_M, 4): { + slot = (method - NV097_SET_VERTEX_DATA4F_M) / 4; + unsigned int part = slot % 4; + slot /= 4; + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + vertex_attribute->inline_value[part] = *(float*)¶meter; + if ((slot == 0) && (part == 3)) { + pgraph_finish_inline_buffer_vertex(pg); + } + break; + } + CASE_16(NV097_SET_VERTEX_DATA2S, 4): { + slot = (method - NV097_SET_VERTEX_DATA2S) / 4; + assert(false); /* FIXME: Untested! */ + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + /* FIXME: Is mapping to [-1,+1] correct? */ + vertex_attribute->inline_value[0] = ((int16_t)(parameter & 0xFFFF) * 2.0f + 1) + / 65535.0f; + vertex_attribute->inline_value[1] = ((int16_t)(parameter >> 16) * 2.0f + 1) + / 65535.0f; + /* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */ + vertex_attribute->inline_value[2] = 0.0f; + vertex_attribute->inline_value[3] = 1.0f; + if (slot == 0) { + pgraph_finish_inline_buffer_vertex(pg); + assert(false); /* FIXME: Untested */ + } + break; + } + CASE_16(NV097_SET_VERTEX_DATA4UB, 4) : { + slot = (method - NV097_SET_VERTEX_DATA4UB) / 4; + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + vertex_attribute->inline_value[0] = (parameter & 0xFF) / 255.0f; + vertex_attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f; + vertex_attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f; + vertex_attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f; + if (slot == 0) { + pgraph_finish_inline_buffer_vertex(pg); + assert(false); /* FIXME: Untested */ + } + break; + } + CASE_32(NV097_SET_VERTEX_DATA4S_M, 4) : { + slot = (method - NV097_SET_VERTEX_DATA4S_M) / 4; + unsigned int part = slot % 2; + slot /= 2; + assert(false); /* FIXME: Untested! */ + VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot]; + pgraph_allocate_inline_buffer_vertices(pg, slot); + /* FIXME: Is mapping to [-1,+1] correct? */ + vertex_attribute->inline_value[part * 2 + 0] = ((int16_t)(parameter & 0xFFFF) + * 2.0f + 1) / 65535.0f; + vertex_attribute->inline_value[part * 2 + 1] = ((int16_t)(parameter >> 16) + * 2.0f + 1) / 65535.0f; + if ((slot == 0) && (part == 1)) { + pgraph_finish_inline_buffer_vertex(pg); + assert(false); /* FIXME: Untested */ + } + break; + } + case NV097_SET_SEMAPHORE_OFFSET: + pg->regs[NV_PGRAPH_SEMAPHOREOFFSET] = parameter; + break; + case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: { + pgraph_update_surface(d, false, true, true); + + //qemu_mutex_unlock(&pg->pgraph_lock); + //qemu_mutex_lock_iothread(); + + uint32_t semaphore_offset = pg->regs[NV_PGRAPH_SEMAPHOREOFFSET]; + + xbaddr semaphore_dma_len; + uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, pg->dma_semaphore, + &semaphore_dma_len); + assert(semaphore_offset < semaphore_dma_len); + semaphore_data += semaphore_offset; + + stl_le_p((uint32_t*)semaphore_data, parameter); + + //qemu_mutex_lock(&pg->pgraph_lock); + //qemu_mutex_unlock_iothread(); + + break; + } + case NV097_SET_ZSTENCIL_CLEAR_VALUE: + pg->regs[NV_PGRAPH_ZSTENCILCLEARVALUE] = parameter; + break; + + case NV097_SET_COLOR_CLEAR_VALUE: + pg->regs[NV_PGRAPH_COLORCLEARVALUE] = parameter; + break; + + case NV097_CLEAR_SURFACE: { + pg->clear_surface = parameter; + if (pgraph_draw_clear != nullptr) { + pgraph_draw_clear(d); + } + break; + } + + case NV097_SET_CLEAR_RECT_HORIZONTAL: + pg->regs[NV_PGRAPH_CLEARRECTX] = parameter; + break; + case NV097_SET_CLEAR_RECT_VERTICAL: + pg->regs[NV_PGRAPH_CLEARRECTY] = parameter; + break; + + CASE_2(NV097_SET_SPECULAR_FOG_FACTOR, 4) : + slot = (method - NV097_SET_SPECULAR_FOG_FACTOR) / 4; + pg->regs[NV_PGRAPH_SPECFOGFACTOR0 + slot * 4] = parameter; + break; + + case NV097_SET_SHADER_CLIP_PLANE_MODE: + pg->regs[NV_PGRAPH_SHADERCLIPMODE] = parameter; + break; + + CASE_8(NV097_SET_COMBINER_COLOR_OCW, 4) : + slot = (method - NV097_SET_COMBINER_COLOR_OCW) / 4; + pg->regs[NV_PGRAPH_COMBINECOLORO0 + slot * 4] = parameter; + break; + + case NV097_SET_COMBINER_CONTROL: + pg->regs[NV_PGRAPH_COMBINECTL] = parameter; + break; + + case NV097_SET_SHADOW_ZSLOPE_THRESHOLD: + pg->regs[NV_PGRAPH_SHADOWZSLOPETHRESHOLD] = parameter; + assert(parameter == 0x7F800000); /* FIXME: Unimplemented */ + break; + + case NV097_SET_SHADER_STAGE_PROGRAM: + pg->regs[NV_PGRAPH_SHADERPROG] = parameter; + break; + + case NV097_SET_SHADER_OTHER_STAGE_INPUT: + pg->regs[NV_PGRAPH_SHADERCTL] = parameter; + break; + + case NV097_SET_TRANSFORM_EXECUTION_MODE: + // Test-case : Whiplash + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_MODE, + GET_MASK(parameter, + NV097_SET_TRANSFORM_EXECUTION_MODE_MODE)); + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_RANGE_MODE, + GET_MASK(parameter, + NV097_SET_TRANSFORM_EXECUTION_MODE_RANGE_MODE)); + break; + case NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN: + // Test-case : Whiplash + pg->enable_vertex_program_write = parameter; + break; + case NV097_SET_TRANSFORM_PROGRAM_LOAD: + assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_PROG_LD_PTR, parameter); + break; + case NV097_SET_TRANSFORM_PROGRAM_START: + assert(parameter < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH); + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START, parameter); + break; + case NV097_SET_TRANSFORM_CONSTANT_LOAD: + assert(parameter < NV2A_VERTEXSHADER_CONSTANTS); + SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET], + NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, parameter); + NV2A_DPRINTF("load to %d\n", parameter); + break; + + case NV097_SET_FLAT_SHADE_OP: + assert(parameter <= 1); + // TODO : value & 1 = first/last? vertex selection for glShaderMode(GL_FLAT) + break; + default: + NV2A_GL_DPRINTF(true, " unhandled (0x%02x 0x%08x)", + graphics_class, method); + break; + } + break; + } + + default: + NV2A_GL_DPRINTF(true, "Unknown Graphics Class/Method 0x%08X/0x%08X", + graphics_class, method); + break; + } + +} + +static void pgraph_switch_context(NV2AState *d, unsigned int channel_id) +{ + bool channel_valid = + d->pgraph.regs[NV_PGRAPH_CTX_CONTROL] & NV_PGRAPH_CTX_CONTROL_CHID; + unsigned pgraph_channel_id = GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID); + // Cxbx Note : This isn't present in xqemu / OpenXbox : d->pgraph.pgraph_lock.lock(); + bool valid = channel_valid && pgraph_channel_id == channel_id; + if (!valid) { + SET_MASK(d->pgraph.regs[NV_PGRAPH_TRAPPED_ADDR], + NV_PGRAPH_TRAPPED_ADDR_CHID, channel_id); + + NV2A_DPRINTF("pgraph switching to ch %d\n", channel_id); + + /* TODO: hardware context switching */ + assert(!(d->pgraph.regs[NV_PGRAPH_DEBUG_3] + & NV_PGRAPH_DEBUG_3_HW_CONTEXT_SWITCH)); + + qemu_mutex_unlock(&d->pgraph.pgraph_lock); + qemu_mutex_lock_iothread(); + d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; // TODO : Should this be done before unlocking pgraph_lock? + update_irq(d); + + qemu_mutex_lock(&d->pgraph.pgraph_lock); + qemu_mutex_unlock_iothread(); + + // wait for the interrupt to be serviced + while (d->pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + qemu_cond_wait(&d->pgraph.interrupt_cond, &d->pgraph.pgraph_lock); + } + } +} + +static void pgraph_wait_fifo_access(NV2AState *d) { + while (!(d->pgraph.regs[NV_PGRAPH_FIFO] & NV_PGRAPH_FIFO_ACCESS)) { + qemu_cond_wait(&d->pgraph.fifo_access_cond, &d->pgraph.pgraph_lock); + } +} + +static void pgraph_log_method(unsigned int subchannel, + unsigned int graphics_class, + unsigned int method, uint32_t parameter) { + static unsigned int last = 0; + static unsigned int count = 0; + + extern const char *NV2AMethodToString(DWORD dwMethod); // implemented in PushBuffer.cpp + + if (last == 0x1800 && method != last) { + const char* method_name = NV2AMethodToString(last); // = 'NV2A_VB_ELEMENT_U16' + NV2A_GL_DPRINTF(true, "d->pgraph method (%d) 0x%08X %s * %d", + subchannel, last, method_name, count); + } + if (method != 0x1800) { + // const char* method_name = NV2AMethodToString(method); + // unsigned int nmethod = 0; + // switch (graphics_class) { + // case NV_KELVIN_PRIMITIVE: + // nmethod = method | (0x5c << 16); + // break; + // case NV_CONTEXT_SURFACES_2D: + // nmethod = method | (0x6d << 16); + // break; + // case NV_CONTEXT_PATTERN: + // nmethod = method | (0x68 << 16); + // break; + // default: + // break; + // } + // if (method_name) { + // NV2A_DPRINTF("d->pgraph method (%d): %s (0x%x)\n", + // subchannel, method_name, parameter); + // } else { + NV2A_DPRINTF("pgraph method (%d): 0x%08X -> 0x%04x (0x%x)\n", + subchannel, graphics_class, method, parameter); + // } + + } + if (method == last) { count++; } + else { count = 0; } + last = method; +} + +static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, + unsigned int attr) +{ + unsigned int i; + VertexAttribute *vertex_attribute = &pg->vertex_attributes[attr]; + + if (vertex_attribute->inline_buffer || pg->inline_buffer_length == 0) { + return; + } + + /* Now upload the previous vertex_attribute value */ + vertex_attribute->inline_buffer = (float*)g_malloc(NV2A_MAX_BATCH_LENGTH + * sizeof(float) * 4); + for (i = 0; i < pg->inline_buffer_length; i++) { + memcpy(&vertex_attribute->inline_buffer[i * 4], + vertex_attribute->inline_value, + sizeof(float) * 4); + } +} + +static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg) +{ + unsigned int i; + + assert(pg->inline_buffer_length < NV2A_MAX_BATCH_LENGTH); + + for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) { + VertexAttribute *vertex_attribute = &pg->vertex_attributes[i]; + if (vertex_attribute->inline_buffer) { + memcpy(&vertex_attribute->inline_buffer[ + pg->inline_buffer_length * 4], + vertex_attribute->inline_value, + sizeof(float) * 4); + } + } + + pg->inline_buffer_length++; +} + +void pgraph_init(NV2AState *d) +{ + int i; + + PGRAPHState *pg = &d->pgraph; + + qemu_mutex_init(&pg->pgraph_lock); + qemu_cond_init(&pg->interrupt_cond); + qemu_cond_init(&pg->fifo_access_cond); + qemu_cond_init(&pg->flip_3d); + + if (!(pg->opengl_enabled)) + return; + + /* attach OpenGL render plugins */ + OpenGL_init_pgraph_plugins(); + + /* fire up opengl */ + + pg->gl_context = glo_context_create(); + assert(pg->gl_context); + +#ifdef DEBUG_NV2A_GL + glEnable(GL_DEBUG_OUTPUT); +#endif + + glextensions_init(); + + /* DXT textures */ + assert(glo_check_extension("GL_EXT_texture_compression_s3tc")); + /* Internal RGB565 texture format */ + assert(glo_check_extension("GL_ARB_ES2_compatibility")); + + GLint max_vertex_attributes; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attributes); + assert(max_vertex_attributes >= NV2A_VERTEXSHADER_ATTRIBUTES); + + glGenFramebuffers(1, &pg->gl_framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer); + + /* need a valid framebuffer to start with */ + glGenTextures(1, &pg->gl_color_buffer); + glBindTexture(GL_TEXTURE_2D, pg->gl_color_buffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 640, 480, + 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, pg->gl_color_buffer, 0); + + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + + //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + +#ifdef USE_TEXTURE_CACHE + pg->texture_cache = g_lru_cache_new_full( + 0, + NULL, + texture_key_destroy, + 0, + NULL, + texture_binding_destroy, + texture_key_hash, + texture_key_equal, + texture_key_retrieve, + NULL, + NULL + ); + + g_lru_cache_set_max_size(pg->texture_cache, 512); +#endif + +#ifdef USE_SHADER_CACHE + pg->shader_cache = g_hash_table_new(shader_hash, shader_equal); +#endif + + for (i=0; ivertex_attributes[i].gl_converted_buffer); + glGenBuffers(1, &pg->vertex_attributes[i].gl_inline_buffer); + } + glGenBuffers(1, &pg->gl_inline_array_buffer); + glGenBuffers(1, &pg->gl_element_buffer); + + glGenBuffers(1, &pg->gl_memory_buffer); + glBindBuffer(GL_ARRAY_BUFFER, pg->gl_memory_buffer); + glBufferData(GL_ARRAY_BUFFER, + d->vram_size, + NULL, + GL_DYNAMIC_DRAW); + + glGenVertexArrays(1, &pg->gl_vertex_array); + glBindVertexArray(pg->gl_vertex_array); + +// assert(glGetError() == GL_NO_ERROR); + + glo_set_current(NULL); +} + +void pgraph_destroy(PGRAPHState *pg) +{ + + qemu_mutex_destroy(&pg->pgraph_lock); + qemu_cond_destroy(&pg->interrupt_cond); + qemu_cond_destroy(&pg->fifo_access_cond); + qemu_cond_destroy(&pg->flip_3d); + + if (pg->opengl_enabled) { + glo_set_current(pg->gl_context); + + if (pg->gl_color_buffer) { + glDeleteTextures(1, &pg->gl_color_buffer); + } + if (pg->gl_zeta_buffer) { + glDeleteTextures(1, &pg->gl_zeta_buffer); + } + glDeleteFramebuffers(1, &pg->gl_framebuffer); + + // TODO: clear out shader cached + // TODO: clear out texture cache + + glo_set_current(NULL); + + glo_context_destroy(pg->gl_context); + } +} + +static void pgraph_update_shader_constants(PGRAPHState *pg, + ShaderBinding *binding, + bool binding_changed, + bool vertex_program, + bool fixed_function) +{ + assert(pg->opengl_enabled); + + unsigned int i, j; + + /* update combiner constants */ + for (i = 0; i<= 8; i++) { + uint32_t constant[2]; + if (i == 8) { + /* final combiner */ + constant[0] = pg->regs[NV_PGRAPH_SPECFOGFACTOR0]; + constant[1] = pg->regs[NV_PGRAPH_SPECFOGFACTOR1]; + } else { + constant[0] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4]; + constant[1] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4]; + } + + for (j = 0; j < 2; j++) { + GLint loc = binding->psh_constant_loc[i][j]; + if (loc != -1) { + float value[4]; + value[0] = (float) ((constant[j] >> 16) & 0xFF) / 255.0f; + value[1] = (float) ((constant[j] >> 8) & 0xFF) / 255.0f; + value[2] = (float) (constant[j] & 0xFF) / 255.0f; + value[3] = (float) ((constant[j] >> 24) & 0xFF) / 255.0f; + + glUniform4fv(loc, 1, value); + } + } + } + if (binding->alpha_ref_loc != -1) { + float alpha_ref = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAREF) / 255.0f; + glUniform1f(binding->alpha_ref_loc, alpha_ref); + } + + + /* For each texture stage */ + for (i = 0; i < NV2A_MAX_TEXTURES; i++) { + // char name[32]; + GLint loc; + + /* Bump luminance only during stages 1 - 3 */ + if (i > 0) { + loc = binding->bump_mat_loc[i]; + if (loc != -1) { + glUniformMatrix2fv(loc, 1, GL_FALSE, pg->bump_env_matrix[i - 1]); + } + loc = binding->bump_scale_loc[i]; + if (loc != -1) { + glUniform1f(loc, *(float*)&pg->regs[ + NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4]); + } + loc = binding->bump_offset_loc[i]; + if (loc != -1) { + glUniform1f(loc, *(float*)&pg->regs[ + NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4]); + } + } + + } + + if (binding->fog_color_loc != -1) { + uint32_t fog_color = pg->regs[NV_PGRAPH_FOGCOLOR]; + glUniform4f(binding->fog_color_loc, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0f, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0f, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0f, + GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0f); + } + if (binding->fog_param_loc[0] != -1) { + glUniform1f(binding->fog_param_loc[0], + *(float*)&pg->regs[NV_PGRAPH_FOGPARAM0]); + } + if (binding->fog_param_loc[1] != -1) { + glUniform1f(binding->fog_param_loc[1], + *(float*)&pg->regs[NV_PGRAPH_FOGPARAM1]); + } + + float zclip_max = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMAX]; + float zclip_min = *(float*)&pg->regs[NV_PGRAPH_ZCLIPMIN]; + + if (fixed_function) { + /* update lighting constants */ + struct { + uint32_t* v; + bool* dirty; + GLint* locs; + size_t len; + } lighting_arrays[] = { + // TODO : Change pointers into offset_of(), so this variable can become static + {&pg->ltctxa[0][0], &pg->ltctxa_dirty[0], binding->ltctxa_loc, NV2A_LTCTXA_COUNT}, + {&pg->ltctxb[0][0], &pg->ltctxb_dirty[0], binding->ltctxb_loc, NV2A_LTCTXB_COUNT}, + {&pg->ltc1[0][0], &pg->ltc1_dirty[0], binding->ltc1_loc, NV2A_LTC1_COUNT}, + }; + + for (i=0; ilight_infinite_half_vector_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_infinite_half_vector[i]); + } + loc = binding->light_infinite_direction_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_infinite_direction[i]); + } + + loc = binding->light_local_position_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_local_position[i]); + } + loc = binding->light_local_attenuation_loc[i]; + if (loc != -1) { + glUniform3fv(loc, 1, pg->light_local_attenuation[i]); + } + } + + /* estimate the viewport by assuming it matches the surface ... */ + //FIXME: Get surface dimensions? + float m11 = 0.5f * pg->surface_shape.clip_width; + float m22 = -0.5f * pg->surface_shape.clip_height; + float m33 = zclip_max - zclip_min; + //float m41 = m11; + //float m42 = -m22; + float m43 = zclip_min; + //float m44 = 1.0f; + + if (m33 == 0.0f) { + m33 = 1.0f; + } + float invViewport[16] = { + 1.0f/m11, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f/m22, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f/m33, 0.0f, + -1.0f, 1.0f, -m43/m33, 1.0f + }; + + if (binding->inv_viewport_loc != -1) { + glUniformMatrix4fv(binding->inv_viewport_loc, + 1, GL_FALSE, &invViewport[0]); + } + + } + + /* update vertex program constants */ + for (i=0; ivsh_constants_dirty[i] && !binding_changed) continue; + + GLint loc = binding->vsh_constant_loc[i]; + //assert(loc != -1); + if (loc != -1) { + glUniform4fv(loc, 1, (const GLfloat*)pg->vsh_constants[i]); + } + pg->vsh_constants_dirty[i] = false; + } + + if (binding->surface_size_loc != -1) { + glUniform2f(binding->surface_size_loc, (GLfloat)pg->surface_shape.clip_width, + (GLfloat)pg->surface_shape.clip_height); + } + + if (binding->clip_range_loc != -1) { + glUniform2f(binding->clip_range_loc, zclip_min, zclip_max); + } + +} + +static void pgraph_bind_shaders(PGRAPHState *pg) +{ + assert(pg->opengl_enabled); + + unsigned int i, j; + + uint32_t csv0_d = pg->regs[NV_PGRAPH_CSV0_D]; + bool vertex_program = GET_MASK(csv0_d, + NV_PGRAPH_CSV0_D_MODE) == 2; + + bool fixed_function = GET_MASK(csv0_d, + NV_PGRAPH_CSV0_D_MODE) == 0; + + uint32_t csv0_c = pg->regs[NV_PGRAPH_CSV0_C]; + int program_start = GET_MASK(csv0_c, + NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START); + + NV2A_GL_DGROUP_BEGIN("%s (VP: %s FFP: %s)", __func__, + vertex_program ? "yes" : "no", + fixed_function ? "yes" : "no"); + + ShaderBinding* old_binding = pg->shader_binding; + + ShaderState state; + /* register combiner stuff */ + state.psh.window_clip_exclusive = pg->regs[NV_PGRAPH_SETUPRASTER] + & NV_PGRAPH_SETUPRASTER_WINDOWCLIPTYPE, + state.psh.combiner_control = pg->regs[NV_PGRAPH_COMBINECTL]; + state.psh.shader_stage_program = pg->regs[NV_PGRAPH_SHADERPROG]; + state.psh.other_stage_input = pg->regs[NV_PGRAPH_SHADERCTL]; + state.psh.final_inputs_0 = pg->regs[NV_PGRAPH_COMBINESPECFOG0]; + state.psh.final_inputs_1 = pg->regs[NV_PGRAPH_COMBINESPECFOG1]; + uint32_t control0 = pg->regs[NV_PGRAPH_CONTROL_0]; + + state.psh.alpha_test = control0 + & NV_PGRAPH_CONTROL_0_ALPHATESTENABLE; + state.psh.alpha_func = (enum PshAlphaFunc)GET_MASK(control0, + NV_PGRAPH_CONTROL_0_ALPHAFUNC); + + /* fixed function stuff */ + state.skinning = (enum VshSkinning)GET_MASK(csv0_d, + NV_PGRAPH_CSV0_D_SKIN); + state.lighting = csv0_c + & NV_PGRAPH_CSV0_C_LIGHTING; + state.normalization = csv0_c + & NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE; + + state.fixed_function = fixed_function; + + /* vertex program stuff */ + state.vertex_program = vertex_program; + state.z_perspective = control0 + & NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE; + + /* geometry shader stuff */ + state.primitive_mode = (enum ShaderPrimitiveMode)pg->primitive_mode; + state.polygon_front_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACEMODE); + state.polygon_back_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_BACKFACEMODE); + + state.program_length = 0; + memset(state.program_data, 0, sizeof(state.program_data)); + + if (vertex_program) { + // copy in vertex program tokens + for (i = program_start; i < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH; i++) { + uint32_t *cur_token = (uint32_t*)&pg->program_data[i]; + memcpy(&state.program_data[state.program_length], + cur_token, + VSH_TOKEN_SIZE * sizeof(uint32_t)); + state.program_length++; + + if (vsh_get_field(cur_token, FLD_FINAL)) { + break; + } + } + } + + /* Texgen */ + for (i = 0; i < 4; i++) { + unsigned int reg = (i < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B; + for (j = 0; j < 4; j++) { + unsigned int masks[] = { + // NOTE: For some reason, Visual Studio thinks NV_PGRAPH_xxxx is signed integer. (possible bug?) + (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_S : (unsigned int)NV_PGRAPH_CSV1_A_T0_S, + (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_T : (unsigned int)NV_PGRAPH_CSV1_A_T0_T, + (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_R : (unsigned int)NV_PGRAPH_CSV1_A_T0_R, + (i % 2U) ? (unsigned int)NV_PGRAPH_CSV1_A_T1_Q : (unsigned int)NV_PGRAPH_CSV1_A_T0_Q + }; + state.texgen[i][j] = (enum VshTexgen)GET_MASK(pg->regs[reg], masks[j]); + } + } + + /* Fog */ + state.fog_enable = pg->regs[NV_PGRAPH_CONTROL_3] + & NV_PGRAPH_CONTROL_3_FOGENABLE; + if (state.fog_enable) { + /*FIXME: Use CSV0_D? */ + state.fog_mode = (enum VshFogMode)GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], + NV_PGRAPH_CONTROL_3_FOG_MODE); + state.foggen = (enum VshFoggen)GET_MASK(csv0_d, + NV_PGRAPH_CSV0_D_FOGGENMODE); + } else { + /* FIXME: Do we still pass the fogmode? */ + state.fog_mode = (enum VshFogMode)0; + state.foggen = (enum VshFoggen)0; + } + + /* Texture matrices */ + for (i = 0; i < 4; i++) { + state.texture_matrix_enable[i] = pg->texture_matrix_enable[i]; + } + + /* Lighting */ + if (state.lighting) { + for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + state.light[i] = (enum VshLight)GET_MASK(csv0_d, + NV_PGRAPH_CSV0_D_LIGHT0 << (i * 2)); + } + } + + /* Window clip + * + * Optimization note: very quickly check to ignore any repeated or zero-size + * clipping regions. Note that if region number 7 is valid, but the rest are + * not, we will still add all of them. Clip regions seem to be typically + * front-loaded (meaning the first one or two regions are populated, and the + * following are zeroed-out), so let's avoid adding any more complicated + * masking or copying logic here for now unless we discover a valid case. + */ + assert(!state.psh.window_clip_exclusive); /* FIXME: Untested */ + state.psh.window_clip_count = 0; + uint32_t last_x = 0, last_y = 0; + + for (i = 0; i < 8; i++) { + const uint32_t x = pg->regs[NV_PGRAPH_WINDOWCLIPX0 + i * 4]; + const uint32_t y = pg->regs[NV_PGRAPH_WINDOWCLIPY0 + i * 4]; + const uint32_t x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN); + const uint32_t x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX); + const uint32_t y_min = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN); + const uint32_t y_max = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX); + + /* Check for zero width or height clipping region */ + if ((x_min == x_max) || (y_min == y_max)) { + continue; + } + + /* Check for in-order duplicate regions */ + if ((x == last_x) && (y == last_y)) { + continue; + } + + NV2A_DPRINTF("Clipping Region %d: min=(%d, %d) max=(%d, %d)\n", + i, x_min, y_min, x_max, y_max); + + state.psh.window_clip_count = i + 1; + last_x = x; + last_y = y; + } + + for (i = 0; i < 8; i++) { + state.psh.rgb_inputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORI0 + i * 4]; + state.psh.rgb_outputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORO0 + i * 4]; + state.psh.alpha_inputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAI0 + i * 4]; + state.psh.alpha_outputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAO0 + i * 4]; + //constant_0[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR0 + i * 4]; + //constant_1[i] = pg->regs[NV_PGRAPH_COMBINEFACTOR1 + i * 4]; + } + + for (i = 0; i < 4; i++) { + state.psh.rect_tex[i] = false; + bool enabled = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4] + & NV_PGRAPH_TEXCTL0_0_ENABLE; + unsigned int color_format = + GET_MASK(pg->regs[NV_PGRAPH_TEXFMT0 + i*4], + NV_PGRAPH_TEXFMT0_COLOR); + + if (enabled && kelvin_color_format_map[color_format].encoding == linear) { + state.psh.rect_tex[i] = true; + } + + for (j = 0; j < 4; j++) { + state.psh.compare_mode[i][j] = + (pg->regs[NV_PGRAPH_SHADERCLIPMODE] >> (4 * i + j)) & 1; + } + state.psh.alphakill[i] = pg->regs[NV_PGRAPH_TEXCTL0_0 + i*4] + & NV_PGRAPH_TEXCTL0_0_ALPHAKILLEN; + } + +#ifdef USE_SHADER_CACHE + ShaderBinding* cached_shader = (ShaderBinding*)g_hash_table_lookup(pg->shader_cache, &state); + + if (cached_shader) { + pg->shader_binding = cached_shader; + } else { +#endif + pg->shader_binding = generate_shaders(state); + +#ifdef USE_SHADER_CACHE + /* cache it */ + ShaderState *cache_state = (ShaderState *)g_malloc(sizeof(*cache_state)); + memcpy(cache_state, &state, sizeof(*cache_state)); + g_hash_table_insert(pg->shader_cache, cache_state, + (gpointer)pg->shader_binding); + } +#endif + + bool binding_changed = (pg->shader_binding != old_binding); + + glUseProgram(pg->shader_binding->gl_program); + + /* Clipping regions */ + for (i = 0; i < state.psh.window_clip_count; i++) { + if (pg->shader_binding->clip_region_loc[i] == -1) { + continue; + } + + uint32_t x = pg->regs[NV_PGRAPH_WINDOWCLIPX0 + i * 4]; + GLuint x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN); + GLuint x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX); + + /* Adjust y-coordinates for the OpenGL viewport: translate coordinates + * to have the origin at the bottom-left of the surface (as opposed to + * top-left), and flip y-min and y-max accordingly. + */ + uint32_t y = pg->regs[NV_PGRAPH_WINDOWCLIPY0 + i * 4]; + GLuint y_min = (pg->surface_shape.clip_height - 1) - + GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX); + GLuint y_max = (pg->surface_shape.clip_height - 1) - + GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN); + + pgraph_apply_anti_aliasing_factor(pg, &x_min, &y_min); + pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max); + + glUniform4i(pg->shader_binding->clip_region_loc[i], + x_min, y_min, x_max, y_max); + } + + pgraph_update_shader_constants(pg, pg->shader_binding, binding_changed, + vertex_program, fixed_function); + + NV2A_GL_DGROUP_END(); +} + +static bool pgraph_get_framebuffer_dirty(PGRAPHState *pg) +{ + bool shape_changed = memcmp(&pg->surface_shape, &pg->last_surface_shape, + sizeof(SurfaceShape)) != 0; + if (!shape_changed || (!pg->surface_shape.color_format + && !pg->surface_shape.zeta_format)) { + return false; + } + return true; +} + +static bool pgraph_get_color_write_enabled(PGRAPHState *pg) +{ + return pg->regs[NV_PGRAPH_CONTROL_0] & ( + NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE + | NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE + | NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE + | NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE); +} + +static bool pgraph_get_zeta_write_enabled(PGRAPHState *pg) +{ + return pg->regs[NV_PGRAPH_CONTROL_0] & ( + NV_PGRAPH_CONTROL_0_ZWRITEENABLE + | NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE); +} + +static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta) +{ + NV2A_DPRINTF("pgraph_set_surface_dirty(%d, %d) -- %d %d\n", + color, zeta, + pgraph_get_color_write_enabled(pg), pgraph_get_zeta_write_enabled(pg)); + /* FIXME: Does this apply to CLEARs too? */ + color = color && pgraph_get_color_write_enabled(pg); + zeta = zeta && pgraph_get_zeta_write_enabled(pg); + pg->surface_color.draw_dirty |= color; + pg->surface_zeta.draw_dirty |= zeta; +} + +static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color) { + PGRAPHState *pg = &d->pgraph; + + unsigned int width, height; + pgraph_get_surface_dimensions(pg, &width, &height); + pgraph_apply_anti_aliasing_factor(pg, &width, &height); + + Surface *surface; + hwaddr dma_address; + GLuint *gl_buffer; + unsigned int bytes_per_pixel; + GLint gl_internal_format; + GLenum gl_format, gl_type, gl_attachment; + + if (color) { + surface = &pg->surface_color; + dma_address = pg->dma_color; + gl_buffer = &pg->gl_color_buffer; + + assert(pg->surface_shape.color_format != 0); + assert(pg->surface_shape.color_format + < ARRAY_SIZE(kelvin_surface_color_format_map)); + SurfaceColorFormatInfo f = + kelvin_surface_color_format_map[pg->surface_shape.color_format]; + if (f.bytes_per_pixel == 0) { + fprintf(stderr, "nv2a: unimplemented color surface format 0x%x\n", + pg->surface_shape.color_format); + abort(); + } + + bytes_per_pixel = f.bytes_per_pixel; + gl_internal_format = f.gl_internal_format; + gl_format = f.gl_format; + gl_type = f.gl_type; + gl_attachment = GL_COLOR_ATTACHMENT0; + + } else { + surface = &pg->surface_zeta; + dma_address = pg->dma_zeta; + gl_buffer = &pg->gl_zeta_buffer; + + assert(pg->surface_shape.zeta_format != 0); + switch (pg->surface_shape.zeta_format) { + case NV097_SET_SURFACE_FORMAT_ZETA_Z16: + bytes_per_pixel = 2; + gl_format = GL_DEPTH_COMPONENT; + gl_attachment = GL_DEPTH_ATTACHMENT; + if (pg->surface_shape.z_format) { + gl_type = GL_HALF_FLOAT; + gl_internal_format = GL_DEPTH_COMPONENT32F; + } else { + gl_type = GL_UNSIGNED_SHORT; + gl_internal_format = GL_DEPTH_COMPONENT16; + } + break; + case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8: + bytes_per_pixel = 4; + gl_format = GL_DEPTH_STENCIL; + gl_attachment = GL_DEPTH_STENCIL_ATTACHMENT; + if (pg->surface_shape.z_format) { + assert(false); + gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + gl_internal_format = GL_DEPTH32F_STENCIL8; + } else { + gl_type = GL_UNSIGNED_INT_24_8; + gl_internal_format = GL_DEPTH24_STENCIL8; + } + break; + default: + assert(false); + break; + } + } + + + DMAObject dma = nv_dma_load(d, dma_address); + /* There's a bunch of bugs that could cause us to hit this function + * at the wrong time and get a invalid dma object. + * Check that it's sane. */ + assert(dma.dma_class == NV_DMA_IN_MEMORY_CLASS); + + assert(dma.address + surface->offset != 0); + assert(surface->offset <= dma.limit); + assert(surface->offset + surface->pitch * height <= dma.limit + 1); + + hwaddr data_len; + uint8_t *data = (uint8_t*)nv_dma_map(d, dma_address, &data_len); + + /* TODO */ + // assert(pg->surface_clip_x == 0 && pg->surface_clip_y == 0); + + bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE); + + uint8_t *buf = data + surface->offset; + if (swizzle) { + buf = (uint8_t*)g_malloc(height * surface->pitch); + } + + bool dirty = surface->buffer_dirty; + if (color) { +#if 1 + // HACK: Always mark as dirty + dirty |= 1; +#else + dirty |= memory_region_test_and_clear_dirty(d->vram, + dma.address + surface->offset, + surface->pitch * height, + DIRTY_MEMORY_NV2A); +#endif + } + if (upload && dirty) { + /* surface modified (or moved) by the cpu. + * copy it into the opengl renderbuffer */ + // TODO: Why does this assert? + //assert(!surface->draw_dirty); + assert(surface->pitch % bytes_per_pixel == 0); + + if (swizzle) { + unswizzle_rect(data + surface->offset, + width, height, + buf, + surface->pitch, + bytes_per_pixel); + } + + if (pg->opengl_enabled) { + if (!color) { + /* need to clear the depth_stencil and depth attachment for zeta */ + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, + gl_attachment, + GL_TEXTURE_2D, + 0, 0); + + if (*gl_buffer) { + glDeleteTextures(1, gl_buffer); + *gl_buffer = 0; + } + + glGenTextures(1, gl_buffer); + glBindTexture(GL_TEXTURE_2D, *gl_buffer); + + /* This is VRAM so we can't do this inplace! */ + uint8_t *flipped_buf = (uint8_t*)g_malloc(width * height * bytes_per_pixel); + unsigned int irow; + for (irow = 0; irow < height; irow++) { + memcpy(&flipped_buf[width * (height - irow - 1) + * bytes_per_pixel], + &buf[surface->pitch * irow], + width * bytes_per_pixel); + } + + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, + width, height, 0, + gl_format, gl_type, + flipped_buf); + + g_free(flipped_buf); + + glFramebufferTexture2D(GL_FRAMEBUFFER, + gl_attachment, + GL_TEXTURE_2D, + *gl_buffer, 0); + + assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) + == GL_FRAMEBUFFER_COMPLETE); + } + + if (color) { + pgraph_update_memory_buffer(d, dma.address + surface->offset, + surface->pitch * height, true); + } + surface->buffer_dirty = false; + +#ifdef DEBUG_NV2A + uint8_t *out = data + surface->offset + 64; + NV2A_DPRINTF("upload_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "%d %d, %d %d, %d) - %x %x %x %x\n", + color ? "color" : "zeta", + dma.address, dma.address + dma.limit, + dma.address + surface->offset, + dma.address + surface->pitch * height, + pg->surface_shape.clip_x, pg->surface_shape.clip_y, + pg->surface_shape.clip_width, + pg->surface_shape.clip_height, + surface->pitch, + out[0], out[1], out[2], out[3]); +#endif + } + + if (!upload && surface->draw_dirty) { + if (pg->opengl_enabled) { + /* read the opengl framebuffer into the surface */ + + glo_readpixels(gl_format, gl_type, + bytes_per_pixel, surface->pitch, + width, height, + buf); + +// assert(glGetError() == GL_NO_ERROR); + } + + if (swizzle) { + swizzle_rect(buf, + width, height, + data + surface->offset, + surface->pitch, + bytes_per_pixel); + } + + // memory_region_set_client_dirty(d->vram, + // dma.address + surface->offset, + // surface->pitch * height, + // DIRTY_MEMORY_VGA); + + if (color) { + pgraph_update_memory_buffer(d, dma.address + surface->offset, + surface->pitch * height, true); + } + + surface->draw_dirty = false; + surface->write_enabled_cache = false; + +#ifdef DEBUG_NV2A + uint8_t *out = data + surface->offset + 64; + NV2A_DPRINTF("read_surface %s 0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "(0x%" HWADDR_PRIx " - 0x%" HWADDR_PRIx ", " + "%d %d, %d %d, %d) - %x %x %x %x\n", + color ? "color" : "zeta", + dma.address, dma.address + dma.limit, + dma.address + surface->offset, + dma.address + surface->pitch * pg->surface_shape.clip_height, + pg->surface_shape.clip_x, pg->surface_shape.clip_y, + pg->surface_shape.clip_width, pg->surface_shape.clip_height, + surface->pitch, + out[0], out[1], out[2], out[3]); +#endif + } + + if (swizzle) { + g_free(buf); + } +} + +static void pgraph_update_surface(NV2AState *d, bool upload, + bool color_write, bool zeta_write) +{ + PGRAPHState *pg = &d->pgraph; + + if (!pg->opengl_enabled) { + return; + } + + pg->surface_shape.z_format = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_Z_FORMAT); + + /* FIXME: Does this apply to CLEARs too? */ + color_write = color_write && pgraph_get_color_write_enabled(pg); + zeta_write = zeta_write && pgraph_get_zeta_write_enabled(pg); + + if (upload && pgraph_get_framebuffer_dirty(pg)) { + assert(!pg->surface_color.draw_dirty); + assert(!pg->surface_zeta.draw_dirty); + + pg->surface_color.buffer_dirty = true; + pg->surface_zeta.buffer_dirty = true; + + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + 0, 0); + + if (pg->gl_color_buffer) { + glDeleteTextures(1, &pg->gl_color_buffer); + pg->gl_color_buffer = 0; + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, + 0, 0); + + if (pg->gl_zeta_buffer) { + glDeleteTextures(1, &pg->gl_zeta_buffer); + pg->gl_zeta_buffer = 0; + } + } + + memcpy(&pg->last_surface_shape, &pg->surface_shape, + sizeof(SurfaceShape)); + + if ((color_write || (!upload && pg->surface_color.write_enabled_cache)) + && (upload || pg->surface_color.draw_dirty)) { + pgraph_update_surface_part(d, upload, true); + } + + + if ((zeta_write || (!upload && pg->surface_zeta.write_enabled_cache)) + && (upload || pg->surface_zeta.draw_dirty)) { + pgraph_update_surface_part(d, upload, false); + } + +} + +static void pgraph_bind_textures(NV2AState *d) +{ + int i; + PGRAPHState *pg = &d->pgraph; + + if (!(pg->opengl_enabled)) + return; + + NV2A_GL_DGROUP_BEGIN("%s", __func__); + + for (i=0; iregs[NV_PGRAPH_TEXCTL0_0 + i*4]; + uint32_t ctl_1 = pg->regs[NV_PGRAPH_TEXCTL1_0 + i*4]; + uint32_t fmt = pg->regs[NV_PGRAPH_TEXFMT0 + i*4]; + uint32_t filter = pg->regs[NV_PGRAPH_TEXFILTER0 + i*4]; + uint32_t address = pg->regs[NV_PGRAPH_TEXADDRESS0 + i*4]; + uint32_t palette = pg->regs[NV_PGRAPH_TEXPALETTE0 + i*4]; + + bool enabled = ctl_0 & NV_PGRAPH_TEXCTL0_0_ENABLE; + unsigned int min_mipmap_level = + GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MIN_LOD_CLAMP); + unsigned int max_mipmap_level = + GET_MASK(ctl_0, NV_PGRAPH_TEXCTL0_0_MAX_LOD_CLAMP); + + unsigned int pitch = + GET_MASK(ctl_1, NV_PGRAPH_TEXCTL1_0_IMAGE_PITCH); + + bool dma_select = + fmt & NV_PGRAPH_TEXFMT0_CONTEXT_DMA; + bool cubemap = + fmt & NV_PGRAPH_TEXFMT0_CUBEMAPENABLE; + unsigned int dimensionality = + GET_MASK(fmt, NV_PGRAPH_TEXFMT0_DIMENSIONALITY); + unsigned int color_format = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_COLOR); + unsigned int levels = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_MIPMAP_LEVELS); + unsigned int log_width = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_U); + unsigned int log_height = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_V); + unsigned int log_depth = GET_MASK(fmt, NV_PGRAPH_TEXFMT0_BASE_SIZE_P); + + unsigned int rect_width = + GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4], + NV_PGRAPH_TEXIMAGERECT0_WIDTH); + unsigned int rect_height = + GET_MASK(pg->regs[NV_PGRAPH_TEXIMAGERECT0 + i*4], + NV_PGRAPH_TEXIMAGERECT0_HEIGHT); +#ifdef DEBUG_NV2A + unsigned int lod_bias = + GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS); +#endif + unsigned int min_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIN); + unsigned int mag_filter = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MAG); + + unsigned int addru = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRU); + unsigned int addrv = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRV); + unsigned int addrp = GET_MASK(address, NV_PGRAPH_TEXADDRESS0_ADDRP); + + bool border_source_color = (fmt & NV_PGRAPH_TEXFMT0_BORDER_SOURCE); // != NV_PGRAPH_TEXFMT0_BORDER_SOURCE_TEXTURE; + uint32_t border_color = pg->regs[NV_PGRAPH_BORDERCOLOR0 + i*4]; + + unsigned int offset = pg->regs[NV_PGRAPH_TEXOFFSET0 + i*4]; + + bool palette_dma_select = + palette & NV_PGRAPH_TEXPALETTE0_CONTEXT_DMA; + unsigned int palette_length_index = + GET_MASK(palette, NV_PGRAPH_TEXPALETTE0_LENGTH); + unsigned int palette_offset = + palette & NV_PGRAPH_TEXPALETTE0_OFFSET; + + unsigned int palette_length = 0; + switch (palette_length_index) { + case NV_PGRAPH_TEXPALETTE0_LENGTH_256: palette_length = 256; break; + case NV_PGRAPH_TEXPALETTE0_LENGTH_128: palette_length = 128; break; + case NV_PGRAPH_TEXPALETTE0_LENGTH_64: palette_length = 64; break; + case NV_PGRAPH_TEXPALETTE0_LENGTH_32: palette_length = 32; break; + default: assert(false); break; + } + + /* Check for unsupported features */ + assert(!(filter & NV_PGRAPH_TEXFILTER0_ASIGNED)); + assert(!(filter & NV_PGRAPH_TEXFILTER0_RSIGNED)); + assert(!(filter & NV_PGRAPH_TEXFILTER0_GSIGNED)); + assert(!(filter & NV_PGRAPH_TEXFILTER0_BSIGNED)); + + glActiveTexture(GL_TEXTURE0 + i); + if (!enabled) { + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindTexture(GL_TEXTURE_RECTANGLE, 0); + glBindTexture(GL_TEXTURE_1D, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_3D, 0); + continue; + } + + if (!pg->texture_dirty[i] && pg->texture_binding[i]) { + glBindTexture(pg->texture_binding[i]->gl_target, + pg->texture_binding[i]->gl_texture); + continue; + } + + NV2A_DPRINTF(" texture %d is format 0x%x, off 0x%x (r %d, %d or %d, %d, %d; %d%s)," + " filter %x %x, levels %d-%d %d bias %d\n", + i, color_format, offset, + rect_width, rect_height, + 1 << log_width, 1 << log_height, 1 << log_depth, + pitch, + cubemap ? "; cubemap" : "", + min_filter, mag_filter, + min_mipmap_level, max_mipmap_level, levels, + lod_bias); + + assert(color_format < ARRAY_SIZE(kelvin_color_format_map)); + ColorFormatInfo f = kelvin_color_format_map[color_format]; + if (f.bytes_per_pixel == 0) { + fprintf(stderr, "nv2a: unimplemented texture color format 0x%x\n", + color_format); + abort(); + } + + unsigned int width, height, depth; + if (f.encoding == linear) { + assert(dimensionality == 2); + width = rect_width; + height = rect_height; + depth = 1; + } else { + width = 1 << log_width; + height = 1 << log_height; + depth = 1 << log_depth; + + /* FIXME: What about 3D mipmaps? */ + levels = MIN(levels, max_mipmap_level + 1); + if (f.encoding == swizzled) { + /* Discard mipmap levels that would be smaller than 1x1. + * FIXME: Is this actually needed? + * + * >> Level 0: 32 x 4 + * Level 1: 16 x 2 + * Level 2: 8 x 1 + * Level 3: 4 x 1 + * Level 4: 2 x 1 + * Level 5: 1 x 1 + */ + levels = MIN(levels, MAX(log_width, log_height) + 1); + } else { + /* OpenGL requires DXT textures to always have a width and + * height a multiple of 4. The Xbox and DirectX handles DXT + * textures smaller than 4 by padding the reset of the block. + * + * See: + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb204843(v=vs.85).aspx + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531%28v=vs.85%29.aspx#Virtual_Size + * + * Work around this for now by discarding mipmap levels that + * would result in too-small textures. A correct solution + * will be to decompress these levels manually, or add texture + * sampling logic. + * + * >> Level 0: 64 x 8 + * Level 1: 32 x 4 + * Level 2: 16 x 2 << Ignored + * >> Level 0: 16 x 16 + * Level 1: 8 x 8 + * Level 2: 4 x 4 << OK! + */ + if (log_width < 2 || log_height < 2) { + /* Base level is smaller than 4x4... */ + levels = 1; + } else { + levels = MIN(levels, MIN(log_width, log_height) - 1); + } + } + assert(levels > 0); + } + + hwaddr dma_len; + uint8_t *texture_data; + if (dma_select) { + texture_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &dma_len); + } else { + texture_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &dma_len); + } + assert(offset < dma_len); + texture_data += offset; + + hwaddr palette_dma_len; + uint8_t *palette_data; + if (palette_dma_select) { + palette_data = (uint8_t*)nv_dma_map(d, pg->dma_b, &palette_dma_len); + } else { + palette_data = (uint8_t*)nv_dma_map(d, pg->dma_a, &palette_dma_len); + } + assert(palette_offset < palette_dma_len); + palette_data += palette_offset; + + NV2A_DPRINTF(" - 0x%tx\n", texture_data - d->vram_ptr); + + size_t length = 0; + if (f.encoding == linear) { + assert(cubemap == false); + assert(dimensionality == 2); + length = height * pitch; + } else { + if (dimensionality >= 2) { + unsigned int w = width, h = height; + unsigned int level; + if (f.encoding == swizzled) { + for (level = 0; level < levels; level++) { + w = MAX(w, 1); h = MAX(h, 1); + length += w * h * f.bytes_per_pixel; + w /= 2; + h /= 2; + } + } else { + /* Compressed textures are a bit different */ + unsigned int block_size; + if (f.gl_internal_format == + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { + block_size = 8; + } else { + block_size = 16; + } + + for (level = 0; level < levels; level++) { + w = MAX(w, 4); h = MAX(h, 4); + length += w/4 * h/4 * block_size; + w /= 2; h /= 2; + } + } + if (cubemap) { + assert(dimensionality == 2); + length *= 6; + } + if (dimensionality >= 3) { + length *= depth; + } + } + } + + TextureShape state; + state.cubemap = cubemap; + state.dimensionality = dimensionality; + state.color_format = color_format; + state.levels = levels; + state.width = width; + state.height = height; + state.depth = depth; + state.min_mipmap_level = min_mipmap_level; + state.max_mipmap_level = max_mipmap_level; + state.pitch = pitch; + +#ifdef USE_TEXTURE_CACHE + TextureKey key; + key.state = state; + key.data_hash = fast_hash(texture_data, length, 5003) + ^ fnv_hash(palette_data, palette_length); + key.texture_data = texture_data; + key.palette_data = palette_data; + + gpointer cache_key = g_malloc(sizeof(TextureKey)); + memcpy(cache_key, &key, sizeof(TextureKey)); + + GError *err; + TextureBinding *binding = (TextureBinding *)g_lru_cache_get(pg->texture_cache, cache_key, &err); + assert(binding); + binding->refcnt++; +#else + TextureBinding *binding = generate_texture(state, + texture_data, palette_data); +#endif + + glBindTexture(binding->gl_target, binding->gl_texture); + + + if (f.encoding == linear) { + /* sometimes games try to set mipmap min filters on linear textures. + * this could indicate a bug... */ + switch (min_filter) { + case NV_PGRAPH_TEXFILTER0_MIN_BOX_NEARESTLOD: + case NV_PGRAPH_TEXFILTER0_MIN_BOX_TENT_LOD: + min_filter = NV_PGRAPH_TEXFILTER0_MIN_BOX_LOD0; + break; + case NV_PGRAPH_TEXFILTER0_MIN_TENT_NEARESTLOD: + case NV_PGRAPH_TEXFILTER0_MIN_TENT_TENT_LOD: + min_filter = NV_PGRAPH_TEXFILTER0_MIN_TENT_LOD0; + break; + } + } + + glTexParameteri(binding->gl_target, GL_TEXTURE_MIN_FILTER, + pgraph_texture_min_filter_map[min_filter]); + glTexParameteri(binding->gl_target, GL_TEXTURE_MAG_FILTER, + pgraph_texture_mag_filter_map[mag_filter]); + + /* Texture wrapping */ + assert(addru < ARRAY_SIZE(pgraph_texture_addr_map)); + glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_S, + pgraph_texture_addr_map[addru]); + if (dimensionality > 1) { + assert(addrv < ARRAY_SIZE(pgraph_texture_addr_map)); + glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_T, + pgraph_texture_addr_map[addrv]); + } + if (dimensionality > 2) { + assert(addrp < ARRAY_SIZE(pgraph_texture_addr_map)); + glTexParameteri(binding->gl_target, GL_TEXTURE_WRAP_R, + pgraph_texture_addr_map[addrp]); + } + + /* FIXME: Only upload if necessary? [s, t or r = GL_CLAMP_TO_BORDER] */ + if (border_source_color) { + GLfloat gl_border_color[] = { + /* FIXME: Color channels might be wrong order */ + ((border_color >> 16) & 0xFF) / 255.0f, /* red */ + ((border_color >> 8) & 0xFF) / 255.0f, /* green */ + (border_color & 0xFF) / 255.0f, /* blue */ + ((border_color >> 24) & 0xFF) / 255.0f /* alpha */ + }; + glTexParameterfv(binding->gl_target, GL_TEXTURE_BORDER_COLOR, + gl_border_color); + } + + if (pg->texture_binding[i]) { + texture_binding_destroy(pg->texture_binding[i]); + } + pg->texture_binding[i] = binding; + pg->texture_dirty[i] = false; + } + NV2A_GL_DGROUP_END(); +} + +static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, + unsigned int *width, + unsigned int *height) +{ + switch (pg->surface_shape.anti_aliasing) { + case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_1: + break; + case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_CENTER_CORNER_2: + if (width) { *width *= 2; } + break; + case NV097_SET_SURFACE_FORMAT_ANTI_ALIASING_SQUARE_OFFSET_4: + if (width) { *width *= 2; } + if (height) { *height *= 2; } + break; + default: + assert(false); + break; + } +} + +static void pgraph_get_surface_dimensions(PGRAPHState *pg, + unsigned int *width, + unsigned int *height) +{ + bool swizzle = (pg->surface_type == NV097_SET_SURFACE_FORMAT_TYPE_SWIZZLE); + if (swizzle) { + *width = 1 << pg->surface_shape.log_width; + *height = 1 << pg->surface_shape.log_height; + } else { + *width = pg->surface_shape.clip_width; + *height = pg->surface_shape.clip_height; + } +} + +static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, + bool f) +{ + glBindBuffer(GL_ARRAY_BUFFER, d->pgraph.gl_memory_buffer); + + hwaddr end = TARGET_PAGE_ALIGN(addr + size); + addr &= TARGET_PAGE_MASK; + + assert(end < d->vram_size); + + // if (f || memory_region_test_and_clear_dirty(d->vram, + // addr, + // end - addr, + // DIRTY_MEMORY_NV2A)) { + glBufferSubData(GL_ARRAY_BUFFER, addr, end - addr, d->vram_ptr + addr); + // } + +// auto error = glGetError(); +// assert(error == GL_NO_ERROR); +} + +static void pgraph_bind_vertex_attributes(NV2AState *d, + unsigned int num_elements, + bool inline_data, + unsigned int inline_stride) +{ + unsigned int i, j; + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + + if (inline_data) { + NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d inline stride: %d)", + __func__, num_elements, inline_stride); + } else { + NV2A_GL_DGROUP_BEGIN("%s (num_elements: %d)", __func__, num_elements); + } + + for (i=0; ivertex_attributes[i]; + if (vertex_attribute->count) { + uint8_t *data; + unsigned int in_stride; + if (inline_data && vertex_attribute->needs_conversion) { + data = (uint8_t*)pg->inline_array + + vertex_attribute->inline_array_offset; + in_stride = inline_stride; + } else { + hwaddr dma_len; + if (vertex_attribute->dma_select) { + data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_b, &dma_len); + } else { + data = (uint8_t*)nv_dma_map(d, pg->dma_vertex_a, &dma_len); + } + + assert(vertex_attribute->offset < dma_len); + data += vertex_attribute->offset; + + in_stride = vertex_attribute->stride; + } + + if (vertex_attribute->needs_conversion) { + NV2A_DPRINTF("converted %d\n", i); + + unsigned int out_stride = vertex_attribute->converted_size + * vertex_attribute->converted_count; + + if (num_elements > vertex_attribute->converted_elements) { + vertex_attribute->converted_buffer = (uint8_t*)g_realloc( + vertex_attribute->converted_buffer, + num_elements * out_stride); + } + + for (j=vertex_attribute->converted_elements; jconverted_buffer + j * out_stride; + + switch (vertex_attribute->format) { + case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP: { + uint32_t p = ldl_le_p((uint32_t*)in); + float *xyz = (float*)out; + xyz[0] = ((int32_t)(((p >> 0) & 0x7FF) << 21) >> 21) + / 1023.0f; + xyz[1] = ((int32_t)(((p >> 11) & 0x7FF) << 21) >> 21) + / 1023.0f; + xyz[2] = ((int32_t)(((p >> 22) & 0x3FF) << 22) >> 22) + / 511.0f; + break; + } + default: + assert(false); + break; + } + } + + + glBindBuffer(GL_ARRAY_BUFFER, vertex_attribute->gl_converted_buffer); + if (num_elements != vertex_attribute->converted_elements) { + glBufferData(GL_ARRAY_BUFFER, + num_elements * out_stride, + vertex_attribute->converted_buffer, + GL_DYNAMIC_DRAW); + vertex_attribute->converted_elements = num_elements; + } + + + glVertexAttribPointer(i, + vertex_attribute->converted_count, + vertex_attribute->gl_type, + vertex_attribute->gl_normalize, + out_stride, + 0); + } else if (inline_data) { + glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); + glVertexAttribPointer(i, + vertex_attribute->gl_count, + vertex_attribute->gl_type, + vertex_attribute->gl_normalize, + inline_stride, + (void*)(uintptr_t)vertex_attribute->inline_array_offset); + } else { + hwaddr addr = data - d->vram_ptr; + pgraph_update_memory_buffer(d, addr, + num_elements * vertex_attribute->stride, + false); + glVertexAttribPointer(i, + vertex_attribute->gl_count, + vertex_attribute->gl_type, + vertex_attribute->gl_normalize, + vertex_attribute->stride, + (void*)(uint64_t)(addr)); + } + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + + glVertexAttrib4fv(i, vertex_attribute->inline_value); + } + } + NV2A_GL_DGROUP_END(); +} + +static unsigned int pgraph_bind_inline_array(NV2AState *d) +{ + int i; + + PGRAPHState *pg = &d->pgraph; + + assert(pg->opengl_enabled); + + unsigned int offset = 0; + for (i=0; ivertex_attributes[i]; + if (vertex_attribute->count) { + vertex_attribute->inline_array_offset = offset; + + NV2A_DPRINTF("bind inline vertex_attribute %d size=%d, count=%d\n", + i, vertex_attribute->size, vertex_attribute->count); + offset += vertex_attribute->size * vertex_attribute->count; + assert(offset % 4 == 0); + } + } + + unsigned int vertex_size = offset; + + + unsigned int index_count = pg->inline_array_length*4 / vertex_size; + + NV2A_DPRINTF("draw inline array %d, %d\n", vertex_size, index_count); + + glBindBuffer(GL_ARRAY_BUFFER, pg->gl_inline_array_buffer); + glBufferData(GL_ARRAY_BUFFER, pg->inline_array_length*4, pg->inline_array, + GL_DYNAMIC_DRAW); + + pgraph_bind_vertex_attributes(d, index_count, true, vertex_size); + + return index_count; +} + +/* 16 bit to [0.0, F16_MAX = 511.9375] */ +static float convert_f16_to_float(uint16_t f16) { + if (f16 == 0x0000) { return 0.0f; } + uint32_t i = (f16 << 11) + 0x3C000000; + return *(float*)&i; +} + +/* 24 bit to [0.0, F24_MAX] */ +static float convert_f24_to_float(uint32_t f24) { + assert(!(f24 >> 24)); + f24 &= 0xFFFFFF; + if (f24 == 0x000000) { return 0.0f; } + uint32_t i = f24 << 7; + return *(float*)&i; +} + +extern void __R6G5B5ToARGBRow_C(const uint8_t* src_r6g5b5, uint8_t* dst_argb, int width); +extern void ____YUY2ToARGBRow_C(const uint8_t* src_yuy2, uint8_t* rgb_buf, int width); +extern void ____UYVYToARGBRow_C(const uint8_t* src_uyvy, uint8_t* rgb_buf, int width); + +/* 'converted_format' indicates the format that results when convert_texture_data() returns non-NULL converted_data. */ +static const int converted_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8; + +static uint8_t* convert_texture_data(const unsigned int color_format, + const uint8_t *data, + const uint8_t *palette_data, + const unsigned int width, + const unsigned int height, + const unsigned int depth, + const unsigned int row_pitch, + const unsigned int slice_pitch) +{ + // Note : Unswizzle is already done when entering here + switch (color_format) { + case NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8: { + // Test-case : WWE RAW2 + assert(depth == 1); /* FIXME */ + uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4); + unsigned int x, y; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + uint8_t index = data[y * row_pitch + x]; + uint32_t color = *(uint32_t*)(palette_data + index * 4); + *(uint32_t*)(converted_data + y * width * 4 + x * 4) = color; + } + } + return converted_data; + } + case NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X7SY9: { + assert(false); /* FIXME */ + return NULL; + } + case NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8: { + // Test-case : WWE RAW2 + assert(depth == 1); /* FIXME */ + uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4); + unsigned int y; + for (y = 0; y < height; y++) { + const uint8_t* line = &data[y * width * 2]; + uint8_t* pixel = &converted_data[(y * width) * 4]; + ____YUY2ToARGBRow_C(line, pixel, width); + // Note : LC_IMAGE_CR8YB8CB8YA8 suggests UYVY format, + // but for an unknown reason, the actual encoding is YUY2 + } + return converted_data; + } + case NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_YB8CR8YA8CB8: { + assert(depth == 1); /* FIXME */ + uint8_t* converted_data = (uint8_t*)g_malloc(width * height * 4); + unsigned int y; + for (y = 0; y < height; y++) { + const uint8_t* line = &data[y * width * 2]; + uint8_t* pixel = &converted_data[(y * width) * 4]; + ____UYVYToARGBRow_C(line, pixel, width); // TODO : Validate LC_IMAGE_YB8CR8YA8CB8 indeed requires ____UYVYToARGBRow_C() + } + return converted_data; + } + case NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_A4V6YB6A4U6YA6: { + assert(false); /* FIXME */ + return NULL; + } + case NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8CR8CB8Y8: { + assert(false); /* FIXME */ + return NULL; + } + case NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5: + case NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R6G5B5: { + assert(depth == 1); /* FIXME */ + uint8_t *converted_data = (uint8_t*)g_malloc(width * height * 4); + unsigned int y; + for (y = 0; y < height; y++) { + uint16_t rgb655 = *(uint16_t*)(data + y * row_pitch); + int8_t *pixel = (int8_t*)&converted_data[(y * width) * 4]; + __R6G5B5ToARGBRow_C((const uint8_t*)rgb655, (uint8_t*)pixel, width); + } + return converted_data; + } + default: + return NULL; + } +} + +/* returns the format of the output, either identical to the input format, or the converted format - see converted_format */ +static int upload_gl_texture(GLenum gl_target, + const TextureShape s, + const uint8_t *texture_data, + const uint8_t *palette_data) +{ + //assert(pg->opengl_enabled); + int resulting_format = s.color_format; + ColorFormatInfo f = kelvin_color_format_map[s.color_format]; + + switch(gl_target) { + case GL_TEXTURE_1D: + assert(false); + break; + case GL_TEXTURE_RECTANGLE: { + /* Can't handle strides unaligned to pixels */ + assert(s.pitch % f.bytes_per_pixel == 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, + s.pitch / f.bytes_per_pixel); + + uint8_t *unswizzled = NULL; + if (f.encoding == swizzled) { // TODO : Verify this works correctly + unswizzled = (uint8_t*)g_malloc(s.height * s.pitch); + unswizzle_rect(texture_data, s.width, s.height, + unswizzled, s.pitch, f.bytes_per_pixel); + } + uint8_t *converted = convert_texture_data(s.color_format, unswizzled ? unswizzled : texture_data, + palette_data, + s.width, s.height, 1, + s.pitch, 0); + + resulting_format = converted ? converted_format : s.color_format; + ColorFormatInfo cf = kelvin_color_format_map[resulting_format]; + glTexImage2D(gl_target, 0, cf.gl_internal_format, + s.width, s.height, 0, + cf.gl_format, cf.gl_type, + converted ? converted : unswizzled ? unswizzled : texture_data); + + if (converted) { + g_free(converted); + } + if (unswizzled) { + g_free(unswizzled); + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + break; + } + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: { + + unsigned int width = s.width, height = s.height; + + unsigned int level; + for (level = 0; level < s.levels; level++) { + if (f.encoding == compressed) { + + width = MAX(width, 4); height = MAX(height, 4); + + unsigned int block_size; + if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { + block_size = 8; + } else { + block_size = 16; + } + + glCompressedTexImage2D(gl_target, level, f.gl_internal_format, + width, height, 0, + width/4 * height/4 * block_size, + texture_data); + + texture_data += width/4 * height/4 * block_size; + } else { + + width = MAX(width, 1); height = MAX(height, 1); + + unsigned int pitch = width * f.bytes_per_pixel; + uint8_t *unswizzled = NULL; + if (f.encoding == swizzled) { + unswizzled = (uint8_t*)g_malloc(height * pitch); + unswizzle_rect(texture_data, width, height, + unswizzled, pitch, f.bytes_per_pixel); + } + + uint8_t *converted = convert_texture_data(s.color_format, unswizzled ? unswizzled : texture_data, + palette_data, + width, height, 1, + pitch, 0); + + resulting_format = converted ? converted_format : s.color_format; + ColorFormatInfo cf = kelvin_color_format_map[resulting_format]; + glTexImage2D(gl_target, level, cf.gl_internal_format, + width, height, 0, + cf.gl_format, cf.gl_type, + converted ? converted : unswizzled ? unswizzled : texture_data); + + if (converted) { + g_free(converted); + } + if (unswizzled) { + g_free(unswizzled); + } + + texture_data += pitch * height; + } + + width /= 2; + height /= 2; + } + + break; + } + case GL_TEXTURE_3D: { + + unsigned int width = s.width, height = s.height, depth = s.depth; + + unsigned int level; + for (level = 0; level < s.levels; level++) { + + unsigned int row_pitch = width * f.bytes_per_pixel; + unsigned int slice_pitch = row_pitch * height; + uint8_t *unswizzled = NULL; + if (f.encoding == swizzled) { + unswizzled = (uint8_t*)g_malloc(slice_pitch * depth); + unswizzle_box(texture_data, width, height, depth, unswizzled, + row_pitch, slice_pitch, f.bytes_per_pixel); + } + uint8_t *converted = convert_texture_data(s.color_format, unswizzled ? unswizzled : texture_data, + palette_data, + width, height, depth, + row_pitch, slice_pitch); + + resulting_format = converted ? converted_format : s.color_format; + ColorFormatInfo cf = kelvin_color_format_map[resulting_format]; + glTexImage3D(gl_target, level, cf.gl_internal_format, + width, height, depth, 0, + cf.gl_format, cf.gl_type, + converted ? converted : unswizzled ? unswizzled : texture_data); + + if (converted) { + g_free(converted); + } + if (unswizzled) { + g_free(unswizzled); + } + + texture_data += width * height * depth * f.bytes_per_pixel; + + width /= 2; + height /= 2; + depth /= 2; + } + break; + } + default: + assert(false); + break; + } + return resulting_format; +} + +static TextureBinding* generate_texture(const TextureShape s, + const uint8_t *texture_data, + const uint8_t *palette_data) +{ + // assert(pg->opengl_enabled); + + ColorFormatInfo f = kelvin_color_format_map[s.color_format]; + + /* Create a new opengl texture */ + GLuint gl_texture; + glGenTextures(1, &gl_texture); + + GLenum gl_target; + if (s.cubemap) { + assert(f.encoding != linear); + assert(s.dimensionality == 2); + gl_target = GL_TEXTURE_CUBE_MAP; + } else { + if (f.encoding == linear) { /* FIXME : Include compressed too? (!= swizzled) */ + /* linear textures use unnormalised texcoords. + * GL_TEXTURE_RECTANGLE_ARB conveniently also does, but + * does not allow repeat and mirror wrap modes. + * (or mipmapping, but xbox d3d says 'Non swizzled and non + * compressed textures cannot be mip mapped.') + * Not sure if that'll be an issue. */ + + /* FIXME: GLSL 330 provides us with textureSize()! Use that? */ + gl_target = GL_TEXTURE_RECTANGLE; + assert(s.dimensionality == 2); + } else { + switch(s.dimensionality) { + case 1: gl_target = GL_TEXTURE_1D; break; + case 2: gl_target = GL_TEXTURE_2D; break; + case 3: gl_target = GL_TEXTURE_3D; break; + default: + assert(false); + break; + } + } + } + + glBindTexture(gl_target, gl_texture); + + NV2A_GL_DLABEL(GL_TEXTURE, gl_texture, + "format: 0x%02X%s, %d dimensions%s, width: %d, height: %d, depth: %d", + s.color_format, (f.encoding == linear) ? "" : (f.encoding == swizzled) ? " (SZ)" : " (DXT)", // compressed + s.dimensionality, s.cubemap ? " (Cubemap)" : "", + s.width, s.height, s.depth); + + /* Linear textures don't support mipmapping */ + if (f.encoding != linear) { + glTexParameteri(gl_target, GL_TEXTURE_BASE_LEVEL, + s.min_mipmap_level); + glTexParameteri(gl_target, GL_TEXTURE_MAX_LEVEL, + s.levels - 1); + } + + /* Set this before calling upload_gl_texture() to prevent potential conversions */ + if (f.gl_swizzle_mask) { + glTexParameteriv(gl_target, GL_TEXTURE_SWIZZLE_RGBA, + f.gl_swizzle_mask); + } + + if (gl_target == GL_TEXTURE_CUBE_MAP) { + + size_t length = 0; + unsigned int w = s.width, h = s.height; + unsigned int level; + for (level = 0; level < s.levels; level++) { + /* FIXME: This is wrong for compressed textures and textures with 1x? non-square mipmaps */ + length += w * h * f.bytes_per_pixel; + w /= 2; + h /= 2; + } + + upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_X, + s, texture_data + 0 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + s, texture_data + 1 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + s, texture_data + 2 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + s, texture_data + 3 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + s, texture_data + 4 * length, palette_data); + upload_gl_texture(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + s, texture_data + 5 * length, palette_data); + } else { + upload_gl_texture(gl_target, s, texture_data, palette_data); + } + + TextureBinding* ret = (TextureBinding *)g_malloc(sizeof(TextureBinding)); + ret->gl_target = gl_target; + ret->gl_texture = gl_texture; + ret->refcnt = 1; + return ret; +} + +// NOTE: Might want to change guint to guint64 for return. +/* functions for texture LRU cache */ +static guint texture_key_hash(gconstpointer key) +{ + const TextureKey *k = (const TextureKey *)key; + uint64_t state_hash = fnv_hash( + (const uint8_t*)&k->state, sizeof(TextureShape)); + return guint(state_hash ^ k->data_hash); +} +static gboolean texture_key_equal(gconstpointer a, gconstpointer b) +{ + const TextureKey *ak = (const TextureKey *)a, *bk = (const TextureKey *)b; + return memcmp(&ak->state, &bk->state, sizeof(TextureShape)) == 0 + && ak->data_hash == bk->data_hash; +} +static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error) +{ + const TextureKey *k = (const TextureKey *)key; + TextureBinding *v = generate_texture(k->state, + k->texture_data, + k->palette_data); + if (error != NULL) { + *error = NULL; + } + return v; +} +static void texture_key_destroy(gpointer data) +{ + g_free(data); +} +static void texture_binding_destroy(gpointer data) +{ + TextureBinding *binding = (TextureBinding *)data; + + // assert(pg->opengl_enabled); + + assert(binding->refcnt > 0); + binding->refcnt--; + if (binding->refcnt == 0) { + glDeleteTextures(1, &binding->gl_texture); + g_free(binding); + } +} + +// NOTE: Might want to change guint to guint64 for return. +/* hash and equality for shader cache hash table */ +static guint shader_hash(gconstpointer key) +{ + return (guint)fnv_hash((const uint8_t *)key, sizeof(ShaderState)); +} +static gboolean shader_equal(gconstpointer a, gconstpointer b) +{ + const ShaderState *as = (const ShaderState *)a, *bs = (const ShaderState *)b; + return memcmp(as, bs, sizeof(ShaderState)) == 0; +} + +static unsigned int kelvin_map_stencil_op(uint32_t parameter) +{ + unsigned int op; + switch (parameter) { + case NV097_SET_STENCIL_OP_V_KEEP: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_KEEP; break; + case NV097_SET_STENCIL_OP_V_ZERO: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_ZERO; break; + case NV097_SET_STENCIL_OP_V_REPLACE: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_REPLACE; break; + case NV097_SET_STENCIL_OP_V_INCRSAT: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCRSAT; break; + case NV097_SET_STENCIL_OP_V_DECRSAT: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECRSAT; break; + case NV097_SET_STENCIL_OP_V_INVERT: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INVERT; break; + case NV097_SET_STENCIL_OP_V_INCR: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCR; break; + case NV097_SET_STENCIL_OP_V_DECR: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECR; break; + default: + assert(false); + break; + } + return op; +} + +static unsigned int kelvin_map_polygon_mode(uint32_t parameter) +{ + unsigned int mode; + switch (parameter) { + case NV097_SET_FRONT_POLYGON_MODE_V_POINT: + mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_POINT; break; + case NV097_SET_FRONT_POLYGON_MODE_V_LINE: + mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_LINE; break; + case NV097_SET_FRONT_POLYGON_MODE_V_FILL: + mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_FILL; break; + default: + assert(false); + break; + } + return mode; +} + +static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel) +{ + assert(channel < 4); + unsigned int texgen; + switch (parameter) { + case NV097_SET_TEXGEN_S_DISABLE: + texgen = NV_PGRAPH_CSV1_A_T0_S_DISABLE; break; + case NV097_SET_TEXGEN_S_EYE_LINEAR: + texgen = NV_PGRAPH_CSV1_A_T0_S_EYE_LINEAR; break; + case NV097_SET_TEXGEN_S_OBJECT_LINEAR: + texgen = NV_PGRAPH_CSV1_A_T0_S_OBJECT_LINEAR; break; + case NV097_SET_TEXGEN_S_SPHERE_MAP: + assert(channel < 2); + texgen = NV_PGRAPH_CSV1_A_T0_S_SPHERE_MAP; break; + case NV097_SET_TEXGEN_S_REFLECTION_MAP: + assert(channel < 3); + texgen = NV_PGRAPH_CSV1_A_T0_S_REFLECTION_MAP; break; + case NV097_SET_TEXGEN_S_NORMAL_MAP: + assert(channel < 3); + texgen = NV_PGRAPH_CSV1_A_T0_S_NORMAL_MAP; break; + default: + assert(false); + break; + } + return texgen; +} + +static uint64_t fnv_hash(const uint8_t *data, size_t len) +{ + /* 64 bit Fowler/Noll/Vo FNV-1a hash code */ + uint64_t hval = 0xcbf29ce484222325ULL; + const uint8_t *dp = data; + const uint8_t *de = data + len; + while (dp < de) { + hval ^= (uint64_t) *dp++; + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); + } + + return hval; +} + +static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples) +{ +#ifdef __SSE4_2__ + uint64_t h[4] = {len, 0, 0, 0}; + assert(samples > 0); + + if (len < 8 || len % 8) { + return fnv_hash(data, len); + } + + assert(len >= 8 && len % 8 == 0); + const uint64_t *dp = (const uint64_t*)data; + const uint64_t *de = dp + (len / 8); + size_t step = len / 8 / samples; + if (step == 0) step = 1; + + while (dp < de - step * 3) { + h[0] = __builtin_ia32_crc32di(h[0], dp[step * 0]); + h[1] = __builtin_ia32_crc32di(h[1], dp[step * 1]); + h[2] = __builtin_ia32_crc32di(h[2], dp[step * 2]); + h[3] = __builtin_ia32_crc32di(h[3], dp[step * 3]); + dp += step * 4; + } + if (dp < de - step * 0) + h[0] = __builtin_ia32_crc32di(h[0], dp[step * 0]); + if (dp < de - step * 1) + h[1] = __builtin_ia32_crc32di(h[1], dp[step * 1]); + if (dp < de - step * 2) + h[2] = __builtin_ia32_crc32di(h[2], dp[step * 2]); + + return h[0] + (h[1] << 10) + (h[2] << 21) + (h[3] << 32); +#else + return fnv_hash(data, len); +#endif +} diff --git a/src/devices/video/nv2a.cpp b/src/devices/video/nv2a.cpp index 30d6ba6ea..548e0627b 100644 --- a/src/devices/video/nv2a.cpp +++ b/src/devices/video/nv2a.cpp @@ -1,1376 +1,1376 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * This file is heavily based on code from XQEMU -// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a.c -// * Copyright (c) 2012 espes -// * Copyright (c) 2015 Jannik Vogel -// * Copyright (c) 2018 Matt Borgerson -// * -// * Contributions for Cxbx-Reloaded -// * Copyright (c) 2017-2018 Luke Usher -// * Copyright (c) 2018 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::NV2A - - -#include // For PKINTERRUPT, etc. - -#ifdef _MSC_VER // Check if MS Visual C compiler -# pragma comment(lib, "opengl32.lib") // Compiler-specific directive to avoid manually configuration -//# pragma comment(lib, "glu32.lib") // Link libraries -# pragma comment(lib, "glew32.lib") -#endif - -#include // For std::string -#include // For uint32_t -#include // For __beginthreadex(), etc. - -#include "core\kernel\init\CxbxKrnl.h" // For XBOX_MEMORY_SIZE, DWORD, etc -#include "core\kernel\support\Emu.h" -#include "core\kernel\exports\EmuKrnl.h" -#include "core\hle\Intercept.hpp" -#include "Logging.h" - -#include "vga.h" -#include "nv2a.h" // For NV2AState -#include "nv2a_int.h" // from https://github.com/espes/xqemu/tree/xbox/hw/xbox -//#include -#include -#include -#include -//#include - -// glib types -typedef char gchar; -typedef int gint; -typedef unsigned int guint; -typedef unsigned int guint32; -typedef const void *gconstpointer; -typedef gint gboolean; -typedef void* gpointer; - -typedef guint32 GQuark; - -typedef struct _GError GError; - -struct _GError -{ - GQuark domain; - gint code; - gchar *message; -}; - -#include "common\util\gloffscreen\glextensions.h" // for glextensions_init - -GLuint create_gl_shader(GLenum gl_shader_type, - const char *code, - const char *name); // forward to nv2a_shaders.cpp - -static void update_irq(NV2AState *d) -{ - /* PFIFO */ - if (d->pfifo.pending_interrupts & d->pfifo.enabled_interrupts) { - d->pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO; - } - else { - d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO; - } - - /* PCRTC */ - if (d->pcrtc.pending_interrupts & d->pcrtc.enabled_interrupts) { - d->pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC; - } - else { - d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC; - } - - /* PGRAPH */ - if (d->pgraph.pending_interrupts & d->pgraph.enabled_interrupts) { - d->pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH; - } - else { - d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH; - } - - /* PVIDEO */ - if (d->pvideo.pending_interrupts & d->pvideo.enabled_interrupts) { - d->pmc.pending_interrupts |= NV_PMC_INTR_0_PVIDEO; - } - else { - d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PVIDEO; - } - - /* TODO : PBUS * / - if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) { - d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS; - } - else { - d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS; - } */ - - /* TODO : SOFTWARE * / - if (d->user.pending_interrupts & d->.enabled_interrupts) { - d->pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE; - } - else { - d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE; - } */ - - if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) { - HalSystemInterrupts[3].Assert(true); - } - else { - HalSystemInterrupts[3].Assert(false); - } - - SwitchToThread(); -} - - -#include "EmuNV2A_DEBUG.cpp" - - -#define DEBUG_READ32(DEV) EmuLog(LOG_LEVEL::DEBUG, "Rd32 NV2A " #DEV "(0x%08X) = 0x%08X [Handled %s]", addr, result, DebugNV_##DEV(addr)) -#define DEBUG_READ32_UNHANDLED(DEV) { EmuLog(LOG_LEVEL::DEBUG, "Rd32 NV2A " #DEV "(0x%08X) = 0x%08X [Unhandled %s]", addr, result, DebugNV_##DEV(addr)); return result; } - -#define DEBUG_WRITE32(DEV) EmuLog(LOG_LEVEL::DEBUG, "Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Handled %s]", addr, value, DebugNV_##DEV(addr)) -#define DEBUG_WRITE32_UNHANDLED(DEV) { EmuLog(LOG_LEVEL::DEBUG, "Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Unhandled %s]", addr, value, DebugNV_##DEV(addr)); return; } - -#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(NV2AState *d, xbaddr addr) -#define DEVICE_READ32_SWITCH() uint32_t result = 0; switch (addr) -#define DEVICE_READ32_REG(dev) result = d->dev.regs[addr] -#define DEVICE_READ32_END(DEV) DEBUG_READ32(DEV); return result - -#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(NV2AState *d, xbaddr addr, uint32_t value) -#define DEVICE_WRITE32_REG(dev) d->dev.regs[addr] = value -#define DEVICE_WRITE32_END(DEV) DEBUG_WRITE32(DEV) - -static inline uint32_t ldl_le_p(const void *p) -{ - return *(uint32_t*)p; -} - -static inline void stq_le_p(uint64_t *p, uint64_t v) -{ - *p = v; -} - -static inline void stl_le_p(uint32_t *p, uint32_t v) -{ - *p = v; -} - -static DMAObject nv_dma_load(NV2AState *d, xbaddr dma_obj_address) -{ - assert(dma_obj_address < d->pramin.ramin_size); - - uint32_t *dma_obj = (uint32_t*)(d->pramin.ramin_ptr + dma_obj_address); - uint32_t flags = ldl_le_p(dma_obj); - uint32_t limit = ldl_le_p(dma_obj + 1); - uint32_t frame = ldl_le_p(dma_obj + 2); - - DMAObject object; - object.dma_class = GET_MASK(flags, NV_DMA_CLASS); - object.dma_target = GET_MASK(flags, NV_DMA_TARGET); - object.address = (frame & NV_DMA_ADDRESS) | GET_MASK(flags, NV_DMA_ADJUST); - object.limit = limit; - - return object; -} - -static void *nv_dma_map(NV2AState *d, xbaddr dma_obj_address, xbaddr *len) -{ -// assert(dma_obj_address < d->pramin.ramin_size); - - DMAObject dma = nv_dma_load(d, dma_obj_address); - - /* TODO: Handle targets and classes properly */ - NV2A_DPRINTF("dma_map %x, %x, %x %x\n", - dma.dma_class, dma.dma_target, dma.address, dma.limit); - - dma.address &= 0x07FFFFFF; - - // assert(dma.address + dma.limit < memory_region_size(d->vram)); - *len = dma.limit; - return d->vram_ptr + dma.address; -// return (void*)(PHYSICAL_MAP_BASE + dma.address); -} - -#include "EmuNV2A_PBUS.cpp" -#include "EmuNV2A_PCRTC.cpp" -#include "EmuNV2A_PFB.cpp" -#include "EmuNV2A_PGRAPH.cpp" -#include "EmuNV2A_PFIFO.cpp" -#include "EmuNV2A_PMC.cpp" -#include "EmuNV2A_PRAMDAC.cpp" -#include "EmuNV2A_PRMCIO.cpp" -#include "EmuNV2A_PRMVIO.cpp" -#include "EmuNV2A_PTIMER.cpp" -#include "EmuNV2A_PVIDEO.cpp" -#include "EmuNV2A_USER.cpp" - -#include "EmuNV2A_PRMA.cpp" -#include "EmuNV2A_PCOUNTER.cpp" -#include "EmuNV2A_PVPE.cpp" -#include "EmuNV2A_PTV.cpp" -#include "EmuNV2A_PRMFB.cpp" -#include "EmuNV2A_PSTRAPS.cpp" -#include "EmuNV2A_PRMDIO.cpp" -#include "EmuNV2A_PRAMIN.cpp" - -const NV2ABlockInfo regions[] = { // blocktable - -// Note : Avoid designated initializers to facilitate C++ builds -#define ENTRY(OFFSET, SIZE, NAME, RDFUNC, WRFUNC) \ - { \ - #NAME, OFFSET, SIZE, \ - { RDFUNC, WRFUNC }, \ - }, \ - - /* card master control */ - ENTRY(0x000000, 0x001000, PMC, EmuNV2A_PMC_Read32, EmuNV2A_PMC_Write32) - /* bus control */ - ENTRY(0x001000, 0x001000, PBUS, EmuNV2A_PBUS_Read32, EmuNV2A_PBUS_Write32) - /* MMIO and DMA FIFO submission to PGRAPH and VPE */ - ENTRY(0x002000, 0x002000, PFIFO, EmuNV2A_PFIFO_Read32, EmuNV2A_PFIFO_Write32) - /* access to BAR0/BAR1 from real mode */ - ENTRY(0x007000, 0x001000, PRMA, EmuNV2A_PRMA_Read32, EmuNV2A_PRMA_Write32) - /* video overlay */ - ENTRY(0x008000, 0x001000, PVIDEO, EmuNV2A_PVIDEO_Read32, EmuNV2A_PVIDEO_Write32) - /* time measurement and time-based alarms */ - ENTRY(0x009000, 0x001000, PTIMER, EmuNV2A_PTIMER_Read32, EmuNV2A_PTIMER_Write32) - /* performance monitoring counters */ - ENTRY(0x00a000, 0x001000, PCOUNTER, EmuNV2A_PCOUNTER_Read32, EmuNV2A_PCOUNTER_Write32) - /* MPEG2 decoding engine */ - ENTRY(0x00b000, 0x001000, PVPE, EmuNV2A_PVPE_Read32, EmuNV2A_PVPE_Write32) - /* TV encoder */ - ENTRY(0x00d000, 0x001000, PTV, EmuNV2A_PTV_Read32, EmuNV2A_PTV_Write32) - /* aliases VGA memory window */ - ENTRY(0x0a0000, 0x020000, PRMFB, EmuNV2A_PRMFB_Read32, EmuNV2A_PRMFB_Write32) - /* aliases VGA sequencer and graphics controller registers */ - ENTRY(0x0c0000, 0x008000, PRMVIO, EmuNV2A_PRMVIO_Read32, EmuNV2A_PRMVIO_Write32) // Size was 0x001000 - /* memory interface */ - ENTRY(0x100000, 0x001000, PFB, EmuNV2A_PFB_Read32, EmuNV2A_PFB_Write32) - /* straps readout / override */ - ENTRY(0x101000, 0x001000, PSTRAPS, EmuNV2A_PSTRAPS_Read32, EmuNV2A_PSTRAPS_Write32) - /* accelerated 2d/3d drawing engine */ - ENTRY(0x400000, 0x002000, PGRAPH, EmuNV2A_PGRAPH_Read32, EmuNV2A_PGRAPH_Write32) - /* more CRTC controls */ - ENTRY(0x600000, 0x001000, PCRTC, EmuNV2A_PCRTC_Read32, EmuNV2A_PCRTC_Write32) - /* aliases VGA CRTC and attribute controller registers */ - ENTRY(0x601000, 0x001000, PRMCIO, EmuNV2A_PRMCIO_Read32, EmuNV2A_PRMCIO_Write32) - /* RAMDAC, cursor, and PLL control */ - ENTRY(0x680000, 0x001000, PRAMDAC, EmuNV2A_PRAMDAC_Read32, EmuNV2A_PRAMDAC_Write32) - /* aliases VGA palette registers */ - ENTRY(0x681000, 0x001000, PRMDIO, EmuNV2A_PRMDIO_Read32, EmuNV2A_PRMDIO_Write32) - /* RAMIN access */ - ENTRY(0x700000, 0x100000, PRAMIN, EmuNV2A_PRAMIN_Read32, EmuNV2A_PRAMIN_Write32) - /* PFIFO MMIO and DMA submission area */ - ENTRY(0x800000, 0x400000, USER, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // Size was 0x800000 - /* UREMAP User area mirror - TODO : Confirm */ - ENTRY(0xC00000, 0x400000, UREMAP, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // NOTE : Mirror of USER - /* Terminating entry */ - ENTRY(0xFFFFFF, 0x000000, END, nullptr, nullptr) -#undef ENTRY -}; - -const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr) -{ - // Find the block in the block table - const NV2ABlockInfo* block = ®ions[0]; - int i = 0; - - while (block->size > 0) { - if (addr >= block->offset && addr < block->offset + block->size) { - return block; - } - - block = ®ions[++i]; - } - - return nullptr; -} - -// HACK: Until we implement VGA/proper interrupt generation -// we simulate VBLANK by calling the interrupt at 60Hz -std::thread vblank_thread; -extern std::chrono::time_point> GetNextVBlankTime(); - -void _check_gl_reset() -{ - while (true) { - GLenum err = glGetError(); - if (err == GL_NO_ERROR) - return; - if (err == 0) - return; - } -} - -void _check_gl_error(const char *file, int line) -{ - while (true) { - GLenum err = glGetError(); - char *error; - switch (err) { - case GL_NO_ERROR: return; - case GL_INVALID_ENUM: error = "GL_INVALID_ENUM"; break; - case GL_INVALID_VALUE: error = "GL_INVALID_VALUE"; break; - case GL_INVALID_OPERATION: error = "GL_INVALID_OPERATION"; break; - case GL_STACK_OVERFLOW: error = "GL_STACK_OVERFLOW"; break; - case GL_STACK_UNDERFLOW: error = "GL_STACK_UNDERFLOW"; break; - case GL_OUT_OF_MEMORY: error = "GL_OUT_OF_MEMORY"; break; - case GL_INVALID_FRAMEBUFFER_OPERATION: error = "GL_INVALID_FRAMEBUFFER_OPERATION"; break; - //case GL_INVALID_FRAMEBUFFER_OPERATION_EXT: error = "GL_INVALID_FRAMEBUFFER_OPERATION_EXT"; break; - case GL_CONTEXT_LOST: error = "GL_CONTEXT_LOST"; break; - default: error = "(unknown)"; break; - } - - printf("OpenGL error 0x%.8X %s\n", err, error); - assert(false); - } -} - -#ifdef DEBUG_NV2A_GL -#define GL_RESET() _check_gl_reset() -#define GL_CHECK() _check_gl_error(__FILE__,__LINE__) -#else -#define GL_RESET() -#define GL_CHECK() -#endif - -enum { - SAMP_TEXCOORD = 0, -}; - -enum { - FRAG_COLOR = 0, -}; - -enum { - ATTR_POSITION = 0, - ATTR_TEXCOORD = 4, -}; - -static GLint m_overlay_gl_uniform_location_texture = -1; - -GLuint Get_YUV_to_RGB_shader_program() -{ - // Use a shader to convert YUV to RGB using information from : - // From https://stackoverflow.com/questions/44291939/portable-yuv-drawing-context - // to https://hg.libsdl.org/SDL/file/1f2cb42aa5d3/src/render/opengl/SDL_shaders_gl.c#l128 - // From https://stackoverflow.com/questions/12428108/ios-how-to-draw-a-yuv-image-using-opengl - // to https://github.com/kolyvan/kxmovie/blob/master/kxmovie/KxMovieGLView.m - // and https://www.opengl.org/discussion_boards/archive/index.php/t-169186.html - // and https://gist.github.com/roxlu/9329339 - // https://github.com/g-truc/ogl-samples/blob/master/data/gl-330/texture-2d.vert - static const char *OPENGL_SHADER_YUV[2] = { - /* vertex shader */ - "#version 330 core \n" - "#define ATTR_POSITION 0 \n" - "#define ATTR_TEXCOORD 4 \n" - " \n" - "precision highp float; \n" - "precision highp int; \n" - "layout(std140, column_major) uniform; \n" - " \n" - "layout(location = ATTR_POSITION) in vec2 Position; \n" - "layout(location = ATTR_TEXCOORD) in vec2 Texcoord; \n" - " \n" - "out block \n" - "{ \n" - " vec2 Texcoord; \n" - "} Out; \n" - " \n" - "void main() \n" - "{ \n" - " Out.Texcoord = Texcoord; \n" - " gl_Position = vec4(Position, 0.0, 1.0); \n" - "} \n" - , /* fragment shader */ - // https://github.com/g-truc/ogl-samples/blob/master/data/gl-330/texture-2d.frag - "#version 330 core \n" - "#define FRAG_COLOR 0 \n" - " \n" - "precision highp float; \n" - "precision highp int; \n" - "layout(std140, column_major) uniform; \n" - " \n" - "uniform sampler2D tex_yuyv; \n" - " \n" - "in block \n" - "{ \n" - " vec2 Texcoord; \n" - "} In; \n" - " \n" - "layout(location = FRAG_COLOR, index = 0) out vec4 Color; \n" - " \n" - "// YUV offset \n" - "const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814); \n" - "// RGB coefficients \n" - "const vec3 Rcoeff = vec3(1.164, 0.000, 1.596); \n" - "const vec3 Gcoeff = vec3(1.164, -0.391, -0.813); \n" - "const vec3 Bcoeff = vec3(1.164, 2.018, 0.000); \n" - "void main(void) \n" - "{ \n" - " // Fetch 4:2:2 YUYV macropixel \n" - " vec4 yuyv = texture2D(tex_yuyv, In.Texcoord); \n" - " // Now r-g-b-a is actually y1-u-y2-v \n" - " float u = yuyv.g; \n" - " float v = yuyv.a; \n" - " vec3 yuv; \n" - " // Convert texture coordinate into texture x position \n" - " ivec2 texture_size = textureSize(tex_yuyv, 0); \n" - " float texture_x = In.Texcoord.x * texture_size.x; \n" - " // Depending on fragment x position choose y1-u-v or y2-u-v \n" - " if (mod(texture_x, 1.0) < 0.5) { // left half \n" - " float y1 = yuyv.r; \n" - " yuv = vec3(y1, u, v); \n" - " } else { // right half \n" - " float y2 = yuyv.b; \n" - " yuv = vec3(y2, u, v); \n" - " } \n" - " // Do the color transform \n" - " yuv += offset; \n" - " Color.r = dot(yuv, Rcoeff); \n" - " Color.g = dot(yuv, Gcoeff); \n" - " Color.b = dot(yuv, Bcoeff); \n" - " Color.a = 1.0; \n" - "} \n" - }; - - // Bind shader - static GLuint shader_program_name_yuv_to_rgb = -1; - if (shader_program_name_yuv_to_rgb == -1) { - // Compile vertex shader - GLuint vertex_shader_name = create_gl_shader(GL_VERTEX_SHADER, OPENGL_SHADER_YUV[0], "YUV>RGB Vertex shader"); - GL_CHECK(); - // Compile fragment shader - GLuint fragment_shader_name = create_gl_shader(GL_FRAGMENT_SHADER, OPENGL_SHADER_YUV[1], "YUV>RGB Fragment shader"); - GL_CHECK(); - - shader_program_name_yuv_to_rgb = glCreateProgram(); - GL_CHECK(); - // Link vertex and fragment shaders - glAttachShader(shader_program_name_yuv_to_rgb, vertex_shader_name); - GL_CHECK(); - glAttachShader(shader_program_name_yuv_to_rgb, fragment_shader_name); - GL_CHECK(); - glBindAttribLocation(shader_program_name_yuv_to_rgb, ATTR_POSITION, "Position"); - GL_CHECK(); - glBindAttribLocation(shader_program_name_yuv_to_rgb, ATTR_TEXCOORD, "Texcoord"); - GL_CHECK(); - glBindFragDataLocation(shader_program_name_yuv_to_rgb, FRAG_COLOR, "Color"); - GL_CHECK(); - glLinkProgram(shader_program_name_yuv_to_rgb); - GL_CHECK(); - - /* Check it linked */ - GLint linked = 0; - glGetProgramiv(shader_program_name_yuv_to_rgb, GL_LINK_STATUS, &linked); - GL_CHECK(); - if (!linked) { - GLchar log[2048]; - glGetProgramInfoLog(shader_program_name_yuv_to_rgb, 2048, NULL, log); - fprintf(stderr, "nv2a: shader linking failed: %s\n", log); - abort(); - } - - m_overlay_gl_uniform_location_texture = glGetUniformLocation(shader_program_name_yuv_to_rgb, "tex_yuyv"); - GL_CHECK(); - assert(m_overlay_gl_uniform_location_texture >= 0); - } - - return shader_program_name_yuv_to_rgb; -} - -GLuint m_framebuffer_gl_uniform_location_texture = -1; - -GLuint GetFramebufferShaderProgram() -{ - static const char *gl_framebuffer_shader_src[2] = { - /* vertex shader */ - "#version 330 core \n" - "#define ATTR_POSITION 0 \n" - "#define ATTR_TEXCOORD 4 \n" - " \n" - "precision highp float; \n" - "precision highp int; \n" - "layout(std140, column_major) uniform; \n" - " \n" - "layout(location = ATTR_POSITION) in vec2 Position; \n" - "layout(location = ATTR_TEXCOORD) in vec2 Texcoord; \n" - " \n" - "out block \n" - "{ \n" - " vec2 Texcoord; \n" - "} Out; \n" - " \n" - "void main(void) \n" - "{ \n" - " Out.Texcoord = Texcoord; \n" - " gl_Position = vec4(Position, 0.0, 1.0); \n" - "} \n" - , /* fragment shader */ - "#version 330 core \n" - "#define FRAG_COLOR 0 \n" - " \n" - "precision highp float; \n" - "precision highp int; \n" - "layout(std140, column_major) uniform; \n" - " \n" - "uniform sampler2D tex; \n" - " \n" - "in block \n" - "{ \n" - " vec2 Texcoord; \n" - "} In; \n" - " \n" - "layout(location = FRAG_COLOR, index = 0) out vec4 Color; \n" - " \n" - "void main() \n" - "{ \n" - " Color = texture2D(tex, In.Texcoord); \n" - "} \n" - }; - - // Bind shader - static GLuint m_framebuffer_gl_shader_program = -1; - if (m_framebuffer_gl_shader_program == -1) { - // Compile vertex shader - GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER, gl_framebuffer_shader_src[0], "Framebuffer vertex shader"); - GL_CHECK(); - // Compile fragment shader - GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER, gl_framebuffer_shader_src[1], "Framebuffer fragment shader"); - GL_CHECK(); - - m_framebuffer_gl_shader_program = glCreateProgram(); - GL_CHECK(); - // Link vertex and fragment shaders - glAttachShader(m_framebuffer_gl_shader_program, vertex_shader); - GL_CHECK(); - glAttachShader(m_framebuffer_gl_shader_program, fragment_shader); - GL_CHECK(); - glBindAttribLocation(m_framebuffer_gl_shader_program, ATTR_POSITION, "Position"); - GL_CHECK(); - glBindAttribLocation(m_framebuffer_gl_shader_program, ATTR_TEXCOORD, "Texcoord"); - GL_CHECK(); - glBindFragDataLocation(m_framebuffer_gl_shader_program, FRAG_COLOR, "Color"); - GL_CHECK(); - glLinkProgram(m_framebuffer_gl_shader_program); - GL_CHECK(); - - /* Check it linked */ - GLint linked = 0; - glGetProgramiv(m_framebuffer_gl_shader_program, GL_LINK_STATUS, &linked); - GL_CHECK(); - if (!linked) { - GLchar log[2048]; - glGetProgramInfoLog(m_framebuffer_gl_shader_program, 2048, NULL, log); - fprintf(stderr, "nv2a: shader linking failed: %s\n", log); - abort(); - } - - m_framebuffer_gl_uniform_location_texture = glGetUniformLocation(m_framebuffer_gl_shader_program, "tex"); - GL_CHECK(); - assert(m_framebuffer_gl_uniform_location_texture >= 0); - } - - return m_framebuffer_gl_shader_program; -} - -static int display_mode_format = 0; -static int frame_pixel_bytes = 1; - -static GLenum frame_gl_internal_format = GL_RGBA8; -static GLenum frame_gl_format = GL_BGRA; -static GLenum frame_gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; -static GLuint frame_gl_texture = 0; - -static GLsizei frame_width = 640; -static GLsizei frame_height = 480; - -void cxbx_gl_update_displaymode(NV2AState *d) { - static GLenum old_frame_gl_internal_format = GL_RGBA8; - static GLenum old_frame_gl_format = GL_BGRA; - static GLenum old_frame_gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - static GLsizei old_frame_width = 640; - static GLsizei old_frame_height = 480; - - // Derive display mode and bytes per pixel from actual hardware register contents: - // This is required for titles that use a non ARGB framebuffer, such as Beats of Rage - bool alt_mode = d->pramdac.regs[NV_PRAMDAC_GENERAL_CONTROL & (NV_PRAMDAC_SIZE - 1)] - & NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; - switch (d->prmcio.cr[NV_CIO_CRE_PIXEL_INDEX] & 0x03) { - case 1: // 8bpp - assert(false); // TODO : Verify this - frame_pixel_bytes = 1; - break; - case 2: // 15 or 16 bpp - if (alt_mode) { - // Test case : Arctic Thunder - display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5; - } - else { - display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5; - } - - frame_pixel_bytes = 2; - break; - case 0: // VGA; Fall through - case 3: // 24 or 32 bpp - if (alt_mode) { - // Test-case : WWE RAW2 - display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8; - } - else { - // Test-case : XDK sample DolphinClassic (after VGA fall-through) - display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8; - } - - frame_pixel_bytes = 4; - break; - } - - // Convert displau format to OpenGl format details - frame_gl_internal_format = kelvin_color_format_map[display_mode_format].gl_internal_format; - frame_gl_format = kelvin_color_format_map[display_mode_format].gl_format; - frame_gl_type = kelvin_color_format_map[display_mode_format].gl_type; - - frame_width = NV2ADevice::GetFrameWidth(d); - frame_height = NV2ADevice::GetFrameHeight(d); - - // Detect changes in framebuffer dimensions - if (old_frame_gl_internal_format != frame_gl_internal_format - || old_frame_gl_format != frame_gl_format - || old_frame_gl_type != frame_gl_type - || old_frame_width != frame_width - || old_frame_height != frame_height) { - old_frame_gl_internal_format = frame_gl_internal_format; - old_frame_gl_format = frame_gl_format; - old_frame_gl_type = frame_gl_type; - old_frame_width = frame_width; - old_frame_height = frame_height; - if (frame_gl_texture) { - // Destroy the texture if format changed - // Test case : XDK sample DolphinClassic - glDeleteTextures(1, &frame_gl_texture); - frame_gl_texture = 0; - } - } -} - -void cxbx_gl_render_framebuffer(NV2AState *d) -{ - // Update the frame texture - uint8_t* frame_pixels = (uint8_t*)(/*CONTIGUOUS_MEMORY_BASE=*/0x80000000 | d->pcrtc.start); // NV_PCRTC_START - uint8_t* palette_data = xbnullptr; // Note : Framebuffer formats aren't paletized - - TextureShape s; - s.cubemap = false; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path - s.dimensionality = 2; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path - s.color_format = display_mode_format; - s.levels = 1; - s.width = frame_width; - s.height = frame_height; - s.depth = 0; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path - s.min_mipmap_level = 0; - s.max_mipmap_level = 0; - s.pitch = 0; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path - - // If we need to create a (new) texture, do so - if (!frame_gl_texture) { - glGenTextures(1, &frame_gl_texture); - GL_CHECK(); - glBindTexture(GL_TEXTURE_2D, frame_gl_texture); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - GL_CHECK(); - glTexImage2D(GL_TEXTURE_2D, /*level=*/0, frame_gl_internal_format, frame_width, frame_height, /*border=*/0, frame_gl_format, frame_gl_type, NULL); - GL_CHECK(); - } - else { - glBindTexture(GL_TEXTURE_2D, frame_gl_texture); - GL_CHECK(); - } - - int rf = upload_gl_texture(GL_TEXTURE_2D, - s, - frame_pixels, - palette_data); - GL_CHECK(); - - // Note : It'd be less code to use generate_texture(), except that puts linear formats - // into GL_TEXTURE_RECTANGLE, while we need GL_TEXTURE_2D here. So instead, handle the - // difference here by separately setting the resulting format's RGBA swizzle: - ColorFormatInfo cfi = kelvin_color_format_map[rf]; - if (cfi.gl_swizzle_mask) { - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, cfi.gl_swizzle_mask); - } - -#define BLIT_FRAMEBUFFER -#ifdef BLIT_FRAMEBUFFER - // If we need to create an OpenGL framebuffer, do so - static GLuint framebuffer = -1; - if (framebuffer == -1) { - glGenFramebuffers(1, &framebuffer); - GL_CHECK(); - } - - // Target the actual framebuffer - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); -#endif - -#ifdef DEBUG_NV2A_GL - // If the screen turns purple, glDrawArrays/glBlitFramebuffer below failed - glClearColor(1.0f, 0.0f, 1.0f, 1.0f); - GL_CHECK(); - glClear(GL_COLOR_BUFFER_BIT); - GL_CHECK(); -#endif - -#ifdef BLIT_FRAMEBUFFER - // Copy frame texture to an internal frame buffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frame_gl_texture, /*level=*/0); - // Blit the active internal 'read' frame buffer to the actual 'draw' framebuffer - static const GLenum filter = GL_NEAREST; - // Note : dstY0 and dstY1 are swapped so the screen doesn't appear upside down - glBlitFramebuffer(0, 0, frame_width, frame_height, 0, frame_height, frame_width, 0, GL_COLOR_BUFFER_BIT, filter); - // Detach internal framebuffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); -#else - // Draw frame texture to host frame buffer - glUseProgram(GetFramebufferShaderProgram()); - GL_CHECK(); - glUniform1i(m_framebuffer_gl_uniform_location_texture, SAMP_TEXCOORD); - GL_CHECK(); - - static const GLfloat vertices[] = { - // x y s t - -1.0f, -1.0f, 0.0f, 1.0f, // BL - 1.0f, -1.0f, 1.0f, 1.0f, // BR - 1.0f, 1.0f, 1.0f, 0.0f, // TR - -1.0f, 1.0f, 0.0f, 0.0f, // TL - }; - - // Populate vertex buffer - static GLuint m_framebuffer_gl_vertex_buffer_object = -1; - if (m_framebuffer_gl_vertex_buffer_object == -1) { - glGenBuffers(1, &m_framebuffer_gl_vertex_buffer_object); - GL_CHECK(); - } - - glBindBuffer(GL_ARRAY_BUFFER, m_framebuffer_gl_vertex_buffer_object); - GL_CHECK(); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); - GL_CHECK(); - // Bind vertex position attribute - glVertexAttribPointer( - /*index=*/ATTR_POSITION, - /*size=vec*/2, - /*type=*/GL_FLOAT, - /*normalized?=*/GL_FALSE, - /*stride=*/4 * sizeof(GLfloat), - /*array buffer offset=*/(void*)0 - ); - GL_CHECK(); - glEnableVertexAttribArray(ATTR_POSITION); - GL_CHECK(); - // Bind vertex texture coordinate attribute - glVertexAttribPointer( - /*index=*/ATTR_TEXCOORD, - /*size=vec*/2, - /*type=*/GL_FLOAT, - /*normalized?=*/GL_FALSE, - /*stride=*/4 * sizeof(GLfloat), - /*array buffer offset=*/(void*)(2 * sizeof(GLfloat)) - ); - GL_CHECK(); - glEnableVertexAttribArray(ATTR_TEXCOORD); - GL_CHECK(); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - GL_CHECK(); -#endif -} - -void pvideo_init(NV2AState *d) -{ - //qemu_cond_init(&d->pvideo.interrupt_cond); -} - -void pvideo_destroy(NV2AState *d) -{ - if (d->pvideo.overlays[0].gl_texture) { - glDeleteTextures(1, &d->pvideo.overlays[0].gl_texture); - d->pvideo.overlays[0].gl_texture = 0; - } - - if (d->pvideo.overlays[1].gl_texture) { - glDeleteTextures(1, &d->pvideo.overlays[1].gl_texture); - d->pvideo.overlays[1].gl_texture = 0; - } - - //qemu_cond_destroy(&d->pvideo.interrupt_cond); -} - -void cxbx_gl_parse_overlay(NV2AState *d, int v) -{ - OverlayState &overlay = d->pvideo.overlays[v]; - - uint32_t video_buffer_use = (v == 0) ? NV_PVIDEO_BUFFER_0_USE : NV_PVIDEO_BUFFER_1_USE; - overlay.video_buffer_use = d->pvideo.regs[NV_PVIDEO_BUFFER] & video_buffer_use; - - // Get overlay measures (from xqemu nv2a_overlay_draw_line) : - uint32_t overlay_offset_high_26 = d->pvideo.regs[NV_PVIDEO_OFFSET(v)]; - uint32_t overlay_offset_lower_6 = d->pvideo.regs[NV_PVIDEO_POINT_IN(v)] >> 3; - uint32_t overlay_size_in = d->pvideo.regs[NV_PVIDEO_SIZE_IN(v)]; - uint32_t overlay_color_key = d->pvideo.regs[NV_PVIDEO_COLOR_KEY(v)]; - uint32_t overlay_format = d->pvideo.regs[NV_PVIDEO_FORMAT(v)]; - -#ifdef DEBUG - // Check a few assumptions - overlay.base = d->pvideo.regs[NV_PVIDEO_BASE(v)]; - overlay.limit = d->pvideo.regs[NV_PVIDEO_LIMIT(v)]; - assert(overlay.base == 0); - assert(overlay.limit == (128 * ONE_MB) - 1); // = CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1 - assert(GET_MASK(overlay_format, NV_PVIDEO_FORMAT_COLOR) == NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8); - -#endif - // Derive actual attributes - overlay.pitch = overlay_format & NV_PVIDEO_FORMAT_PITCH; - overlay.is_transparent = overlay_format & NV_PVIDEO_FORMAT_DISPLAY; - overlay.offset = overlay_offset_high_26 | overlay_offset_lower_6; - - uint32_t overlay_size_in_height_width = overlay_size_in - (overlay_offset_lower_6 >> 1); - overlay.in_height = overlay_size_in_height_width >> 16; - overlay.in_width = overlay_size_in_height_width & 0xFFFF; - - overlay.out_x = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT(v)], NV_PVIDEO_POINT_OUT_X); - overlay.out_y = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT(v)], NV_PVIDEO_POINT_OUT_Y); - overlay.out_width = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT(v)], NV_PVIDEO_SIZE_OUT_WIDTH); - overlay.out_height = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT(v)], NV_PVIDEO_SIZE_OUT_HEIGHT); - - // Detect changes in overlay dimensions - if (overlay.old_in_width != overlay.in_width - || overlay.old_in_height != overlay.in_height - || overlay.old_pitch != overlay.pitch) { - overlay.old_in_width = overlay.in_width; - overlay.old_in_height = overlay.in_height; - overlay.old_pitch = overlay.pitch; - if (overlay.gl_texture) { - glDeleteTextures(1, &overlay.gl_texture); - overlay.gl_texture = 0; - } - } - - overlay.covers_framebuffer = overlay.video_buffer_use - && (!overlay.is_transparent) - && (overlay.out_x == 0) - && (overlay.out_y == 0) - && (overlay.out_width == frame_width) - && (overlay.out_height == frame_height); -} - -void cxbx_gl_render_overlays(NV2AState *d) -{ - static const GLenum overlay_gl_internal_format = GL_RGBA8; - static const GLenum overlay_gl_format = GL_BGRA; - static const GLenum overlay_gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - - for (int v = 0; v < 2; v++) { - OverlayState &overlay = d->pvideo.overlays[v]; - if (!overlay.video_buffer_use) { - continue; - } - - // TODO : Speed this up using 2 PixelBufferObjects (and use asynchronous DMA transfer)? - - // If we need to create a (new) overlay texture, do so - if (!overlay.gl_texture) { - glGenTextures(1, &overlay.gl_texture); - GL_CHECK(); - glBindTexture(GL_TEXTURE_2D, overlay.gl_texture); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - GL_CHECK(); - // Don't average YUYV samples when resizing - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - GL_CHECK(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - GL_CHECK(); - // Note : YUYV formats are sampled using BGRA in OPENGL_SHADER_YUV[1] fragment shader - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask_BGRA); - GL_CHECK(); - glTexImage2D(GL_TEXTURE_2D, /*level=*/0, overlay_gl_internal_format, overlay.pitch / 4, overlay.in_height, /*border=*/0, overlay_gl_format, overlay_gl_type, NULL); - GL_CHECK(); - } - else { - glBindTexture(GL_TEXTURE_2D, overlay.gl_texture); - GL_CHECK(); - } - - // Update the YUV video texture - hwaddr overlay_pixels = /*CONTIGUOUS_MEMORY_BASE=*/0x80000000 | overlay.offset; - glTexSubImage2D(GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, /*yoffset=*/0, overlay.pitch / 4, overlay.in_height, overlay_gl_format, overlay_gl_type, (void*)overlay_pixels); - GL_CHECK(); - - // Note : we cannot convert overlay_offset into actual top/left coordinate, so assume (0,0) - static const int overlay_in_s = 0; - static const int overlay_in_t = 0; - - // Determine source and destination coordinates, with that draw the overlay over the framebuffer - GLint srcX0 = overlay_in_s; - GLint srcY0 = overlay_in_t; - GLint srcX1 = overlay.in_width; - GLint srcY1 = overlay.in_height; - GLint dstX0 = overlay.out_x; - GLint dstY0 = overlay.out_y; - GLint dstX1 = overlay.out_width; - GLint dstY1 = overlay.out_height; - - // Detect some special cases, for later finetuning - if (overlay_in_s != 0 || overlay_in_t != 0 || !overlay.covers_framebuffer) { - LOG_TEST_CASE("Non-standard overlay dimensions"); - } - - // Convert UV coordinates to [0.0, 1.0] - GLfloat srcX0f = (GLfloat)srcX0 / overlay.in_width; - GLfloat srcX1f = (GLfloat)srcX1 / overlay.in_width; - GLfloat srcY0f = (GLfloat)srcY0 / overlay.in_height; - GLfloat srcY1f = (GLfloat)srcY1 / overlay.in_height; - - // Convert screen coordinates to [-1.0, 1.0] - GLfloat dstX0f = (GLfloat)((dstX0 / frame_width) * 2.0f) - 1.0f; - GLfloat dstX1f = (GLfloat)((dstX1 / frame_width) * 2.0f) - 1.0f; - GLfloat dstY0f = (GLfloat)((dstY0 / frame_height) * 2.0f) - 1.0f; - GLfloat dstY1f = (GLfloat)((dstY1 / frame_height) * 2.0f) - 1.0f; - - glUseProgram(Get_YUV_to_RGB_shader_program()); - GL_CHECK(); - - // Attach texture #0 to the shader sampler location - glUniform1i(m_overlay_gl_uniform_location_texture, SAMP_TEXCOORD); - GL_CHECK(); - - // Flip Y to prevent upside down rendering - std::swap(srcY0f, srcY1f); - - // Feed screen and texture coordinates through a vertex buffer object - const GLfloat overlay_vertex_buffer_data[] = { - dstX0f, dstY0f, srcX0f, srcY0f, - dstX1f, dstY0f, srcX1f, srcY0f, - dstX1f, dstY1f, srcX1f, srcY1f, - dstX0f, dstY1f, srcX0f, srcY1f, - }; - - static GLuint overlay_gl_vertex_buffer_object = -1; - if (overlay_gl_vertex_buffer_object == -1) { - glGenBuffers(1, &overlay_gl_vertex_buffer_object); - GL_CHECK(); - } - - glBindBuffer(GL_ARRAY_BUFFER, overlay_gl_vertex_buffer_object); - GL_CHECK(); - glBufferData(GL_ARRAY_BUFFER, sizeof(overlay_vertex_buffer_data), overlay_vertex_buffer_data, GL_DYNAMIC_DRAW); - GL_CHECK(); - // Bind vertex position attribute - glVertexAttribPointer( - /*index=*/ATTR_POSITION, - /*size=vec*/2, - /*type=*/GL_FLOAT, - /*normalized?=*/GL_FALSE, - /*stride=*/4 * sizeof(GLfloat), - /*array buffer offset=*/(void*)0 - ); - GL_CHECK(); - glEnableVertexAttribArray(ATTR_POSITION); - GL_CHECK(); - // Bind vertex texture coordinate attribute - glVertexAttribPointer( - /*index=*/ATTR_TEXCOORD, - /*size=vec*/2, - /*type=*/GL_FLOAT, - /*normalized?=*/GL_FALSE, - /*stride=*/4 * sizeof(GLfloat), - /*array buffer offset=*/(void*)(2 * sizeof(GLfloat)) - ); - GL_CHECK(); - glEnableVertexAttribArray(ATTR_TEXCOORD); - GL_CHECK(); - // Finally! Draw the dang overlay... - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - GL_CHECK(); - } -} - -extern void UpdateFPSCounter(); -void NV2ADevice::UpdateHostDisplay(NV2AState *d) -{ - PGRAPHState *pg = &d->pgraph; - if (!pg->opengl_enabled) { - return; - } - - NV2A_GL_DGROUP_BEGIN("VGA Frame"); - - glo_set_current(pg->gl_context); - - cxbx_gl_update_displaymode(d); - - for (int v = 0; v < 2; v++) { - cxbx_gl_parse_overlay(d, v); - } - - GL_RESET(); - - // Target the host framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); // NOTE : If disabled, overlays don't show?! - GL_CHECK(); - glDisable(GL_CULL_FACE); - GL_CHECK(); - // Render using texture #0 - glActiveTexture(GL_TEXTURE0); - GL_CHECK(); - - // Is either overlay fullscreen ? - if (d->pvideo.overlays[0].covers_framebuffer - || d->pvideo.overlays[1].covers_framebuffer) { - // Then the framebuffer won't be visible anyway, so doesn't have to be rendered - } else { - cxbx_gl_render_framebuffer(d); - } - - cxbx_gl_render_overlays(d); - - // Unbind everything we've used - glUseProgram(0); - GL_CHECK(); - glBindBuffer(GL_ARRAY_BUFFER, 0);//pg->gl_memory_buffer); - GL_CHECK(); - glBindTexture(GL_TEXTURE_2D, 0);//pg->gl_color_buffer); - GL_CHECK(); - // Restore xbox framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer); - GL_CHECK(); - - glo_swap(pg->gl_context); - - NV2A_GL_DGROUP_END(); - -// glo_set_current(NULL); - - UpdateFPSCounter(); -} - -// TODO: Fix this properly -static void nv2a_vblank_thread(NV2AState *d) -{ - SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); - CxbxSetThreadName("Cxbx NV2A VBLANK"); - auto nextVBlankTime = GetNextVBlankTime(); - - while (!d->exiting) { - // Handle VBlank - if (std::chrono::steady_clock::now() > nextVBlankTime) { - d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK; - update_irq(d); - nextVBlankTime = GetNextVBlankTime(); - - // TODO: We should swap here for the purposes of supporting overlays + direct framebuffer access - // But it causes crashes on AMD hardware for reasons currently unknown... - //NV2ADevice::UpdateHostDisplay(d); - } - - Sleep(1); - } -} - -// See NV2ABlockInfo regions[] PRAMIN -#define NV_PRAMIN_ADDR 0x00700000 -#define NV_PRAMIN_SIZE 0x100000 - -void CxbxReserveNV2AMemory(NV2AState *d) -{ - // The NV2A memory was reserved already by the loader! - - printf("[0x%.4X] INIT: Reserved %d MiB of Xbox NV2A memory at 0x%.8X to 0x%.8X\n", - GetCurrentThreadId(), NV2A_SIZE / ONE_MB, NV2A_ADDR, NV2A_ADDR + NV2A_SIZE - 1); - - // Allocate PRAMIN Region (the loader only reserved this region, it still needs to be committed!) - // We are looping here because memory-reservation happens in 64 KiB increments - d->pramin.ramin_size = NV_PRAMIN_SIZE; - d->pramin.ramin_ptr = (uint8_t*)(NV2A_ADDR + NV_PRAMIN_ADDR); - for (int i = 0; i < 16; i++) { - LPVOID ret = VirtualAlloc((LPVOID)(NV2A_ADDR + NV_PRAMIN_ADDR + i * 64 * ONE_KB), 64 * ONE_KB, MEM_COMMIT, PAGE_READWRITE); - if (ret != (LPVOID)(NV2A_ADDR + NV_PRAMIN_ADDR + i * 64 * ONE_KB)) { - CxbxKrnlCleanup("VirtualAlloc failed to commit the memory for the nv2a pramin. The error was 0x%08X", GetLastError()); - } - } - - printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8p to 0x%.8p\n", - GetCurrentThreadId(), d->pramin.ramin_size / ONE_MB, (uintptr_t)d->pramin.ramin_ptr, (uintptr_t)d->pramin.ramin_ptr + d->pramin.ramin_size - 1); -} - -/* NV2ADevice */ - -NV2ADevice::NV2ADevice() -{ - m_nv2a_state = new NV2AState(); - m_nv2a_state->pgraph.opengl_enabled = bLLE_GPU; -} - -NV2ADevice::~NV2ADevice() -{ - Reset(); // TODO : Review this - delete m_nv2a_state; -} - -// PCI Device functions - -void NV2ADevice::Init() -{ - PCIBarRegister r; - - // Register Memory bar : - r.Raw.type = PCI_BAR_TYPE_MEMORY; - r.Memory.address = NV2A_ADDR >> 4; - RegisterBAR(0, NV2A_SIZE, r.value); - - // Register physical memory on bar 1 - r.Memory.address = 0xF0000000 >> 4; - RegisterBAR(1, g_SystemMaxMemory, r.value); - - m_DeviceId = 0x02A5; - m_VendorId = PCI_VENDOR_ID_NVIDIA; - - NV2AState *d = m_nv2a_state; // glue - - CxbxReserveNV2AMemory(d); - - d->pcrtc.start = 0; - - d->vram_ptr = (uint8_t*)PHYSICAL_MAP_BASE; - d->vram_size = g_SystemMaxMemory; - - d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */ - d->pramdac.core_clock_freq = 189000000; - d->pramdac.memory_clock_coeff = 0; - d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */ - - // Setup the conditions/mutexes - pgraph_init(d); - - // Only init PVIDEO when LLE is enabled - if (d->pgraph.opengl_enabled) { - pvideo_init(d); - } - - vblank_thread = std::thread(nv2a_vblank_thread, d); - - qemu_mutex_init(&d->pfifo.pfifo_lock); - qemu_cond_init(&d->pfifo.puller_cond); - qemu_cond_init(&d->pfifo.pusher_cond); - - d->pfifo.regs[NV_PFIFO_CACHE1_STATUS] |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; - - /* fire up puller */ - d->pfifo.puller_thread = std::thread(pfifo_puller_thread, d); - /* fire up pusher */ - d->pfifo.pusher_thread = std::thread(pfifo_pusher_thread, d); -} - -void NV2ADevice::Reset() -{ - NV2AState *d = m_nv2a_state; // glue - if (!d) return; - - d->exiting = true; - - qemu_cond_broadcast(&d->pfifo.puller_cond); - qemu_cond_broadcast(&d->pfifo.pusher_cond); - d->pfifo.puller_thread.join(); - d->pfifo.pusher_thread.join(); - qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cbxbx addition - if (d->pgraph.opengl_enabled) { - vblank_thread.join(); - pvideo_destroy(d); - } - - pgraph_destroy(&d->pgraph); -} - -uint32_t NV2ADevice::IORead(int barIndex, uint32_t port, unsigned size) -{ - return 0; -} - -void NV2ADevice::IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) -{ -} - -uint32_t NV2ADevice::BlockRead(const NV2ABlockInfo* block, uint32_t addr, unsigned size) -{ - switch (size) { - case sizeof(uint8_t) : - return block->ops.read(m_nv2a_state, addr - block->offset) & 0xFF; - case sizeof(uint16_t) : - assert((addr & 1) == 0); // TODO : What if this fails? - - return block->ops.read(m_nv2a_state, addr - block->offset) & 0xFFFF; - case sizeof(uint32_t) : - assert((addr & 3) == 0); // TODO : What if this fails? - - return block->ops.read(m_nv2a_state, addr - block->offset); - default: - assert(false); - - return 0; - } -} - -uint32_t NV2ADevice::MMIORead(int barIndex, uint32_t addr, unsigned size) -{ - switch (barIndex) { - case 0: { - // Access NV2A regardless weither HLE is disabled or not (ignoring bLLE_GPU) - const NV2ABlockInfo* block = EmuNV2A_Block(addr); - if (block != nullptr) { - return BlockRead(block, addr, size); - } - break; - } - case 1: { - // TODO : access physical memory - break; - } - } - - EmuLog(LOG_LEVEL::WARNING, "NV2ADevice::MMIORead: Unhandled barIndex %d, addr %08X, size %d", barIndex, addr, size); - return 0; -} - -void NV2ADevice::BlockWrite(const NV2ABlockInfo* block, uint32_t addr, uint32_t value, unsigned size) -{ - switch (size) { - case sizeof(uint8_t) : { -#if 0 - xbaddr aligned_addr; - uint32_t aligned_value; - int shift; - uint32_t mask; - - aligned_addr = addr & ~3; - aligned_value = block->ops.read(m_nv2a_state, aligned_addr - block->offset); - shift = (addr & 3) * 8; - mask = 0xFF << shift; - block->ops.write(m_nv2a_state, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); -#else - block->ops.write(m_nv2a_state, addr - block->offset, value); -#endif - return; - } - case sizeof(uint16_t) : { - assert((addr & 1) == 0); // TODO : What if this fails? - - xbaddr aligned_addr; - uint32_t aligned_value; - int shift; - uint32_t mask; - - aligned_addr = addr & ~3; - aligned_value = block->ops.read(m_nv2a_state, aligned_addr - block->offset); - shift = (addr & 2) * 16; - mask = 0xFFFF << shift; - block->ops.write(m_nv2a_state, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); - return; - } - case sizeof(uint32_t) : - assert((addr & 3) == 0); // TODO : What if this fails? - - block->ops.write(m_nv2a_state, addr - block->offset, value); - return; - } -} - -void NV2ADevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) -{ - switch (barIndex) { - case 0: { - // Access NV2A regardless whether HLE is disabled or not (ignoring bLLE_GPU) - const NV2ABlockInfo* block = EmuNV2A_Block(addr); - - if (block != nullptr) { - BlockWrite(block, addr, value, size); - return; - } - - break; - } - case 1: { - // TODO : access physical memory - break; - } - } - - EmuLog(LOG_LEVEL::WARNING, "NV2ADevice::MMIOWrite: Unhandled barIndex %d, addr %08X, value %08X, size %d", barIndex, addr, value, size); -} - -int NV2ADevice::GetFrameHeight(NV2AState* d) -{ - // Derive frame_height from hardware registers - int height = ((int)d->prmcio.cr[NV_CIO_CR_VDE_INDEX]) - | (((int)d->prmcio.cr[NV_CIO_CR_OVL_INDEX] & 0x02) >> 1 << 8) - | (((int)d->prmcio.cr[NV_CIO_CR_OVL_INDEX] & 0x40) >> 6 << 9) - | (((int)d->prmcio.cr[NV_CIO_CRE_LSR_INDEX] & 0x02) >> 1 << 10); - - return height++; -} - -int NV2ADevice::GetFrameWidth(NV2AState* d) -{ - // Test case : Arctic Thunder, sets a 16 bit framebuffer (R5G6B5) not via - // AvSetDisplayMode(), but via VGA control register writes, which implies - // that it's format argument cannot be used to determine the framebuffer - // width. Instead, read the framebuffer width from the VGA control registers : - int width = ((int)d->prmcio.cr[NV_CIO_CR_OFFSET_INDEX]) - | (0x700 & ((int)d->prmcio.cr[NV_CIO_CRE_RPC0_INDEX] << 3)) - | (0x800 & ((int)d->prmcio.cr[NV_CIO_CRE_LSR_INDEX] << 6)); - width *= 8; - width /= frame_pixel_bytes; - - return width; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * This file is heavily based on code from XQEMU +// * https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a/nv2a.c +// * Copyright (c) 2012 espes +// * Copyright (c) 2015 Jannik Vogel +// * Copyright (c) 2018 Matt Borgerson +// * +// * Contributions for Cxbx-Reloaded +// * Copyright (c) 2017-2018 Luke Usher +// * Copyright (c) 2018 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::NV2A + + +#include // For PKINTERRUPT, etc. + +#ifdef _MSC_VER // Check if MS Visual C compiler +# pragma comment(lib, "opengl32.lib") // Compiler-specific directive to avoid manually configuration +//# pragma comment(lib, "glu32.lib") // Link libraries +# pragma comment(lib, "glew32.lib") +#endif + +#include // For std::string +#include // For uint32_t +#include // For __beginthreadex(), etc. + +#include "core\kernel\init\CxbxKrnl.h" // For XBOX_MEMORY_SIZE, DWORD, etc +#include "core\kernel\support\Emu.h" +#include "core\kernel\exports\EmuKrnl.h" +#include "core\hle\Intercept.hpp" +#include "Logging.h" + +#include "vga.h" +#include "nv2a.h" // For NV2AState +#include "nv2a_int.h" // from https://github.com/espes/xqemu/tree/xbox/hw/xbox +//#include +#include +#include +#include +//#include + +// glib types +typedef char gchar; +typedef int gint; +typedef unsigned int guint; +typedef unsigned int guint32; +typedef const void *gconstpointer; +typedef gint gboolean; +typedef void* gpointer; + +typedef guint32 GQuark; + +typedef struct _GError GError; + +struct _GError +{ + GQuark domain; + gint code; + gchar *message; +}; + +#include "common\util\gloffscreen\glextensions.h" // for glextensions_init + +GLuint create_gl_shader(GLenum gl_shader_type, + const char *code, + const char *name); // forward to nv2a_shaders.cpp + +static void update_irq(NV2AState *d) +{ + /* PFIFO */ + if (d->pfifo.pending_interrupts & d->pfifo.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO; + } + + /* PCRTC */ + if (d->pcrtc.pending_interrupts & d->pcrtc.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC; + } + + /* PGRAPH */ + if (d->pgraph.pending_interrupts & d->pgraph.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH; + } + + /* PVIDEO */ + if (d->pvideo.pending_interrupts & d->pvideo.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PVIDEO; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PVIDEO; + } + + /* TODO : PBUS * / + if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS; + } */ + + /* TODO : SOFTWARE * / + if (d->user.pending_interrupts & d->.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE; + } + else { + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE; + } */ + + if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) { + HalSystemInterrupts[3].Assert(true); + } + else { + HalSystemInterrupts[3].Assert(false); + } + + SwitchToThread(); +} + + +#include "EmuNV2A_DEBUG.cpp" + + +#define DEBUG_READ32(DEV) EmuLog(LOG_LEVEL::DEBUG, "Rd32 NV2A " #DEV "(0x%08X) = 0x%08X [Handled %s]", addr, result, DebugNV_##DEV(addr)) +#define DEBUG_READ32_UNHANDLED(DEV) { EmuLog(LOG_LEVEL::DEBUG, "Rd32 NV2A " #DEV "(0x%08X) = 0x%08X [Unhandled %s]", addr, result, DebugNV_##DEV(addr)); return result; } + +#define DEBUG_WRITE32(DEV) EmuLog(LOG_LEVEL::DEBUG, "Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Handled %s]", addr, value, DebugNV_##DEV(addr)) +#define DEBUG_WRITE32_UNHANDLED(DEV) { EmuLog(LOG_LEVEL::DEBUG, "Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Unhandled %s]", addr, value, DebugNV_##DEV(addr)); return; } + +#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(NV2AState *d, xbaddr addr) +#define DEVICE_READ32_SWITCH() uint32_t result = 0; switch (addr) +#define DEVICE_READ32_REG(dev) result = d->dev.regs[addr] +#define DEVICE_READ32_END(DEV) DEBUG_READ32(DEV); return result + +#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(NV2AState *d, xbaddr addr, uint32_t value) +#define DEVICE_WRITE32_REG(dev) d->dev.regs[addr] = value +#define DEVICE_WRITE32_END(DEV) DEBUG_WRITE32(DEV) + +static inline uint32_t ldl_le_p(const void *p) +{ + return *(uint32_t*)p; +} + +static inline void stq_le_p(uint64_t *p, uint64_t v) +{ + *p = v; +} + +static inline void stl_le_p(uint32_t *p, uint32_t v) +{ + *p = v; +} + +static DMAObject nv_dma_load(NV2AState *d, xbaddr dma_obj_address) +{ + assert(dma_obj_address < d->pramin.ramin_size); + + uint32_t *dma_obj = (uint32_t*)(d->pramin.ramin_ptr + dma_obj_address); + uint32_t flags = ldl_le_p(dma_obj); + uint32_t limit = ldl_le_p(dma_obj + 1); + uint32_t frame = ldl_le_p(dma_obj + 2); + + DMAObject object; + object.dma_class = GET_MASK(flags, NV_DMA_CLASS); + object.dma_target = GET_MASK(flags, NV_DMA_TARGET); + object.address = (frame & NV_DMA_ADDRESS) | GET_MASK(flags, NV_DMA_ADJUST); + object.limit = limit; + + return object; +} + +static void *nv_dma_map(NV2AState *d, xbaddr dma_obj_address, xbaddr *len) +{ +// assert(dma_obj_address < d->pramin.ramin_size); + + DMAObject dma = nv_dma_load(d, dma_obj_address); + + /* TODO: Handle targets and classes properly */ + NV2A_DPRINTF("dma_map %x, %x, %x %x\n", + dma.dma_class, dma.dma_target, dma.address, dma.limit); + + dma.address &= 0x07FFFFFF; + + // assert(dma.address + dma.limit < memory_region_size(d->vram)); + *len = dma.limit; + return d->vram_ptr + dma.address; +// return (void*)(PHYSICAL_MAP_BASE + dma.address); +} + +#include "EmuNV2A_PBUS.cpp" +#include "EmuNV2A_PCRTC.cpp" +#include "EmuNV2A_PFB.cpp" +#include "EmuNV2A_PGRAPH.cpp" +#include "EmuNV2A_PFIFO.cpp" +#include "EmuNV2A_PMC.cpp" +#include "EmuNV2A_PRAMDAC.cpp" +#include "EmuNV2A_PRMCIO.cpp" +#include "EmuNV2A_PRMVIO.cpp" +#include "EmuNV2A_PTIMER.cpp" +#include "EmuNV2A_PVIDEO.cpp" +#include "EmuNV2A_USER.cpp" + +#include "EmuNV2A_PRMA.cpp" +#include "EmuNV2A_PCOUNTER.cpp" +#include "EmuNV2A_PVPE.cpp" +#include "EmuNV2A_PTV.cpp" +#include "EmuNV2A_PRMFB.cpp" +#include "EmuNV2A_PSTRAPS.cpp" +#include "EmuNV2A_PRMDIO.cpp" +#include "EmuNV2A_PRAMIN.cpp" + +const NV2ABlockInfo regions[] = { // blocktable + +// Note : Avoid designated initializers to facilitate C++ builds +#define ENTRY(OFFSET, SIZE, NAME, RDFUNC, WRFUNC) \ + { \ + #NAME, OFFSET, SIZE, \ + { RDFUNC, WRFUNC }, \ + }, \ + + /* card master control */ + ENTRY(0x000000, 0x001000, PMC, EmuNV2A_PMC_Read32, EmuNV2A_PMC_Write32) + /* bus control */ + ENTRY(0x001000, 0x001000, PBUS, EmuNV2A_PBUS_Read32, EmuNV2A_PBUS_Write32) + /* MMIO and DMA FIFO submission to PGRAPH and VPE */ + ENTRY(0x002000, 0x002000, PFIFO, EmuNV2A_PFIFO_Read32, EmuNV2A_PFIFO_Write32) + /* access to BAR0/BAR1 from real mode */ + ENTRY(0x007000, 0x001000, PRMA, EmuNV2A_PRMA_Read32, EmuNV2A_PRMA_Write32) + /* video overlay */ + ENTRY(0x008000, 0x001000, PVIDEO, EmuNV2A_PVIDEO_Read32, EmuNV2A_PVIDEO_Write32) + /* time measurement and time-based alarms */ + ENTRY(0x009000, 0x001000, PTIMER, EmuNV2A_PTIMER_Read32, EmuNV2A_PTIMER_Write32) + /* performance monitoring counters */ + ENTRY(0x00a000, 0x001000, PCOUNTER, EmuNV2A_PCOUNTER_Read32, EmuNV2A_PCOUNTER_Write32) + /* MPEG2 decoding engine */ + ENTRY(0x00b000, 0x001000, PVPE, EmuNV2A_PVPE_Read32, EmuNV2A_PVPE_Write32) + /* TV encoder */ + ENTRY(0x00d000, 0x001000, PTV, EmuNV2A_PTV_Read32, EmuNV2A_PTV_Write32) + /* aliases VGA memory window */ + ENTRY(0x0a0000, 0x020000, PRMFB, EmuNV2A_PRMFB_Read32, EmuNV2A_PRMFB_Write32) + /* aliases VGA sequencer and graphics controller registers */ + ENTRY(0x0c0000, 0x008000, PRMVIO, EmuNV2A_PRMVIO_Read32, EmuNV2A_PRMVIO_Write32) // Size was 0x001000 + /* memory interface */ + ENTRY(0x100000, 0x001000, PFB, EmuNV2A_PFB_Read32, EmuNV2A_PFB_Write32) + /* straps readout / override */ + ENTRY(0x101000, 0x001000, PSTRAPS, EmuNV2A_PSTRAPS_Read32, EmuNV2A_PSTRAPS_Write32) + /* accelerated 2d/3d drawing engine */ + ENTRY(0x400000, 0x002000, PGRAPH, EmuNV2A_PGRAPH_Read32, EmuNV2A_PGRAPH_Write32) + /* more CRTC controls */ + ENTRY(0x600000, 0x001000, PCRTC, EmuNV2A_PCRTC_Read32, EmuNV2A_PCRTC_Write32) + /* aliases VGA CRTC and attribute controller registers */ + ENTRY(0x601000, 0x001000, PRMCIO, EmuNV2A_PRMCIO_Read32, EmuNV2A_PRMCIO_Write32) + /* RAMDAC, cursor, and PLL control */ + ENTRY(0x680000, 0x001000, PRAMDAC, EmuNV2A_PRAMDAC_Read32, EmuNV2A_PRAMDAC_Write32) + /* aliases VGA palette registers */ + ENTRY(0x681000, 0x001000, PRMDIO, EmuNV2A_PRMDIO_Read32, EmuNV2A_PRMDIO_Write32) + /* RAMIN access */ + ENTRY(0x700000, 0x100000, PRAMIN, EmuNV2A_PRAMIN_Read32, EmuNV2A_PRAMIN_Write32) + /* PFIFO MMIO and DMA submission area */ + ENTRY(0x800000, 0x400000, USER, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // Size was 0x800000 + /* UREMAP User area mirror - TODO : Confirm */ + ENTRY(0xC00000, 0x400000, UREMAP, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // NOTE : Mirror of USER + /* Terminating entry */ + ENTRY(0xFFFFFF, 0x000000, END, nullptr, nullptr) +#undef ENTRY +}; + +const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr) +{ + // Find the block in the block table + const NV2ABlockInfo* block = ®ions[0]; + int i = 0; + + while (block->size > 0) { + if (addr >= block->offset && addr < block->offset + block->size) { + return block; + } + + block = ®ions[++i]; + } + + return nullptr; +} + +// HACK: Until we implement VGA/proper interrupt generation +// we simulate VBLANK by calling the interrupt at 60Hz +std::thread vblank_thread; +extern std::chrono::time_point> GetNextVBlankTime(); + +void _check_gl_reset() +{ + while (true) { + GLenum err = glGetError(); + if (err == GL_NO_ERROR) + return; + if (err == 0) + return; + } +} + +void _check_gl_error(const char *file, int line) +{ + while (true) { + GLenum err = glGetError(); + char *error; + switch (err) { + case GL_NO_ERROR: return; + case GL_INVALID_ENUM: error = "GL_INVALID_ENUM"; break; + case GL_INVALID_VALUE: error = "GL_INVALID_VALUE"; break; + case GL_INVALID_OPERATION: error = "GL_INVALID_OPERATION"; break; + case GL_STACK_OVERFLOW: error = "GL_STACK_OVERFLOW"; break; + case GL_STACK_UNDERFLOW: error = "GL_STACK_UNDERFLOW"; break; + case GL_OUT_OF_MEMORY: error = "GL_OUT_OF_MEMORY"; break; + case GL_INVALID_FRAMEBUFFER_OPERATION: error = "GL_INVALID_FRAMEBUFFER_OPERATION"; break; + //case GL_INVALID_FRAMEBUFFER_OPERATION_EXT: error = "GL_INVALID_FRAMEBUFFER_OPERATION_EXT"; break; + case GL_CONTEXT_LOST: error = "GL_CONTEXT_LOST"; break; + default: error = "(unknown)"; break; + } + + printf("OpenGL error 0x%.8X %s\n", err, error); + assert(false); + } +} + +#ifdef DEBUG_NV2A_GL +#define GL_RESET() _check_gl_reset() +#define GL_CHECK() _check_gl_error(__FILE__,__LINE__) +#else +#define GL_RESET() +#define GL_CHECK() +#endif + +enum { + SAMP_TEXCOORD = 0, +}; + +enum { + FRAG_COLOR = 0, +}; + +enum { + ATTR_POSITION = 0, + ATTR_TEXCOORD = 4, +}; + +static GLint m_overlay_gl_uniform_location_texture = -1; + +GLuint Get_YUV_to_RGB_shader_program() +{ + // Use a shader to convert YUV to RGB using information from : + // From https://stackoverflow.com/questions/44291939/portable-yuv-drawing-context + // to https://hg.libsdl.org/SDL/file/1f2cb42aa5d3/src/render/opengl/SDL_shaders_gl.c#l128 + // From https://stackoverflow.com/questions/12428108/ios-how-to-draw-a-yuv-image-using-opengl + // to https://github.com/kolyvan/kxmovie/blob/master/kxmovie/KxMovieGLView.m + // and https://www.opengl.org/discussion_boards/archive/index.php/t-169186.html + // and https://gist.github.com/roxlu/9329339 + // https://github.com/g-truc/ogl-samples/blob/master/data/gl-330/texture-2d.vert + static const char *OPENGL_SHADER_YUV[2] = { + /* vertex shader */ + "#version 330 core \n" + "#define ATTR_POSITION 0 \n" + "#define ATTR_TEXCOORD 4 \n" + " \n" + "precision highp float; \n" + "precision highp int; \n" + "layout(std140, column_major) uniform; \n" + " \n" + "layout(location = ATTR_POSITION) in vec2 Position; \n" + "layout(location = ATTR_TEXCOORD) in vec2 Texcoord; \n" + " \n" + "out block \n" + "{ \n" + " vec2 Texcoord; \n" + "} Out; \n" + " \n" + "void main() \n" + "{ \n" + " Out.Texcoord = Texcoord; \n" + " gl_Position = vec4(Position, 0.0, 1.0); \n" + "} \n" + , /* fragment shader */ + // https://github.com/g-truc/ogl-samples/blob/master/data/gl-330/texture-2d.frag + "#version 330 core \n" + "#define FRAG_COLOR 0 \n" + " \n" + "precision highp float; \n" + "precision highp int; \n" + "layout(std140, column_major) uniform; \n" + " \n" + "uniform sampler2D tex_yuyv; \n" + " \n" + "in block \n" + "{ \n" + " vec2 Texcoord; \n" + "} In; \n" + " \n" + "layout(location = FRAG_COLOR, index = 0) out vec4 Color; \n" + " \n" + "// YUV offset \n" + "const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814); \n" + "// RGB coefficients \n" + "const vec3 Rcoeff = vec3(1.164, 0.000, 1.596); \n" + "const vec3 Gcoeff = vec3(1.164, -0.391, -0.813); \n" + "const vec3 Bcoeff = vec3(1.164, 2.018, 0.000); \n" + "void main(void) \n" + "{ \n" + " // Fetch 4:2:2 YUYV macropixel \n" + " vec4 yuyv = texture2D(tex_yuyv, In.Texcoord); \n" + " // Now r-g-b-a is actually y1-u-y2-v \n" + " float u = yuyv.g; \n" + " float v = yuyv.a; \n" + " vec3 yuv; \n" + " // Convert texture coordinate into texture x position \n" + " ivec2 texture_size = textureSize(tex_yuyv, 0); \n" + " float texture_x = In.Texcoord.x * texture_size.x; \n" + " // Depending on fragment x position choose y1-u-v or y2-u-v \n" + " if (mod(texture_x, 1.0) < 0.5) { // left half \n" + " float y1 = yuyv.r; \n" + " yuv = vec3(y1, u, v); \n" + " } else { // right half \n" + " float y2 = yuyv.b; \n" + " yuv = vec3(y2, u, v); \n" + " } \n" + " // Do the color transform \n" + " yuv += offset; \n" + " Color.r = dot(yuv, Rcoeff); \n" + " Color.g = dot(yuv, Gcoeff); \n" + " Color.b = dot(yuv, Bcoeff); \n" + " Color.a = 1.0; \n" + "} \n" + }; + + // Bind shader + static GLuint shader_program_name_yuv_to_rgb = -1; + if (shader_program_name_yuv_to_rgb == -1) { + // Compile vertex shader + GLuint vertex_shader_name = create_gl_shader(GL_VERTEX_SHADER, OPENGL_SHADER_YUV[0], "YUV>RGB Vertex shader"); + GL_CHECK(); + // Compile fragment shader + GLuint fragment_shader_name = create_gl_shader(GL_FRAGMENT_SHADER, OPENGL_SHADER_YUV[1], "YUV>RGB Fragment shader"); + GL_CHECK(); + + shader_program_name_yuv_to_rgb = glCreateProgram(); + GL_CHECK(); + // Link vertex and fragment shaders + glAttachShader(shader_program_name_yuv_to_rgb, vertex_shader_name); + GL_CHECK(); + glAttachShader(shader_program_name_yuv_to_rgb, fragment_shader_name); + GL_CHECK(); + glBindAttribLocation(shader_program_name_yuv_to_rgb, ATTR_POSITION, "Position"); + GL_CHECK(); + glBindAttribLocation(shader_program_name_yuv_to_rgb, ATTR_TEXCOORD, "Texcoord"); + GL_CHECK(); + glBindFragDataLocation(shader_program_name_yuv_to_rgb, FRAG_COLOR, "Color"); + GL_CHECK(); + glLinkProgram(shader_program_name_yuv_to_rgb); + GL_CHECK(); + + /* Check it linked */ + GLint linked = 0; + glGetProgramiv(shader_program_name_yuv_to_rgb, GL_LINK_STATUS, &linked); + GL_CHECK(); + if (!linked) { + GLchar log[2048]; + glGetProgramInfoLog(shader_program_name_yuv_to_rgb, 2048, NULL, log); + fprintf(stderr, "nv2a: shader linking failed: %s\n", log); + abort(); + } + + m_overlay_gl_uniform_location_texture = glGetUniformLocation(shader_program_name_yuv_to_rgb, "tex_yuyv"); + GL_CHECK(); + assert(m_overlay_gl_uniform_location_texture >= 0); + } + + return shader_program_name_yuv_to_rgb; +} + +GLuint m_framebuffer_gl_uniform_location_texture = -1; + +GLuint GetFramebufferShaderProgram() +{ + static const char *gl_framebuffer_shader_src[2] = { + /* vertex shader */ + "#version 330 core \n" + "#define ATTR_POSITION 0 \n" + "#define ATTR_TEXCOORD 4 \n" + " \n" + "precision highp float; \n" + "precision highp int; \n" + "layout(std140, column_major) uniform; \n" + " \n" + "layout(location = ATTR_POSITION) in vec2 Position; \n" + "layout(location = ATTR_TEXCOORD) in vec2 Texcoord; \n" + " \n" + "out block \n" + "{ \n" + " vec2 Texcoord; \n" + "} Out; \n" + " \n" + "void main(void) \n" + "{ \n" + " Out.Texcoord = Texcoord; \n" + " gl_Position = vec4(Position, 0.0, 1.0); \n" + "} \n" + , /* fragment shader */ + "#version 330 core \n" + "#define FRAG_COLOR 0 \n" + " \n" + "precision highp float; \n" + "precision highp int; \n" + "layout(std140, column_major) uniform; \n" + " \n" + "uniform sampler2D tex; \n" + " \n" + "in block \n" + "{ \n" + " vec2 Texcoord; \n" + "} In; \n" + " \n" + "layout(location = FRAG_COLOR, index = 0) out vec4 Color; \n" + " \n" + "void main() \n" + "{ \n" + " Color = texture2D(tex, In.Texcoord); \n" + "} \n" + }; + + // Bind shader + static GLuint m_framebuffer_gl_shader_program = -1; + if (m_framebuffer_gl_shader_program == -1) { + // Compile vertex shader + GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER, gl_framebuffer_shader_src[0], "Framebuffer vertex shader"); + GL_CHECK(); + // Compile fragment shader + GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER, gl_framebuffer_shader_src[1], "Framebuffer fragment shader"); + GL_CHECK(); + + m_framebuffer_gl_shader_program = glCreateProgram(); + GL_CHECK(); + // Link vertex and fragment shaders + glAttachShader(m_framebuffer_gl_shader_program, vertex_shader); + GL_CHECK(); + glAttachShader(m_framebuffer_gl_shader_program, fragment_shader); + GL_CHECK(); + glBindAttribLocation(m_framebuffer_gl_shader_program, ATTR_POSITION, "Position"); + GL_CHECK(); + glBindAttribLocation(m_framebuffer_gl_shader_program, ATTR_TEXCOORD, "Texcoord"); + GL_CHECK(); + glBindFragDataLocation(m_framebuffer_gl_shader_program, FRAG_COLOR, "Color"); + GL_CHECK(); + glLinkProgram(m_framebuffer_gl_shader_program); + GL_CHECK(); + + /* Check it linked */ + GLint linked = 0; + glGetProgramiv(m_framebuffer_gl_shader_program, GL_LINK_STATUS, &linked); + GL_CHECK(); + if (!linked) { + GLchar log[2048]; + glGetProgramInfoLog(m_framebuffer_gl_shader_program, 2048, NULL, log); + fprintf(stderr, "nv2a: shader linking failed: %s\n", log); + abort(); + } + + m_framebuffer_gl_uniform_location_texture = glGetUniformLocation(m_framebuffer_gl_shader_program, "tex"); + GL_CHECK(); + assert(m_framebuffer_gl_uniform_location_texture >= 0); + } + + return m_framebuffer_gl_shader_program; +} + +static int display_mode_format = 0; +static int frame_pixel_bytes = 1; + +static GLenum frame_gl_internal_format = GL_RGBA8; +static GLenum frame_gl_format = GL_BGRA; +static GLenum frame_gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; +static GLuint frame_gl_texture = 0; + +static GLsizei frame_width = 640; +static GLsizei frame_height = 480; + +void cxbx_gl_update_displaymode(NV2AState *d) { + static GLenum old_frame_gl_internal_format = GL_RGBA8; + static GLenum old_frame_gl_format = GL_BGRA; + static GLenum old_frame_gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + static GLsizei old_frame_width = 640; + static GLsizei old_frame_height = 480; + + // Derive display mode and bytes per pixel from actual hardware register contents: + // This is required for titles that use a non ARGB framebuffer, such as Beats of Rage + bool alt_mode = d->pramdac.regs[NV_PRAMDAC_GENERAL_CONTROL & (NV_PRAMDAC_SIZE - 1)] + & NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; + switch (d->prmcio.cr[NV_CIO_CRE_PIXEL_INDEX] & 0x03) { + case 1: // 8bpp + assert(false); // TODO : Verify this + frame_pixel_bytes = 1; + break; + case 2: // 15 or 16 bpp + if (alt_mode) { + // Test case : Arctic Thunder + display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5; + } + else { + display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5; + } + + frame_pixel_bytes = 2; + break; + case 0: // VGA; Fall through + case 3: // 24 or 32 bpp + if (alt_mode) { + // Test-case : WWE RAW2 + display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8; + } + else { + // Test-case : XDK sample DolphinClassic (after VGA fall-through) + display_mode_format = NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8; + } + + frame_pixel_bytes = 4; + break; + } + + // Convert displau format to OpenGl format details + frame_gl_internal_format = kelvin_color_format_map[display_mode_format].gl_internal_format; + frame_gl_format = kelvin_color_format_map[display_mode_format].gl_format; + frame_gl_type = kelvin_color_format_map[display_mode_format].gl_type; + + frame_width = NV2ADevice::GetFrameWidth(d); + frame_height = NV2ADevice::GetFrameHeight(d); + + // Detect changes in framebuffer dimensions + if (old_frame_gl_internal_format != frame_gl_internal_format + || old_frame_gl_format != frame_gl_format + || old_frame_gl_type != frame_gl_type + || old_frame_width != frame_width + || old_frame_height != frame_height) { + old_frame_gl_internal_format = frame_gl_internal_format; + old_frame_gl_format = frame_gl_format; + old_frame_gl_type = frame_gl_type; + old_frame_width = frame_width; + old_frame_height = frame_height; + if (frame_gl_texture) { + // Destroy the texture if format changed + // Test case : XDK sample DolphinClassic + glDeleteTextures(1, &frame_gl_texture); + frame_gl_texture = 0; + } + } +} + +void cxbx_gl_render_framebuffer(NV2AState *d) +{ + // Update the frame texture + uint8_t* frame_pixels = (uint8_t*)(/*CONTIGUOUS_MEMORY_BASE=*/0x80000000 | d->pcrtc.start); // NV_PCRTC_START + uint8_t* palette_data = xbnullptr; // Note : Framebuffer formats aren't paletized + + TextureShape s; + s.cubemap = false; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path + s.dimensionality = 2; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path + s.color_format = display_mode_format; + s.levels = 1; + s.width = frame_width; + s.height = frame_height; + s.depth = 0; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path + s.min_mipmap_level = 0; + s.max_mipmap_level = 0; + s.pitch = 0; // Note : Unused in upload_gl_texture GL_TEXTURE_2D path + + // If we need to create a (new) texture, do so + if (!frame_gl_texture) { + glGenTextures(1, &frame_gl_texture); + GL_CHECK(); + glBindTexture(GL_TEXTURE_2D, frame_gl_texture); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GL_CHECK(); + glTexImage2D(GL_TEXTURE_2D, /*level=*/0, frame_gl_internal_format, frame_width, frame_height, /*border=*/0, frame_gl_format, frame_gl_type, NULL); + GL_CHECK(); + } + else { + glBindTexture(GL_TEXTURE_2D, frame_gl_texture); + GL_CHECK(); + } + + int rf = upload_gl_texture(GL_TEXTURE_2D, + s, + frame_pixels, + palette_data); + GL_CHECK(); + + // Note : It'd be less code to use generate_texture(), except that puts linear formats + // into GL_TEXTURE_RECTANGLE, while we need GL_TEXTURE_2D here. So instead, handle the + // difference here by separately setting the resulting format's RGBA swizzle: + ColorFormatInfo cfi = kelvin_color_format_map[rf]; + if (cfi.gl_swizzle_mask) { + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, cfi.gl_swizzle_mask); + } + +#define BLIT_FRAMEBUFFER +#ifdef BLIT_FRAMEBUFFER + // If we need to create an OpenGL framebuffer, do so + static GLuint framebuffer = -1; + if (framebuffer == -1) { + glGenFramebuffers(1, &framebuffer); + GL_CHECK(); + } + + // Target the actual framebuffer + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +#endif + +#ifdef DEBUG_NV2A_GL + // If the screen turns purple, glDrawArrays/glBlitFramebuffer below failed + glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + GL_CHECK(); + glClear(GL_COLOR_BUFFER_BIT); + GL_CHECK(); +#endif + +#ifdef BLIT_FRAMEBUFFER + // Copy frame texture to an internal frame buffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frame_gl_texture, /*level=*/0); + // Blit the active internal 'read' frame buffer to the actual 'draw' framebuffer + static const GLenum filter = GL_NEAREST; + // Note : dstY0 and dstY1 are swapped so the screen doesn't appear upside down + glBlitFramebuffer(0, 0, frame_width, frame_height, 0, frame_height, frame_width, 0, GL_COLOR_BUFFER_BIT, filter); + // Detach internal framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +#else + // Draw frame texture to host frame buffer + glUseProgram(GetFramebufferShaderProgram()); + GL_CHECK(); + glUniform1i(m_framebuffer_gl_uniform_location_texture, SAMP_TEXCOORD); + GL_CHECK(); + + static const GLfloat vertices[] = { + // x y s t + -1.0f, -1.0f, 0.0f, 1.0f, // BL + 1.0f, -1.0f, 1.0f, 1.0f, // BR + 1.0f, 1.0f, 1.0f, 0.0f, // TR + -1.0f, 1.0f, 0.0f, 0.0f, // TL + }; + + // Populate vertex buffer + static GLuint m_framebuffer_gl_vertex_buffer_object = -1; + if (m_framebuffer_gl_vertex_buffer_object == -1) { + glGenBuffers(1, &m_framebuffer_gl_vertex_buffer_object); + GL_CHECK(); + } + + glBindBuffer(GL_ARRAY_BUFFER, m_framebuffer_gl_vertex_buffer_object); + GL_CHECK(); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); + GL_CHECK(); + // Bind vertex position attribute + glVertexAttribPointer( + /*index=*/ATTR_POSITION, + /*size=vec*/2, + /*type=*/GL_FLOAT, + /*normalized?=*/GL_FALSE, + /*stride=*/4 * sizeof(GLfloat), + /*array buffer offset=*/(void*)0 + ); + GL_CHECK(); + glEnableVertexAttribArray(ATTR_POSITION); + GL_CHECK(); + // Bind vertex texture coordinate attribute + glVertexAttribPointer( + /*index=*/ATTR_TEXCOORD, + /*size=vec*/2, + /*type=*/GL_FLOAT, + /*normalized?=*/GL_FALSE, + /*stride=*/4 * sizeof(GLfloat), + /*array buffer offset=*/(void*)(2 * sizeof(GLfloat)) + ); + GL_CHECK(); + glEnableVertexAttribArray(ATTR_TEXCOORD); + GL_CHECK(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + GL_CHECK(); +#endif +} + +void pvideo_init(NV2AState *d) +{ + //qemu_cond_init(&d->pvideo.interrupt_cond); +} + +void pvideo_destroy(NV2AState *d) +{ + if (d->pvideo.overlays[0].gl_texture) { + glDeleteTextures(1, &d->pvideo.overlays[0].gl_texture); + d->pvideo.overlays[0].gl_texture = 0; + } + + if (d->pvideo.overlays[1].gl_texture) { + glDeleteTextures(1, &d->pvideo.overlays[1].gl_texture); + d->pvideo.overlays[1].gl_texture = 0; + } + + //qemu_cond_destroy(&d->pvideo.interrupt_cond); +} + +void cxbx_gl_parse_overlay(NV2AState *d, int v) +{ + OverlayState &overlay = d->pvideo.overlays[v]; + + uint32_t video_buffer_use = (v == 0) ? NV_PVIDEO_BUFFER_0_USE : NV_PVIDEO_BUFFER_1_USE; + overlay.video_buffer_use = d->pvideo.regs[NV_PVIDEO_BUFFER] & video_buffer_use; + + // Get overlay measures (from xqemu nv2a_overlay_draw_line) : + uint32_t overlay_offset_high_26 = d->pvideo.regs[NV_PVIDEO_OFFSET(v)]; + uint32_t overlay_offset_lower_6 = d->pvideo.regs[NV_PVIDEO_POINT_IN(v)] >> 3; + uint32_t overlay_size_in = d->pvideo.regs[NV_PVIDEO_SIZE_IN(v)]; + uint32_t overlay_color_key = d->pvideo.regs[NV_PVIDEO_COLOR_KEY(v)]; + uint32_t overlay_format = d->pvideo.regs[NV_PVIDEO_FORMAT(v)]; + +#ifdef DEBUG + // Check a few assumptions + overlay.base = d->pvideo.regs[NV_PVIDEO_BASE(v)]; + overlay.limit = d->pvideo.regs[NV_PVIDEO_LIMIT(v)]; + assert(overlay.base == 0); + assert(overlay.limit == (128 * ONE_MB) - 1); // = CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1 + assert(GET_MASK(overlay_format, NV_PVIDEO_FORMAT_COLOR) == NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8); + +#endif + // Derive actual attributes + overlay.pitch = overlay_format & NV_PVIDEO_FORMAT_PITCH; + overlay.is_transparent = overlay_format & NV_PVIDEO_FORMAT_DISPLAY; + overlay.offset = overlay_offset_high_26 | overlay_offset_lower_6; + + uint32_t overlay_size_in_height_width = overlay_size_in - (overlay_offset_lower_6 >> 1); + overlay.in_height = overlay_size_in_height_width >> 16; + overlay.in_width = overlay_size_in_height_width & 0xFFFF; + + overlay.out_x = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT(v)], NV_PVIDEO_POINT_OUT_X); + overlay.out_y = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT(v)], NV_PVIDEO_POINT_OUT_Y); + overlay.out_width = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT(v)], NV_PVIDEO_SIZE_OUT_WIDTH); + overlay.out_height = GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT(v)], NV_PVIDEO_SIZE_OUT_HEIGHT); + + // Detect changes in overlay dimensions + if (overlay.old_in_width != overlay.in_width + || overlay.old_in_height != overlay.in_height + || overlay.old_pitch != overlay.pitch) { + overlay.old_in_width = overlay.in_width; + overlay.old_in_height = overlay.in_height; + overlay.old_pitch = overlay.pitch; + if (overlay.gl_texture) { + glDeleteTextures(1, &overlay.gl_texture); + overlay.gl_texture = 0; + } + } + + overlay.covers_framebuffer = overlay.video_buffer_use + && (!overlay.is_transparent) + && (overlay.out_x == 0) + && (overlay.out_y == 0) + && (overlay.out_width == frame_width) + && (overlay.out_height == frame_height); +} + +void cxbx_gl_render_overlays(NV2AState *d) +{ + static const GLenum overlay_gl_internal_format = GL_RGBA8; + static const GLenum overlay_gl_format = GL_BGRA; + static const GLenum overlay_gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + + for (int v = 0; v < 2; v++) { + OverlayState &overlay = d->pvideo.overlays[v]; + if (!overlay.video_buffer_use) { + continue; + } + + // TODO : Speed this up using 2 PixelBufferObjects (and use asynchronous DMA transfer)? + + // If we need to create a (new) overlay texture, do so + if (!overlay.gl_texture) { + glGenTextures(1, &overlay.gl_texture); + GL_CHECK(); + glBindTexture(GL_TEXTURE_2D, overlay.gl_texture); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + GL_CHECK(); + // Don't average YUYV samples when resizing + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GL_CHECK(); + // Note : YUYV formats are sampled using BGRA in OPENGL_SHADER_YUV[1] fragment shader + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask_BGRA); + GL_CHECK(); + glTexImage2D(GL_TEXTURE_2D, /*level=*/0, overlay_gl_internal_format, overlay.pitch / 4, overlay.in_height, /*border=*/0, overlay_gl_format, overlay_gl_type, NULL); + GL_CHECK(); + } + else { + glBindTexture(GL_TEXTURE_2D, overlay.gl_texture); + GL_CHECK(); + } + + // Update the YUV video texture + hwaddr overlay_pixels = /*CONTIGUOUS_MEMORY_BASE=*/0x80000000 | overlay.offset; + glTexSubImage2D(GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, /*yoffset=*/0, overlay.pitch / 4, overlay.in_height, overlay_gl_format, overlay_gl_type, (void*)overlay_pixels); + GL_CHECK(); + + // Note : we cannot convert overlay_offset into actual top/left coordinate, so assume (0,0) + static const int overlay_in_s = 0; + static const int overlay_in_t = 0; + + // Determine source and destination coordinates, with that draw the overlay over the framebuffer + GLint srcX0 = overlay_in_s; + GLint srcY0 = overlay_in_t; + GLint srcX1 = overlay.in_width; + GLint srcY1 = overlay.in_height; + GLint dstX0 = overlay.out_x; + GLint dstY0 = overlay.out_y; + GLint dstX1 = overlay.out_width; + GLint dstY1 = overlay.out_height; + + // Detect some special cases, for later finetuning + if (overlay_in_s != 0 || overlay_in_t != 0 || !overlay.covers_framebuffer) { + LOG_TEST_CASE("Non-standard overlay dimensions"); + } + + // Convert UV coordinates to [0.0, 1.0] + GLfloat srcX0f = (GLfloat)srcX0 / overlay.in_width; + GLfloat srcX1f = (GLfloat)srcX1 / overlay.in_width; + GLfloat srcY0f = (GLfloat)srcY0 / overlay.in_height; + GLfloat srcY1f = (GLfloat)srcY1 / overlay.in_height; + + // Convert screen coordinates to [-1.0, 1.0] + GLfloat dstX0f = (GLfloat)((dstX0 / frame_width) * 2.0f) - 1.0f; + GLfloat dstX1f = (GLfloat)((dstX1 / frame_width) * 2.0f) - 1.0f; + GLfloat dstY0f = (GLfloat)((dstY0 / frame_height) * 2.0f) - 1.0f; + GLfloat dstY1f = (GLfloat)((dstY1 / frame_height) * 2.0f) - 1.0f; + + glUseProgram(Get_YUV_to_RGB_shader_program()); + GL_CHECK(); + + // Attach texture #0 to the shader sampler location + glUniform1i(m_overlay_gl_uniform_location_texture, SAMP_TEXCOORD); + GL_CHECK(); + + // Flip Y to prevent upside down rendering + std::swap(srcY0f, srcY1f); + + // Feed screen and texture coordinates through a vertex buffer object + const GLfloat overlay_vertex_buffer_data[] = { + dstX0f, dstY0f, srcX0f, srcY0f, + dstX1f, dstY0f, srcX1f, srcY0f, + dstX1f, dstY1f, srcX1f, srcY1f, + dstX0f, dstY1f, srcX0f, srcY1f, + }; + + static GLuint overlay_gl_vertex_buffer_object = -1; + if (overlay_gl_vertex_buffer_object == -1) { + glGenBuffers(1, &overlay_gl_vertex_buffer_object); + GL_CHECK(); + } + + glBindBuffer(GL_ARRAY_BUFFER, overlay_gl_vertex_buffer_object); + GL_CHECK(); + glBufferData(GL_ARRAY_BUFFER, sizeof(overlay_vertex_buffer_data), overlay_vertex_buffer_data, GL_DYNAMIC_DRAW); + GL_CHECK(); + // Bind vertex position attribute + glVertexAttribPointer( + /*index=*/ATTR_POSITION, + /*size=vec*/2, + /*type=*/GL_FLOAT, + /*normalized?=*/GL_FALSE, + /*stride=*/4 * sizeof(GLfloat), + /*array buffer offset=*/(void*)0 + ); + GL_CHECK(); + glEnableVertexAttribArray(ATTR_POSITION); + GL_CHECK(); + // Bind vertex texture coordinate attribute + glVertexAttribPointer( + /*index=*/ATTR_TEXCOORD, + /*size=vec*/2, + /*type=*/GL_FLOAT, + /*normalized?=*/GL_FALSE, + /*stride=*/4 * sizeof(GLfloat), + /*array buffer offset=*/(void*)(2 * sizeof(GLfloat)) + ); + GL_CHECK(); + glEnableVertexAttribArray(ATTR_TEXCOORD); + GL_CHECK(); + // Finally! Draw the dang overlay... + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + GL_CHECK(); + } +} + +extern void UpdateFPSCounter(); +void NV2ADevice::UpdateHostDisplay(NV2AState *d) +{ + PGRAPHState *pg = &d->pgraph; + if (!pg->opengl_enabled) { + return; + } + + NV2A_GL_DGROUP_BEGIN("VGA Frame"); + + glo_set_current(pg->gl_context); + + cxbx_gl_update_displaymode(d); + + for (int v = 0; v < 2; v++) { + cxbx_gl_parse_overlay(d, v); + } + + GL_RESET(); + + // Target the host framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); // NOTE : If disabled, overlays don't show?! + GL_CHECK(); + glDisable(GL_CULL_FACE); + GL_CHECK(); + // Render using texture #0 + glActiveTexture(GL_TEXTURE0); + GL_CHECK(); + + // Is either overlay fullscreen ? + if (d->pvideo.overlays[0].covers_framebuffer + || d->pvideo.overlays[1].covers_framebuffer) { + // Then the framebuffer won't be visible anyway, so doesn't have to be rendered + } else { + cxbx_gl_render_framebuffer(d); + } + + cxbx_gl_render_overlays(d); + + // Unbind everything we've used + glUseProgram(0); + GL_CHECK(); + glBindBuffer(GL_ARRAY_BUFFER, 0);//pg->gl_memory_buffer); + GL_CHECK(); + glBindTexture(GL_TEXTURE_2D, 0);//pg->gl_color_buffer); + GL_CHECK(); + // Restore xbox framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, pg->gl_framebuffer); + GL_CHECK(); + + glo_swap(pg->gl_context); + + NV2A_GL_DGROUP_END(); + +// glo_set_current(NULL); + + UpdateFPSCounter(); +} + +// TODO: Fix this properly +static void nv2a_vblank_thread(NV2AState *d) +{ + SetThreadAffinityMask(GetCurrentThread(), g_CPUOthers); + CxbxSetThreadName("Cxbx NV2A VBLANK"); + auto nextVBlankTime = GetNextVBlankTime(); + + while (!d->exiting) { + // Handle VBlank + if (std::chrono::steady_clock::now() > nextVBlankTime) { + d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK; + update_irq(d); + nextVBlankTime = GetNextVBlankTime(); + + // TODO: We should swap here for the purposes of supporting overlays + direct framebuffer access + // But it causes crashes on AMD hardware for reasons currently unknown... + //NV2ADevice::UpdateHostDisplay(d); + } + + Sleep(1); + } +} + +// See NV2ABlockInfo regions[] PRAMIN +#define NV_PRAMIN_ADDR 0x00700000 +#define NV_PRAMIN_SIZE 0x100000 + +void CxbxReserveNV2AMemory(NV2AState *d) +{ + // The NV2A memory was reserved already by the loader! + + printf("[0x%.4X] INIT: Reserved %d MiB of Xbox NV2A memory at 0x%.8X to 0x%.8X\n", + GetCurrentThreadId(), NV2A_SIZE / ONE_MB, NV2A_ADDR, NV2A_ADDR + NV2A_SIZE - 1); + + // Allocate PRAMIN Region (the loader only reserved this region, it still needs to be committed!) + // We are looping here because memory-reservation happens in 64 KiB increments + d->pramin.ramin_size = NV_PRAMIN_SIZE; + d->pramin.ramin_ptr = (uint8_t*)(NV2A_ADDR + NV_PRAMIN_ADDR); + for (int i = 0; i < 16; i++) { + LPVOID ret = VirtualAlloc((LPVOID)(NV2A_ADDR + NV_PRAMIN_ADDR + i * 64 * ONE_KB), 64 * ONE_KB, MEM_COMMIT, PAGE_READWRITE); + if (ret != (LPVOID)(NV2A_ADDR + NV_PRAMIN_ADDR + i * 64 * ONE_KB)) { + CxbxKrnlCleanup("VirtualAlloc failed to commit the memory for the nv2a pramin. The error was 0x%08X", GetLastError()); + } + } + + printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8p to 0x%.8p\n", + GetCurrentThreadId(), d->pramin.ramin_size / ONE_MB, (uintptr_t)d->pramin.ramin_ptr, (uintptr_t)d->pramin.ramin_ptr + d->pramin.ramin_size - 1); +} + +/* NV2ADevice */ + +NV2ADevice::NV2ADevice() +{ + m_nv2a_state = new NV2AState(); + m_nv2a_state->pgraph.opengl_enabled = bLLE_GPU; +} + +NV2ADevice::~NV2ADevice() +{ + Reset(); // TODO : Review this + delete m_nv2a_state; +} + +// PCI Device functions + +void NV2ADevice::Init() +{ + PCIBarRegister r; + + // Register Memory bar : + r.Raw.type = PCI_BAR_TYPE_MEMORY; + r.Memory.address = NV2A_ADDR >> 4; + RegisterBAR(0, NV2A_SIZE, r.value); + + // Register physical memory on bar 1 + r.Memory.address = 0xF0000000 >> 4; + RegisterBAR(1, g_SystemMaxMemory, r.value); + + m_DeviceId = 0x02A5; + m_VendorId = PCI_VENDOR_ID_NVIDIA; + + NV2AState *d = m_nv2a_state; // glue + + CxbxReserveNV2AMemory(d); + + d->pcrtc.start = 0; + + d->vram_ptr = (uint8_t*)PHYSICAL_MAP_BASE; + d->vram_size = g_SystemMaxMemory; + + d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */ + d->pramdac.core_clock_freq = 189000000; + d->pramdac.memory_clock_coeff = 0; + d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */ + + // Setup the conditions/mutexes + pgraph_init(d); + + // Only init PVIDEO when LLE is enabled + if (d->pgraph.opengl_enabled) { + pvideo_init(d); + } + + vblank_thread = std::thread(nv2a_vblank_thread, d); + + qemu_mutex_init(&d->pfifo.pfifo_lock); + qemu_cond_init(&d->pfifo.puller_cond); + qemu_cond_init(&d->pfifo.pusher_cond); + + d->pfifo.regs[NV_PFIFO_CACHE1_STATUS] |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; + + /* fire up puller */ + d->pfifo.puller_thread = std::thread(pfifo_puller_thread, d); + /* fire up pusher */ + d->pfifo.pusher_thread = std::thread(pfifo_pusher_thread, d); +} + +void NV2ADevice::Reset() +{ + NV2AState *d = m_nv2a_state; // glue + if (!d) return; + + d->exiting = true; + + qemu_cond_broadcast(&d->pfifo.puller_cond); + qemu_cond_broadcast(&d->pfifo.pusher_cond); + d->pfifo.puller_thread.join(); + d->pfifo.pusher_thread.join(); + qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cbxbx addition + if (d->pgraph.opengl_enabled) { + vblank_thread.join(); + pvideo_destroy(d); + } + + pgraph_destroy(&d->pgraph); +} + +uint32_t NV2ADevice::IORead(int barIndex, uint32_t port, unsigned size) +{ + return 0; +} + +void NV2ADevice::IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) +{ +} + +uint32_t NV2ADevice::BlockRead(const NV2ABlockInfo* block, uint32_t addr, unsigned size) +{ + switch (size) { + case sizeof(uint8_t) : + return block->ops.read(m_nv2a_state, addr - block->offset) & 0xFF; + case sizeof(uint16_t) : + assert((addr & 1) == 0); // TODO : What if this fails? + + return block->ops.read(m_nv2a_state, addr - block->offset) & 0xFFFF; + case sizeof(uint32_t) : + assert((addr & 3) == 0); // TODO : What if this fails? + + return block->ops.read(m_nv2a_state, addr - block->offset); + default: + assert(false); + + return 0; + } +} + +uint32_t NV2ADevice::MMIORead(int barIndex, uint32_t addr, unsigned size) +{ + switch (barIndex) { + case 0: { + // Access NV2A regardless weither HLE is disabled or not (ignoring bLLE_GPU) + const NV2ABlockInfo* block = EmuNV2A_Block(addr); + if (block != nullptr) { + return BlockRead(block, addr, size); + } + break; + } + case 1: { + // TODO : access physical memory + break; + } + } + + EmuLog(LOG_LEVEL::WARNING, "NV2ADevice::MMIORead: Unhandled barIndex %d, addr %08X, size %d", barIndex, addr, size); + return 0; +} + +void NV2ADevice::BlockWrite(const NV2ABlockInfo* block, uint32_t addr, uint32_t value, unsigned size) +{ + switch (size) { + case sizeof(uint8_t) : { +#if 0 + xbaddr aligned_addr; + uint32_t aligned_value; + int shift; + uint32_t mask; + + aligned_addr = addr & ~3; + aligned_value = block->ops.read(m_nv2a_state, aligned_addr - block->offset); + shift = (addr & 3) * 8; + mask = 0xFF << shift; + block->ops.write(m_nv2a_state, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); +#else + block->ops.write(m_nv2a_state, addr - block->offset, value); +#endif + return; + } + case sizeof(uint16_t) : { + assert((addr & 1) == 0); // TODO : What if this fails? + + xbaddr aligned_addr; + uint32_t aligned_value; + int shift; + uint32_t mask; + + aligned_addr = addr & ~3; + aligned_value = block->ops.read(m_nv2a_state, aligned_addr - block->offset); + shift = (addr & 2) * 16; + mask = 0xFFFF << shift; + block->ops.write(m_nv2a_state, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); + return; + } + case sizeof(uint32_t) : + assert((addr & 3) == 0); // TODO : What if this fails? + + block->ops.write(m_nv2a_state, addr - block->offset, value); + return; + } +} + +void NV2ADevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) +{ + switch (barIndex) { + case 0: { + // Access NV2A regardless whether HLE is disabled or not (ignoring bLLE_GPU) + const NV2ABlockInfo* block = EmuNV2A_Block(addr); + + if (block != nullptr) { + BlockWrite(block, addr, value, size); + return; + } + + break; + } + case 1: { + // TODO : access physical memory + break; + } + } + + EmuLog(LOG_LEVEL::WARNING, "NV2ADevice::MMIOWrite: Unhandled barIndex %d, addr %08X, value %08X, size %d", barIndex, addr, value, size); +} + +int NV2ADevice::GetFrameHeight(NV2AState* d) +{ + // Derive frame_height from hardware registers + int height = ((int)d->prmcio.cr[NV_CIO_CR_VDE_INDEX]) + | (((int)d->prmcio.cr[NV_CIO_CR_OVL_INDEX] & 0x02) >> 1 << 8) + | (((int)d->prmcio.cr[NV_CIO_CR_OVL_INDEX] & 0x40) >> 6 << 9) + | (((int)d->prmcio.cr[NV_CIO_CRE_LSR_INDEX] & 0x02) >> 1 << 10); + + return height++; +} + +int NV2ADevice::GetFrameWidth(NV2AState* d) +{ + // Test case : Arctic Thunder, sets a 16 bit framebuffer (R5G6B5) not via + // AvSetDisplayMode(), but via VGA control register writes, which implies + // that it's format argument cannot be used to determine the framebuffer + // width. Instead, read the framebuffer width from the VGA control registers : + int width = ((int)d->prmcio.cr[NV_CIO_CR_OFFSET_INDEX]) + | (0x700 & ((int)d->prmcio.cr[NV_CIO_CRE_RPC0_INDEX] << 3)) + | (0x800 & ((int)d->prmcio.cr[NV_CIO_CRE_LSR_INDEX] << 6)); + width *= 8; + width /= frame_pixel_bytes; + + return width; +} diff --git a/src/devices/video/nv2a.h b/src/devices/video/nv2a.h index 8551e3466..07719e287 100644 --- a/src/devices/video/nv2a.h +++ b/src/devices/video/nv2a.h @@ -1,113 +1,113 @@ -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2016-2018 Luke Usher -// * (c) 2018 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** -#pragma once - -#include "devices\PCIDevice.h" // For PCIDevice - -#include "nv2a_int.h" // For NV2AState - -// NV2A Push buffer command masks -#define PUSH_TYPE_MASK 0x00000002 // 2 bits -#define PUSH_TYPE_SHIFT 0 -#define PUSH_TYPE_METHOD 0 // method -#define PUSH_TYPE_JMP_FAR 1 // jump far -#define PUSH_TYPE_CALL_FAR 2 // call far -#define PUSH_TYPE_METHOD_UNUSED 3 // method (unused) -#define PUSH_METHOD_MASK 0x00001FFC // 12 bits -#define PUSH_METHOD_SHIFT 0 // Dxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4) -#define PUSH_SUBCH_MASK 0x0000E000 // 3 bits -#define PUSH_SUBCH_SHIFT 13 -#define PUSH_COUNT_MASK 0x1FFC0000 // 11 bits -#define PUSH_COUNT_SHIFT 18 -#define PUSH_INSTR_MASK 0xE0000000 // 3 bits -#define PUSH_INSTR_SHIFT 29 -#define PUSH_INSTR_IMM_INCR 0 // immediate, increment -#define PUSH_INSTR_JMP_NEAR 1 // near jump -#define PUSH_INSTR_IMM_NOINC 2 // immediate, no-increment -#define PUSH_ADDR_FAR_MASK 0xFFFFFFFC // 30 bits -#define PUSH_ADDR_FAR_SHIFT 0 -#define PUSH_ADDR_NEAR_MASK 0x1FFFFFFC // 27 bits -#define PUSH_ADDR_NEAR_SHIFT 0 // Cxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4) - -#define PUSH_TYPE(dwPushCommand) ((dwPushCommand & PUSH_TYPE_MASK) >> PUSH_TYPE_SHIFT) -#define PUSH_METHOD(dwPushCommand) ((dwPushCommand & PUSH_METHOD_MASK) >> PUSH_METHOD_SHIFT) -#define PUSH_SUBCH(dwPushCommand) ((dwPushCommand & PUSH_SUBCH_MASK) >> PUSH_SUBCH_SHIFT) -#define PUSH_COUNT(dwPushCommand) ((dwPushCommand & PUSH_COUNT_MASK) >> PUSH_COUNT_SHIFT) -#define PUSH_INSTR(dwPushCommand) ((dwPushCommand & PUSH_INSTR_MASK) >> PUSH_INSTR_SHIFT) -#define PUSH_ADDR_FAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_FAR_MASK) >> PUSH_ADDR_FAR_SHIFT) -#define PUSH_ADDR_NEAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_NEAR_MASK) >> PUSH_ADDR_NEAR_SHIFT) - -#define PUSH_METHOD_MAX ((PUSH_METHOD_MASK | 3) >> PUSH_METHOD_SHIFT) // = 8191 -#define PUSH_SUBCH_MAX (PUSH_SUBCH_MASK >> PUSH_SUBCH_SHIFT) // = 7 -#define PUSH_COUNT_MAX (PUSH_COUNT_MASK >> PUSH_COUNT_SHIFT) // = 2047 - -// Decode push buffer command (inverse of D3DPUSH_ENCODE) -inline void D3DPUSH_DECODE(const DWORD dwPushCommand, DWORD & dwMethod, DWORD & dwSubCh, DWORD & dwCount) -{ - dwMethod = PUSH_METHOD(dwPushCommand); - dwSubCh = PUSH_SUBCH(dwPushCommand); - dwCount = PUSH_COUNT(dwPushCommand); -} - -typedef struct NV2ABlockInfo { - const char* name; - hwaddr offset; - uint64_t size; - MemoryRegionOps ops; -} NV2ABlockInfo; - -const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr); - -void CxbxReserveNV2AMemory(NV2AState *d); - -class NV2ADevice : public PCIDevice { -public: - // constructor - NV2ADevice(); - // destructor - ~NV2ADevice(); - - // PCI Device functions - void Init(); - void Reset(); - - // State Getter: Used for HLE reading of device state - NV2AState* GetDeviceState() { return m_nv2a_state; }; - - uint32_t IORead(int barIndex, uint32_t port, unsigned size); - void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size); - uint32_t BlockRead(const NV2ABlockInfo* block, uint32_t addr, unsigned size); - uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size); - void BlockWrite(const NV2ABlockInfo* block, uint32_t addr, uint32_t value, unsigned size); - void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size); - - static void UpdateHostDisplay(NV2AState *d); - - static int GetFrameWidth(NV2AState *d); - static int GetFrameHeight(NV2AState *d); -private: - NV2AState *m_nv2a_state; -}; +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2016-2018 Luke Usher +// * (c) 2018 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** +#pragma once + +#include "devices\PCIDevice.h" // For PCIDevice + +#include "nv2a_int.h" // For NV2AState + +// NV2A Push buffer command masks +#define PUSH_TYPE_MASK 0x00000002 // 2 bits +#define PUSH_TYPE_SHIFT 0 +#define PUSH_TYPE_METHOD 0 // method +#define PUSH_TYPE_JMP_FAR 1 // jump far +#define PUSH_TYPE_CALL_FAR 2 // call far +#define PUSH_TYPE_METHOD_UNUSED 3 // method (unused) +#define PUSH_METHOD_MASK 0x00001FFC // 12 bits +#define PUSH_METHOD_SHIFT 0 // Dxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4) +#define PUSH_SUBCH_MASK 0x0000E000 // 3 bits +#define PUSH_SUBCH_SHIFT 13 +#define PUSH_COUNT_MASK 0x1FFC0000 // 11 bits +#define PUSH_COUNT_SHIFT 18 +#define PUSH_INSTR_MASK 0xE0000000 // 3 bits +#define PUSH_INSTR_SHIFT 29 +#define PUSH_INSTR_IMM_INCR 0 // immediate, increment +#define PUSH_INSTR_JMP_NEAR 1 // near jump +#define PUSH_INSTR_IMM_NOINC 2 // immediate, no-increment +#define PUSH_ADDR_FAR_MASK 0xFFFFFFFC // 30 bits +#define PUSH_ADDR_FAR_SHIFT 0 +#define PUSH_ADDR_NEAR_MASK 0x1FFFFFFC // 27 bits +#define PUSH_ADDR_NEAR_SHIFT 0 // Cxbx note : Not 2, because methods are actually DWORD offsets (and thus defined with increments of 4) + +#define PUSH_TYPE(dwPushCommand) ((dwPushCommand & PUSH_TYPE_MASK) >> PUSH_TYPE_SHIFT) +#define PUSH_METHOD(dwPushCommand) ((dwPushCommand & PUSH_METHOD_MASK) >> PUSH_METHOD_SHIFT) +#define PUSH_SUBCH(dwPushCommand) ((dwPushCommand & PUSH_SUBCH_MASK) >> PUSH_SUBCH_SHIFT) +#define PUSH_COUNT(dwPushCommand) ((dwPushCommand & PUSH_COUNT_MASK) >> PUSH_COUNT_SHIFT) +#define PUSH_INSTR(dwPushCommand) ((dwPushCommand & PUSH_INSTR_MASK) >> PUSH_INSTR_SHIFT) +#define PUSH_ADDR_FAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_FAR_MASK) >> PUSH_ADDR_FAR_SHIFT) +#define PUSH_ADDR_NEAR(dwPushCommand) ((dwPushCommand & PUSH_ADDR_NEAR_MASK) >> PUSH_ADDR_NEAR_SHIFT) + +#define PUSH_METHOD_MAX ((PUSH_METHOD_MASK | 3) >> PUSH_METHOD_SHIFT) // = 8191 +#define PUSH_SUBCH_MAX (PUSH_SUBCH_MASK >> PUSH_SUBCH_SHIFT) // = 7 +#define PUSH_COUNT_MAX (PUSH_COUNT_MASK >> PUSH_COUNT_SHIFT) // = 2047 + +// Decode push buffer command (inverse of D3DPUSH_ENCODE) +inline void D3DPUSH_DECODE(const DWORD dwPushCommand, DWORD & dwMethod, DWORD & dwSubCh, DWORD & dwCount) +{ + dwMethod = PUSH_METHOD(dwPushCommand); + dwSubCh = PUSH_SUBCH(dwPushCommand); + dwCount = PUSH_COUNT(dwPushCommand); +} + +typedef struct NV2ABlockInfo { + const char* name; + hwaddr offset; + uint64_t size; + MemoryRegionOps ops; +} NV2ABlockInfo; + +const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr); + +void CxbxReserveNV2AMemory(NV2AState *d); + +class NV2ADevice : public PCIDevice { +public: + // constructor + NV2ADevice(); + // destructor + ~NV2ADevice(); + + // PCI Device functions + void Init(); + void Reset(); + + // State Getter: Used for HLE reading of device state + NV2AState* GetDeviceState() { return m_nv2a_state; }; + + uint32_t IORead(int barIndex, uint32_t port, unsigned size); + void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size); + uint32_t BlockRead(const NV2ABlockInfo* block, uint32_t addr, unsigned size); + uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size); + void BlockWrite(const NV2ABlockInfo* block, uint32_t addr, uint32_t value, unsigned size); + void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size); + + static void UpdateHostDisplay(NV2AState *d); + + static int GetFrameWidth(NV2AState *d); + static int GetFrameHeight(NV2AState *d); +private: + NV2AState *m_nv2a_state; +}; diff --git a/src/devices/video/nv2a_int.h b/src/devices/video/nv2a_int.h index 41dbdf5cc..27b337ae8 100644 --- a/src/devices/video/nv2a_int.h +++ b/src/devices/video/nv2a_int.h @@ -1,467 +1,467 @@ -// Source : https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a_int.h -/* - * QEMU Geforce NV2A internal definitions - * - * Copyright (c) 2012 espes - * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018 Matt Borgerson - * - * 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) version 3 of the License. - * - * 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, see . - */ - -#ifndef HW_NV2A_INT_H -#define HW_NV2A_INT_H - -#undef USE_SHADER_CACHE - -#include -#include -#include - -#include "Cxbx.h" // For xbaddr - -#include "qemu-thread.h" // For qemu_mutex, etc - -#ifdef USE_SHADER_CACHE -#include "glib_compat.h" // For GHashTable, g_hash_table_new, g_hash_table_lookup, g_hash_table_insert -#endif -#include "common\util\gloffscreen\gloffscreen.h" // For GloContext, etc - -#include "swizzle.h" - -#include "nv2a_debug.h" // For HWADDR_PRIx, NV2A_DPRINTF, NV2A_GL_DPRINTF, etc. -#include "nv2a_shaders.h" // For ShaderBinding, etc -#include "nv2a_regs.h" // For NV2A_MAX_TEXTURES, etc - - -typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr -typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t) - -#define NV_PMC_SIZE 0x001000 -#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE -#define NV_PVIDEO_SIZE 0x001000 -#define NV_PTIMER_SIZE 0x001000 -#define NV_PFB_SIZE 0x001000 -#define NV_PGRAPH_SIZE 0x002000 -#define NV_PCRTC_SIZE 0x001000 -#define NV_PRAMDAC_SIZE 0x001000 - -#define VSH_TOKEN_SIZE 4 // Compatibility; TODO : Move this to nv2a_vsh.h -#define MAX(a,b) ((a)>(b) ? (a) : (b)) // Compatibility -#define MIN(a,b) ((a)<(b) ? (a) : (b)) // Compatibility - -#define g_free(x) free(x) // Compatibility -#define g_malloc(x) malloc(x) // Compatibility -#define g_malloc0(x) calloc(1, x) // Compatibility -#define g_realloc(x, y) realloc(x, y) // Compatibility - -#undef USE_TEXTURE_CACHE - -#if __cplusplus >= 201402L -# define NV2A_CONSTEXPR constexpr -#else -# define NV2A_CONSTEXPR static -#endif - -// GCC implementation of FFS -static int ffs(int valu) -{ - int bit; - - if (valu == 0) - return 0; - - for (bit = 1; !(valu & 1); bit++) - valu >>= 1; - - return bit; -} - -#define GET_MASK(v, mask) (((v) & (mask)) >> (ffs(mask)-1)) - -#define SET_MASK(v, mask, val) \ - do { \ - const unsigned int __val = (val); \ - const unsigned int __mask = (mask); \ - (v) &= ~(__mask); \ - (v) |= ((__val) << (ffs(__mask) - 1)) & (__mask); \ - } while (0) - -// Power-of-two CASE statements -#define CASE_1(v, step) case (v) -#define CASE_2(v, step) CASE_1(v, step) : CASE_1(v + (step) * 1, step) -#define CASE_4(v, step) CASE_2(v, step) : CASE_2(v + (step) * 2, step) -#define CASE_8(v, step) CASE_4(v, step) : CASE_4(v + (step) * 4, step) -#define CASE_16(v, step) CASE_8(v, step) : CASE_8(v + (step) * 8, step) -#define CASE_32(v, step) CASE_16(v, step) : CASE_16(v + (step) * 16, step) -#define CASE_64(v, step) CASE_32(v, step) : CASE_32(v + (step) * 32, step) -#define CASE_128(v, step) CASE_64(v, step) : CASE_64(v + (step) * 64, step) -#define CASE_256(v, step) CASE_128(v, step) : CASE_128(v + (step) * 128, step) - -// Non-power-of-two CASE statements -#define CASE_3(v, step) CASE_2(v, step) : CASE_1(v + (step) * 2, step) - -#define NV2A_DEVICE(obj) \ - OBJECT_CHECK(NV2AState, (obj), "nv2a") - -//void reg_log_read(int block, hwaddr addr, uint64_t val); -//void reg_log_write(int block, hwaddr addr, uint64_t val); - -enum FIFOEngine { - ENGINE_SOFTWARE = 0, - ENGINE_GRAPHICS = 1, - ENGINE_DVD = 2, -}; - -typedef struct DMAObject { - unsigned int dma_class; - unsigned int dma_target; - xbaddr address; - xbaddr limit; -} DMAObject; - -typedef struct VertexAttribute { - bool dma_select; - xbaddr offset; - - /* inline arrays are packed in order? - * Need to pass the offset to converted attributes */ - unsigned int inline_array_offset; - - float inline_value[4]; - - unsigned int format; - unsigned int size; /* size of the data type */ - unsigned int count; /* number of components */ - uint32_t stride; - - bool needs_conversion; - uint8_t *converted_buffer; - unsigned int converted_elements; - unsigned int converted_size; - unsigned int converted_count; - - float *inline_buffer; - - GLint gl_count; - GLenum gl_type; - GLboolean gl_normalize; - - GLuint gl_converted_buffer; - GLuint gl_inline_buffer; -} VertexAttribute; - -typedef struct Surface { - bool draw_dirty; - bool buffer_dirty; - bool write_enabled_cache; - unsigned int pitch; - - xbaddr offset; -} Surface; - -typedef struct SurfaceShape { - unsigned int z_format; - unsigned int color_format; - unsigned int zeta_format; - unsigned int log_width, log_height; - unsigned int clip_x, clip_y; - unsigned int clip_width, clip_height; - unsigned int anti_aliasing; -} SurfaceShape; - -typedef struct TextureShape { - bool cubemap; - unsigned int dimensionality; - unsigned int color_format; - unsigned int levels; - unsigned int width, height, depth; - - unsigned int min_mipmap_level, max_mipmap_level; - unsigned int pitch; -} TextureShape; - -typedef struct TextureKey { - TextureShape state; - uint64_t data_hash; - uint8_t* texture_data; - uint8_t* palette_data; -} TextureKey; - -typedef struct TextureBinding { - GLenum gl_target; - GLuint gl_texture; - unsigned int refcnt; -} TextureBinding; - -typedef struct KelvinState { - xbaddr object_instance; -} KelvinState; - -typedef struct ContextSurfaces2DState { - xbaddr object_instance; - xbaddr dma_image_source; - xbaddr dma_image_dest; - unsigned int color_format; - unsigned int source_pitch, dest_pitch; - xbaddr source_offset, dest_offset; -} ContextSurfaces2DState; - -typedef struct ImageBlitState { - xbaddr object_instance; - xbaddr context_surfaces; - unsigned int operation; - unsigned int in_x, in_y; - unsigned int out_x, out_y; - unsigned int width, height; -} ImageBlitState; - -typedef struct PGRAPHState { - bool opengl_enabled; // == bLLE_GPU - QemuMutex pgraph_lock; - - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - QemuCond interrupt_cond; - - /* subchannels state we're not sure the location of... */ - ContextSurfaces2DState context_surfaces_2d; - ImageBlitState image_blit; - KelvinState kelvin; - - QemuCond fifo_access_cond; - QemuCond flip_3d; - - xbaddr dma_color, dma_zeta; - Surface surface_color, surface_zeta; - unsigned int surface_type; - SurfaceShape surface_shape; - SurfaceShape last_surface_shape; - - xbaddr dma_a, dma_b; -#ifdef USE_TEXTURE_CACHE - GLruCache *texture_cache; -#endif - bool texture_dirty[NV2A_MAX_TEXTURES]; - TextureBinding *texture_binding[NV2A_MAX_TEXTURES]; - -#ifdef USE_SHADER_CACHE - GHashTable *shader_cache; -#endif - ShaderBinding *shader_binding; - - bool texture_matrix_enable[NV2A_MAX_TEXTURES]; - - /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ - float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */ - - GloContext *gl_context; - GLuint gl_framebuffer; - GLuint gl_color_buffer, gl_zeta_buffer; - - xbaddr dma_state; - xbaddr dma_notifies; - xbaddr dma_semaphore; - - xbaddr dma_report; - xbaddr report_offset; - bool zpass_pixel_count_enable; - unsigned int zpass_pixel_count_result; - unsigned int gl_zpass_pixel_count_query_count; - GLuint* gl_zpass_pixel_count_queries; - - xbaddr dma_vertex_a, dma_vertex_b; - - unsigned int primitive_mode; - - unsigned int clear_surface; - - bool enable_vertex_program_write; - - uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; - - uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4]; - bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS]; - - /* lighting constant arrays */ - uint32_t ltctxa[NV2A_LTCTXA_COUNT][4]; - bool ltctxa_dirty[NV2A_LTCTXA_COUNT]; - uint32_t ltctxb[NV2A_LTCTXB_COUNT][4]; - bool ltctxb_dirty[NV2A_LTCTXB_COUNT]; - uint32_t ltc1[NV2A_LTC1_COUNT][4]; - bool ltc1_dirty[NV2A_LTC1_COUNT]; - - // should figure out where these are in lighting context - float light_infinite_half_vector[NV2A_MAX_LIGHTS][3]; - float light_infinite_direction[NV2A_MAX_LIGHTS][3]; - float light_local_position[NV2A_MAX_LIGHTS][3]; - float light_local_attenuation[NV2A_MAX_LIGHTS][3]; - - VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES]; - - unsigned int inline_array_length; - uint32_t inline_array[NV2A_MAX_BATCH_LENGTH]; - GLuint gl_inline_array_buffer; - - unsigned int inline_elements_length; - uint16_t inline_elements[NV2A_MAX_BATCH_LENGTH]; // Cxbx-Reloaded TODO : Restore uint32_t once HLE_draw_inline_elements can using that - - unsigned int inline_buffer_length; - - unsigned int draw_arrays_length; - unsigned int draw_arrays_max_count; - - /* FIXME: Unknown size, possibly endless, 1000 will do for now */ - GLint gl_draw_arrays_start[1000]; - GLsizei gl_draw_arrays_count[1000]; - - GLuint gl_element_buffer; - GLuint gl_memory_buffer; - GLuint gl_vertex_array; - - uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union -} PGRAPHState; - -typedef struct OverlayState { - bool video_buffer_use; - int pitch; - bool is_transparent; -#ifdef DEBUG - hwaddr base; - hwaddr limit; -#endif - hwaddr offset; - uint32_t in_height; - uint32_t in_width; - int out_x; - int out_y; - int out_width; - int out_height; - - bool covers_framebuffer; - int old_in_width; - int old_in_height; - int old_pitch; - GLuint gl_texture; -} OverlayState; - -typedef struct NV2AState { - // PCIDevice dev; - // qemu_irq irq; - bool exiting; - bool enable_overlay = false; - - // VGACommonState vga; - // GraphicHwOps hw_ops; - // QEMUTimer *vblank_timer; - - // MemoryRegion *vram; - // MemoryRegion vram_pci; - uint8_t *vram_ptr; - size_t vram_size; - // MemoryRegion ramin; - struct { - uint8_t *ramin_ptr; - size_t ramin_size; - } pramin; - - // MemoryRegion mmio; - // MemoryRegion block_mmio[NV_NUM_BLOCKS]; - - struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - uint32_t regs[NV_PMC_SIZE]; // Not in xqemu/openxbox? TODO : union - } pmc; - - struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union - QemuMutex pfifo_lock; - std::thread puller_thread; - QemuCond puller_cond; - std::thread pusher_thread; - QemuCond pusher_cond; - } pfifo; - - struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - //QemuCond interrupt_cond; // pvideo.interrupt_cond not used (yet) - OverlayState overlays[2]; // NV2A supports 2 video overlays - uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union - } pvideo; - - struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - uint32_t numerator; - uint32_t denominator; - uint32_t alarm_time; - uint32_t regs[NV_PTIMER_SIZE]; // Not in xqemu/openxbox? TODO : union - } ptimer; - - struct { - uint32_t regs[NV_PFB_SIZE]; // TODO : union - } pfb; - - struct PGRAPHState pgraph; - - struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - hwaddr start; - uint32_t regs[NV_PCRTC_SIZE]; // Not in xqemu/openxbox? TODO : union - } pcrtc; - - struct { - uint32_t core_clock_coeff; - uint64_t core_clock_freq; - uint32_t memory_clock_coeff; - uint32_t video_clock_coeff; - uint32_t regs[NV_PRAMDAC_SIZE]; // Not in xqemu/openxbox? TODO : union - } pramdac; - - // PRMCIO (Actually the VGA controller) - struct { - uint8_t cr_index; - uint8_t cr[256]; /* CRT registers */ - } prmcio; // Not in xqemu/openxbox? -} NV2AState; - -typedef value_t(*read_func)(NV2AState *d, hwaddr addr); //, unsigned int size); -typedef void(*write_func)(NV2AState *d, hwaddr addr, value_t val); //, unsigned int size); - -typedef struct { - read_func read; - write_func write; -} MemoryRegionOps; - -#if 0 -// Valid after PCI init : -#define NV20_REG_BASE_KERNEL 0xFD000000 - -typedef volatile DWORD *PPUSH; - -typedef struct { - DWORD Ignored[0x10]; - PPUSH Put; // On Xbox1, this field is only written to by the CPU (the GPU uses this as a trigger to start executing from the given address) - PPUSH Get; // On Xbox1, this field is only read from by the CPU (the GPU reflects in here where it is/stopped executing) - PPUSH Reference; // TODO : xbaddr / void* / DWORD ? - DWORD Ignored2[0x7ED]; -} Nv2AControlDma; -#endif - -#endif +// Source : https://github.com/xqemu/xqemu/blob/master/hw/xbox/nv2a_int.h +/* + * QEMU Geforce NV2A internal definitions + * + * Copyright (c) 2012 espes + * Copyright (c) 2015 Jannik Vogel + * Copyright (c) 2018 Matt Borgerson + * + * 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) version 3 of the License. + * + * 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, see . + */ + +#ifndef HW_NV2A_INT_H +#define HW_NV2A_INT_H + +#undef USE_SHADER_CACHE + +#include +#include +#include + +#include "Cxbx.h" // For xbaddr + +#include "qemu-thread.h" // For qemu_mutex, etc + +#ifdef USE_SHADER_CACHE +#include "glib_compat.h" // For GHashTable, g_hash_table_new, g_hash_table_lookup, g_hash_table_insert +#endif +#include "common\util\gloffscreen\gloffscreen.h" // For GloContext, etc + +#include "swizzle.h" + +#include "nv2a_debug.h" // For HWADDR_PRIx, NV2A_DPRINTF, NV2A_GL_DPRINTF, etc. +#include "nv2a_shaders.h" // For ShaderBinding, etc +#include "nv2a_regs.h" // For NV2A_MAX_TEXTURES, etc + + +typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr +typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t) + +#define NV_PMC_SIZE 0x001000 +#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE +#define NV_PVIDEO_SIZE 0x001000 +#define NV_PTIMER_SIZE 0x001000 +#define NV_PFB_SIZE 0x001000 +#define NV_PGRAPH_SIZE 0x002000 +#define NV_PCRTC_SIZE 0x001000 +#define NV_PRAMDAC_SIZE 0x001000 + +#define VSH_TOKEN_SIZE 4 // Compatibility; TODO : Move this to nv2a_vsh.h +#define MAX(a,b) ((a)>(b) ? (a) : (b)) // Compatibility +#define MIN(a,b) ((a)<(b) ? (a) : (b)) // Compatibility + +#define g_free(x) free(x) // Compatibility +#define g_malloc(x) malloc(x) // Compatibility +#define g_malloc0(x) calloc(1, x) // Compatibility +#define g_realloc(x, y) realloc(x, y) // Compatibility + +#undef USE_TEXTURE_CACHE + +#if __cplusplus >= 201402L +# define NV2A_CONSTEXPR constexpr +#else +# define NV2A_CONSTEXPR static +#endif + +// GCC implementation of FFS +static int ffs(int valu) +{ + int bit; + + if (valu == 0) + return 0; + + for (bit = 1; !(valu & 1); bit++) + valu >>= 1; + + return bit; +} + +#define GET_MASK(v, mask) (((v) & (mask)) >> (ffs(mask)-1)) + +#define SET_MASK(v, mask, val) \ + do { \ + const unsigned int __val = (val); \ + const unsigned int __mask = (mask); \ + (v) &= ~(__mask); \ + (v) |= ((__val) << (ffs(__mask) - 1)) & (__mask); \ + } while (0) + +// Power-of-two CASE statements +#define CASE_1(v, step) case (v) +#define CASE_2(v, step) CASE_1(v, step) : CASE_1(v + (step) * 1, step) +#define CASE_4(v, step) CASE_2(v, step) : CASE_2(v + (step) * 2, step) +#define CASE_8(v, step) CASE_4(v, step) : CASE_4(v + (step) * 4, step) +#define CASE_16(v, step) CASE_8(v, step) : CASE_8(v + (step) * 8, step) +#define CASE_32(v, step) CASE_16(v, step) : CASE_16(v + (step) * 16, step) +#define CASE_64(v, step) CASE_32(v, step) : CASE_32(v + (step) * 32, step) +#define CASE_128(v, step) CASE_64(v, step) : CASE_64(v + (step) * 64, step) +#define CASE_256(v, step) CASE_128(v, step) : CASE_128(v + (step) * 128, step) + +// Non-power-of-two CASE statements +#define CASE_3(v, step) CASE_2(v, step) : CASE_1(v + (step) * 2, step) + +#define NV2A_DEVICE(obj) \ + OBJECT_CHECK(NV2AState, (obj), "nv2a") + +//void reg_log_read(int block, hwaddr addr, uint64_t val); +//void reg_log_write(int block, hwaddr addr, uint64_t val); + +enum FIFOEngine { + ENGINE_SOFTWARE = 0, + ENGINE_GRAPHICS = 1, + ENGINE_DVD = 2, +}; + +typedef struct DMAObject { + unsigned int dma_class; + unsigned int dma_target; + xbaddr address; + xbaddr limit; +} DMAObject; + +typedef struct VertexAttribute { + bool dma_select; + xbaddr offset; + + /* inline arrays are packed in order? + * Need to pass the offset to converted attributes */ + unsigned int inline_array_offset; + + float inline_value[4]; + + unsigned int format; + unsigned int size; /* size of the data type */ + unsigned int count; /* number of components */ + uint32_t stride; + + bool needs_conversion; + uint8_t *converted_buffer; + unsigned int converted_elements; + unsigned int converted_size; + unsigned int converted_count; + + float *inline_buffer; + + GLint gl_count; + GLenum gl_type; + GLboolean gl_normalize; + + GLuint gl_converted_buffer; + GLuint gl_inline_buffer; +} VertexAttribute; + +typedef struct Surface { + bool draw_dirty; + bool buffer_dirty; + bool write_enabled_cache; + unsigned int pitch; + + xbaddr offset; +} Surface; + +typedef struct SurfaceShape { + unsigned int z_format; + unsigned int color_format; + unsigned int zeta_format; + unsigned int log_width, log_height; + unsigned int clip_x, clip_y; + unsigned int clip_width, clip_height; + unsigned int anti_aliasing; +} SurfaceShape; + +typedef struct TextureShape { + bool cubemap; + unsigned int dimensionality; + unsigned int color_format; + unsigned int levels; + unsigned int width, height, depth; + + unsigned int min_mipmap_level, max_mipmap_level; + unsigned int pitch; +} TextureShape; + +typedef struct TextureKey { + TextureShape state; + uint64_t data_hash; + uint8_t* texture_data; + uint8_t* palette_data; +} TextureKey; + +typedef struct TextureBinding { + GLenum gl_target; + GLuint gl_texture; + unsigned int refcnt; +} TextureBinding; + +typedef struct KelvinState { + xbaddr object_instance; +} KelvinState; + +typedef struct ContextSurfaces2DState { + xbaddr object_instance; + xbaddr dma_image_source; + xbaddr dma_image_dest; + unsigned int color_format; + unsigned int source_pitch, dest_pitch; + xbaddr source_offset, dest_offset; +} ContextSurfaces2DState; + +typedef struct ImageBlitState { + xbaddr object_instance; + xbaddr context_surfaces; + unsigned int operation; + unsigned int in_x, in_y; + unsigned int out_x, out_y; + unsigned int width, height; +} ImageBlitState; + +typedef struct PGRAPHState { + bool opengl_enabled; // == bLLE_GPU + QemuMutex pgraph_lock; + + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + QemuCond interrupt_cond; + + /* subchannels state we're not sure the location of... */ + ContextSurfaces2DState context_surfaces_2d; + ImageBlitState image_blit; + KelvinState kelvin; + + QemuCond fifo_access_cond; + QemuCond flip_3d; + + xbaddr dma_color, dma_zeta; + Surface surface_color, surface_zeta; + unsigned int surface_type; + SurfaceShape surface_shape; + SurfaceShape last_surface_shape; + + xbaddr dma_a, dma_b; +#ifdef USE_TEXTURE_CACHE + GLruCache *texture_cache; +#endif + bool texture_dirty[NV2A_MAX_TEXTURES]; + TextureBinding *texture_binding[NV2A_MAX_TEXTURES]; + +#ifdef USE_SHADER_CACHE + GHashTable *shader_cache; +#endif + ShaderBinding *shader_binding; + + bool texture_matrix_enable[NV2A_MAX_TEXTURES]; + + /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ + float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */ + + GloContext *gl_context; + GLuint gl_framebuffer; + GLuint gl_color_buffer, gl_zeta_buffer; + + xbaddr dma_state; + xbaddr dma_notifies; + xbaddr dma_semaphore; + + xbaddr dma_report; + xbaddr report_offset; + bool zpass_pixel_count_enable; + unsigned int zpass_pixel_count_result; + unsigned int gl_zpass_pixel_count_query_count; + GLuint* gl_zpass_pixel_count_queries; + + xbaddr dma_vertex_a, dma_vertex_b; + + unsigned int primitive_mode; + + unsigned int clear_surface; + + bool enable_vertex_program_write; + + uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; + + uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4]; + bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS]; + + /* lighting constant arrays */ + uint32_t ltctxa[NV2A_LTCTXA_COUNT][4]; + bool ltctxa_dirty[NV2A_LTCTXA_COUNT]; + uint32_t ltctxb[NV2A_LTCTXB_COUNT][4]; + bool ltctxb_dirty[NV2A_LTCTXB_COUNT]; + uint32_t ltc1[NV2A_LTC1_COUNT][4]; + bool ltc1_dirty[NV2A_LTC1_COUNT]; + + // should figure out where these are in lighting context + float light_infinite_half_vector[NV2A_MAX_LIGHTS][3]; + float light_infinite_direction[NV2A_MAX_LIGHTS][3]; + float light_local_position[NV2A_MAX_LIGHTS][3]; + float light_local_attenuation[NV2A_MAX_LIGHTS][3]; + + VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES]; + + unsigned int inline_array_length; + uint32_t inline_array[NV2A_MAX_BATCH_LENGTH]; + GLuint gl_inline_array_buffer; + + unsigned int inline_elements_length; + uint16_t inline_elements[NV2A_MAX_BATCH_LENGTH]; // Cxbx-Reloaded TODO : Restore uint32_t once HLE_draw_inline_elements can using that + + unsigned int inline_buffer_length; + + unsigned int draw_arrays_length; + unsigned int draw_arrays_max_count; + + /* FIXME: Unknown size, possibly endless, 1000 will do for now */ + GLint gl_draw_arrays_start[1000]; + GLsizei gl_draw_arrays_count[1000]; + + GLuint gl_element_buffer; + GLuint gl_memory_buffer; + GLuint gl_vertex_array; + + uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union +} PGRAPHState; + +typedef struct OverlayState { + bool video_buffer_use; + int pitch; + bool is_transparent; +#ifdef DEBUG + hwaddr base; + hwaddr limit; +#endif + hwaddr offset; + uint32_t in_height; + uint32_t in_width; + int out_x; + int out_y; + int out_width; + int out_height; + + bool covers_framebuffer; + int old_in_width; + int old_in_height; + int old_pitch; + GLuint gl_texture; +} OverlayState; + +typedef struct NV2AState { + // PCIDevice dev; + // qemu_irq irq; + bool exiting; + bool enable_overlay = false; + + // VGACommonState vga; + // GraphicHwOps hw_ops; + // QEMUTimer *vblank_timer; + + // MemoryRegion *vram; + // MemoryRegion vram_pci; + uint8_t *vram_ptr; + size_t vram_size; + // MemoryRegion ramin; + struct { + uint8_t *ramin_ptr; + size_t ramin_size; + } pramin; + + // MemoryRegion mmio; + // MemoryRegion block_mmio[NV_NUM_BLOCKS]; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + uint32_t regs[NV_PMC_SIZE]; // Not in xqemu/openxbox? TODO : union + } pmc; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union + QemuMutex pfifo_lock; + std::thread puller_thread; + QemuCond puller_cond; + std::thread pusher_thread; + QemuCond pusher_cond; + } pfifo; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + //QemuCond interrupt_cond; // pvideo.interrupt_cond not used (yet) + OverlayState overlays[2]; // NV2A supports 2 video overlays + uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union + } pvideo; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + uint32_t numerator; + uint32_t denominator; + uint32_t alarm_time; + uint32_t regs[NV_PTIMER_SIZE]; // Not in xqemu/openxbox? TODO : union + } ptimer; + + struct { + uint32_t regs[NV_PFB_SIZE]; // TODO : union + } pfb; + + struct PGRAPHState pgraph; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + hwaddr start; + uint32_t regs[NV_PCRTC_SIZE]; // Not in xqemu/openxbox? TODO : union + } pcrtc; + + struct { + uint32_t core_clock_coeff; + uint64_t core_clock_freq; + uint32_t memory_clock_coeff; + uint32_t video_clock_coeff; + uint32_t regs[NV_PRAMDAC_SIZE]; // Not in xqemu/openxbox? TODO : union + } pramdac; + + // PRMCIO (Actually the VGA controller) + struct { + uint8_t cr_index; + uint8_t cr[256]; /* CRT registers */ + } prmcio; // Not in xqemu/openxbox? +} NV2AState; + +typedef value_t(*read_func)(NV2AState *d, hwaddr addr); //, unsigned int size); +typedef void(*write_func)(NV2AState *d, hwaddr addr, value_t val); //, unsigned int size); + +typedef struct { + read_func read; + write_func write; +} MemoryRegionOps; + +#if 0 +// Valid after PCI init : +#define NV20_REG_BASE_KERNEL 0xFD000000 + +typedef volatile DWORD *PPUSH; + +typedef struct { + DWORD Ignored[0x10]; + PPUSH Put; // On Xbox1, this field is only written to by the CPU (the GPU uses this as a trigger to start executing from the given address) + PPUSH Get; // On Xbox1, this field is only read from by the CPU (the GPU reflects in here where it is/stopped executing) + PPUSH Reference; // TODO : xbaddr / void* / DWORD ? + DWORD Ignored2[0x7ED]; +} Nv2AControlDma; +#endif + +#endif diff --git a/src/devices/video/qstring.h b/src/devices/video/qstring.h index ccf01bd2d..c8c3f1079 100644 --- a/src/devices/video/qstring.h +++ b/src/devices/video/qstring.h @@ -8,23 +8,23 @@ typedef std::string QString; static QString* qstring_from_fmt(std::string fmt, ...) { - int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code - std::string *str = new std::string(""); - va_list ap; - while (1) { // Maximum two passes on a POSIX system... - str->resize(size); - va_start(ap, fmt); - int n = vsnprintf((char *)str->data(), size, fmt.c_str(), ap); - va_end(ap); - if (n > -1 && n < size) { // Everything worked - str->resize(n); - return str; - } - if (n > -1) // Needed size returned - size = n + 1; // For null char - else - size *= 2; // Guess at a larger size (OS specific) - } + int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code + std::string *str = new std::string(""); + va_list ap; + while (1) { // Maximum two passes on a POSIX system... + str->resize(size); + va_start(ap, fmt); + int n = vsnprintf((char *)str->data(), size, fmt.c_str(), ap); + va_end(ap); + if (n > -1 && n < size) { // Everything worked + str->resize(n); + return str; + } + if (n > -1) // Needed size returned + size = n + 1; // For null char + else + size *= 2; // Guess at a larger size (OS specific) + } return str; } diff --git a/src/devices/x86/EmuX86.cpp b/src/devices/x86/EmuX86.cpp index f054a9c08..287f3cc3e 100644 --- a/src/devices/x86/EmuX86.cpp +++ b/src/devices/x86/EmuX86.cpp @@ -474,8 +474,8 @@ bool EmuX86_Operand_Addr_ForReadOnly(const LPEXCEPTION_POINTERS e, const _DInst& return true; } case O_DISP: // memory dereference with displacement only, instruction.disp. - { - // Disabled as software is expected to hit this situaton, eg: + { + // Disabled as software is expected to hit this situaton, eg: // TEST byte ptr DS:[FEF00098h],1 //assert(opAddr.size == sizeof(uint32_t)); @@ -2926,9 +2926,9 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e) EmuLog(LOG_LEVEL::DEBUG, "Starting instruction emulation from 0x%08X", e->ContextRecord->Eip); // Execute op-codes until we hit an unhandled instruction, or an error occurs - //while (true) - // TODO: Find where the weird memory addresses come from when using the above case - // There is obviously something wrong with one or more of our instruction implementations + //while (true) + // TODO: Find where the weird memory addresses come from when using the above case + // There is obviously something wrong with one or more of our instruction implementations // For now, we only execute one instruction at a time... for (int x=0;x<1;x++) { @@ -3131,8 +3131,8 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e) goto opcode_error; case I_NEG: if (EmuX86_Opcode_NEG(e, info)) break; - goto opcode_error; - case I_NOP: + goto opcode_error; + case I_NOP: break; case I_NOT: if (EmuX86_Opcode_NOT(e, info)) break; @@ -3281,9 +3281,9 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e) if (EmuX86_Opcode_XOR(e, info)) break; goto opcode_error; default: - EmuLog(LOG_LEVEL::DEBUG, "Unhandled instruction : %s (%u)", Distorm_OpcodeString(info.opcode), info.opcode); + EmuLog(LOG_LEVEL::DEBUG, "Unhandled instruction : %s (%u)", Distorm_OpcodeString(info.opcode), info.opcode); // HACK: If we hit an unhandled instruction, log and skip it - e->ContextRecord->Eip += info.size; + e->ContextRecord->Eip += info.size; return true; } // switch info.opcode diff --git a/src/emulator/cxbxr-emu.cpp b/src/emulator/cxbxr-emu.cpp index 70c68a631..88c59c363 100644 --- a/src/emulator/cxbxr-emu.cpp +++ b/src/emulator/cxbxr-emu.cpp @@ -1,194 +1,194 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of Cxbx-Reloaded. -// * -// * Cxbx-Reloaded is free software; you can redistribute it -// * and/or modify it under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2017-2019 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -// cxbxr-emu.cpp : Defines the exported functions for the DLL application. - -#define LOG_PREFIX CXBXR_MODULE::CXBXR - -#include "Cxbx.h" // For FUNC_EXPORTS -#include "VerifyAddressRanges.h" // For VerifyBaseAddr() -//#include "CxbxKrnl/Emu.h" -#include "EmuShared.h" -#include "core\kernel\init\CxbxKrnl.h" // For HandleFirstLaunch() and LaunchEmulation() -//#include -#include "common/util/cliConverter.hpp" -#include "common/util/cliConfig.hpp" - -PCHAR* -CommandLineToArgvA( - PCHAR CmdLine, - int* _argc -) -{ - PCHAR* argv; - PCHAR _argv; - ULONG len; - ULONG argc; - CHAR a; - ULONG i, j; - - BOOLEAN in_QM; - BOOLEAN in_TEXT; - BOOLEAN in_SPACE; - - len = strlen(CmdLine); - i = ((len + 2) / 2) * sizeof(PVOID) + sizeof(PVOID); - - argv = (PCHAR*)LocalAlloc(GMEM_FIXED, - i + (len + 2) * sizeof(CHAR)); - - _argv = (PCHAR)(((PUCHAR)argv) + i); - - argc = 0; - argv[argc] = _argv; - in_QM = FALSE; - in_TEXT = FALSE; - in_SPACE = TRUE; - i = 0; - j = 0; - - while (a = CmdLine[i]) { - if (in_QM) { - if (a == '\"') { - in_QM = FALSE; - } - else { - _argv[j] = a; - j++; - } - } - else { - switch (a) { - case '\"': - in_QM = TRUE; - in_TEXT = TRUE; - if (in_SPACE) { - argv[argc] = _argv + j; - argc++; - } - in_SPACE = FALSE; - break; - case ' ': - case '\t': - case '\n': - case '\r': - if (in_TEXT) { - _argv[j] = '\0'; - j++; - } - in_TEXT = FALSE; - in_SPACE = TRUE; - break; - default: - in_TEXT = TRUE; - if (in_SPACE) { - argv[argc] = _argv + j; - argc++; - } - _argv[j] = a; - j++; - in_SPACE = FALSE; - break; - } - } - i++; - } - _argv[j] = '\0'; - argv[argc] = NULL; - - (*_argc) = argc; - return argv; -} - -DWORD WINAPI Emulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved) -{ - FUNC_EXPORTS - - /*! Verify our host executable, cxbxr-ldr.exe, is loaded to base address 0x00010000 */ - if (!VerifyBaseAddr()) { - PopupError(nullptr, "cxbx-ldr.exe was not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)"); - return EXIT_FAILURE; - } - - LPSTR CommandLine = GetCommandLine(); - if (!CommandLine) { - PopupError(nullptr, "Couldn't retrieve command line!"); - return EXIT_FAILURE; - } - - int argc = 0; - PCHAR *argv = CommandLineToArgvA(CommandLine, &argc); - if (!argv) { - PopupError(nullptr, "Couldn't parse command line!"); - return EXIT_FAILURE; - } - - if (!cli_config::GenConfig(argv, argc)) { - PopupError(nullptr, "Couldn't convert parsed command line!"); - LocalFree(argv); - return EXIT_FAILURE; - } - LocalFree(argv); - - /*! verify load argument is included */ - if (!cli_config::hasKey("load")) { - PopupError(nullptr, "No /load argument in command line!"); - return EXIT_FAILURE; - } - - /*! initialize shared memory */ - if (!EmuShared::Init(cli_config::GetSessionID())) { - PopupError(nullptr, "Could not map shared memory!"); - return EXIT_FAILURE; - } - - if (!HandleFirstLaunch()) { - PopupError(nullptr, "First launch failed!"); - EmuShared::Cleanup(); - return EXIT_FAILURE; - } - - if (!reserved_systems) { - PopupError(nullptr, "Unable to preserve any system's memory ranges!"); - EmuShared::Cleanup(); - return EXIT_FAILURE; - } - - CxbxKrnlEmulate(reserved_systems, blocks_reserved); - - /*! cleanup shared memory */ - EmuShared::Cleanup(); - - // Note : Emulate() must never return to it's caller (rawMain() in loader.cpp), - // because that function resides in a block of memory that's overwritten with - // xbox executable contents. Returning there would lead to undefined behaviour. - // Since we're done emulating, it's al right to terminate here : - TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); - - // This line will never be reached: - return EXIT_FAILURE; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of Cxbx-Reloaded. +// * +// * Cxbx-Reloaded is free software; you can redistribute it +// * and/or modify it under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2017-2019 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +// cxbxr-emu.cpp : Defines the exported functions for the DLL application. + +#define LOG_PREFIX CXBXR_MODULE::CXBXR + +#include "Cxbx.h" // For FUNC_EXPORTS +#include "VerifyAddressRanges.h" // For VerifyBaseAddr() +//#include "CxbxKrnl/Emu.h" +#include "EmuShared.h" +#include "core\kernel\init\CxbxKrnl.h" // For HandleFirstLaunch() and LaunchEmulation() +//#include +#include "common/util/cliConverter.hpp" +#include "common/util/cliConfig.hpp" + +PCHAR* +CommandLineToArgvA( + PCHAR CmdLine, + int* _argc +) +{ + PCHAR* argv; + PCHAR _argv; + ULONG len; + ULONG argc; + CHAR a; + ULONG i, j; + + BOOLEAN in_QM; + BOOLEAN in_TEXT; + BOOLEAN in_SPACE; + + len = strlen(CmdLine); + i = ((len + 2) / 2) * sizeof(PVOID) + sizeof(PVOID); + + argv = (PCHAR*)LocalAlloc(GMEM_FIXED, + i + (len + 2) * sizeof(CHAR)); + + _argv = (PCHAR)(((PUCHAR)argv) + i); + + argc = 0; + argv[argc] = _argv; + in_QM = FALSE; + in_TEXT = FALSE; + in_SPACE = TRUE; + i = 0; + j = 0; + + while (a = CmdLine[i]) { + if (in_QM) { + if (a == '\"') { + in_QM = FALSE; + } + else { + _argv[j] = a; + j++; + } + } + else { + switch (a) { + case '\"': + in_QM = TRUE; + in_TEXT = TRUE; + if (in_SPACE) { + argv[argc] = _argv + j; + argc++; + } + in_SPACE = FALSE; + break; + case ' ': + case '\t': + case '\n': + case '\r': + if (in_TEXT) { + _argv[j] = '\0'; + j++; + } + in_TEXT = FALSE; + in_SPACE = TRUE; + break; + default: + in_TEXT = TRUE; + if (in_SPACE) { + argv[argc] = _argv + j; + argc++; + } + _argv[j] = a; + j++; + in_SPACE = FALSE; + break; + } + } + i++; + } + _argv[j] = '\0'; + argv[argc] = NULL; + + (*_argc) = argc; + return argv; +} + +DWORD WINAPI Emulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved) +{ + FUNC_EXPORTS + + /*! Verify our host executable, cxbxr-ldr.exe, is loaded to base address 0x00010000 */ + if (!VerifyBaseAddr()) { + PopupError(nullptr, "cxbx-ldr.exe was not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)"); + return EXIT_FAILURE; + } + + LPSTR CommandLine = GetCommandLine(); + if (!CommandLine) { + PopupError(nullptr, "Couldn't retrieve command line!"); + return EXIT_FAILURE; + } + + int argc = 0; + PCHAR *argv = CommandLineToArgvA(CommandLine, &argc); + if (!argv) { + PopupError(nullptr, "Couldn't parse command line!"); + return EXIT_FAILURE; + } + + if (!cli_config::GenConfig(argv, argc)) { + PopupError(nullptr, "Couldn't convert parsed command line!"); + LocalFree(argv); + return EXIT_FAILURE; + } + LocalFree(argv); + + /*! verify load argument is included */ + if (!cli_config::hasKey("load")) { + PopupError(nullptr, "No /load argument in command line!"); + return EXIT_FAILURE; + } + + /*! initialize shared memory */ + if (!EmuShared::Init(cli_config::GetSessionID())) { + PopupError(nullptr, "Could not map shared memory!"); + return EXIT_FAILURE; + } + + if (!HandleFirstLaunch()) { + PopupError(nullptr, "First launch failed!"); + EmuShared::Cleanup(); + return EXIT_FAILURE; + } + + if (!reserved_systems) { + PopupError(nullptr, "Unable to preserve any system's memory ranges!"); + EmuShared::Cleanup(); + return EXIT_FAILURE; + } + + CxbxKrnlEmulate(reserved_systems, blocks_reserved); + + /*! cleanup shared memory */ + EmuShared::Cleanup(); + + // Note : Emulate() must never return to it's caller (rawMain() in loader.cpp), + // because that function resides in a block of memory that's overwritten with + // xbox executable contents. Returning there would lead to undefined behaviour. + // Since we're done emulating, it's al right to terminate here : + TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); + + // This line will never be reached: + return EXIT_FAILURE; +} diff --git a/src/emulator/dllmain.cpp b/src/emulator/dllmain.cpp index 08d0ffd50..22bc7e48a 100644 --- a/src/emulator/dllmain.cpp +++ b/src/emulator/dllmain.cpp @@ -1,48 +1,48 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of Cxbx-Reloaded. -// * -// * Cxbx-Reloaded is free software; you can redistribute it -// * and/or modify it under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2017-2019 Patrick van Logchem -// * -// * All rights reserved -// * -// ****************************************************************** - -#include "EmuShared.h" // For hActiveModule - -// The entry point for the DLL application. -BOOL APIENTRY DllMain(HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved -) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - hActiveModule = hModule; // For shared code - break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - - return TRUE; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of Cxbx-Reloaded. +// * +// * Cxbx-Reloaded is free software; you can redistribute it +// * and/or modify it under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2017-2019 Patrick van Logchem +// * +// * All rights reserved +// * +// ****************************************************************** + +#include "EmuShared.h" // For hActiveModule + +// The entry point for the DLL application. +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + hActiveModule = hModule; // For shared code + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} diff --git a/src/emulator/targetver.h b/src/emulator/targetver.h index ba864c29a..b8da604f4 100644 --- a/src/emulator/targetver.h +++ b/src/emulator/targetver.h @@ -1,8 +1,8 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -//#include +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +//#include diff --git a/src/gui/DbgConsole.cpp b/src/gui/DbgConsole.cpp index a7d3937d4..07e9a4190 100644 --- a/src/gui/DbgConsole.cpp +++ b/src/gui/DbgConsole.cpp @@ -185,7 +185,7 @@ void DbgConsole::ParseCommand() printf("CxbxDbg: DisableVB [DVB #] : Disable Active Vertex Buffer(s)\n"); printf("CxbxDbg: EnableVB [EVB #] : Enable Active Vertex Buffer(s)\n"); printf("CxbxDbg: DumpStreamCache [DSC] : Dumps the patched streams cache\n"); - } + } #ifdef _DEBUG_ALLOC printf("CxbxDbg: DumpMem [DMEM] : Dump the heap allocation tracking table\n"); @@ -207,7 +207,7 @@ void DbgConsole::ParseCommand() printf("CxbxDbg: Trace is now %s\n", g_bPrintfOn ? "ON" : "OFF"); } else if(_stricmp(szCmd, "lvb") == 0 || _stricmp(szCmd, "ListVB") == 0) - { + { LOG_CHECK_ENABLED_EX(CXBXR_MODULE::VTXB, LOG_LEVEL::DEBUG) { int v = 0; @@ -224,11 +224,11 @@ void DbgConsole::ParseCommand() cur = cur->pNext; } - g_VBTrackTotal.Unlock(); + g_VBTrackTotal.Unlock(); } } else if(_stricmp(szCmd, "dvb") == 0 || _stricmp(szCmd, "DisableVB") == 0) - { + { LOG_CHECK_ENABLED_EX(CXBXR_MODULE::VTXB, LOG_LEVEL::DEBUG) { int n = 0, m = 0; @@ -245,11 +245,11 @@ void DbgConsole::ParseCommand() else { printf("CxbxDbg: Syntax Incorrect (dvb #)\n"); - } + } } } else if(_stricmp(szCmd, "evb") == 0 || _stricmp(szCmd, "EnableVB") == 0) - { + { LOG_CHECK_ENABLED_EX(CXBXR_MODULE::VTXB, LOG_LEVEL::DEBUG) { int n = 0, m = 0; @@ -266,7 +266,7 @@ void DbgConsole::ParseCommand() else { printf("CxbxDbg: Syntax Incorrect (dvb #)\n"); - } + } } } #ifdef _DEBUG_ALLOC diff --git a/src/gui/DlgAbout.cpp b/src/gui/DlgAbout.cpp index cc9363572..a2b29a35a 100644 --- a/src/gui/DlgAbout.cpp +++ b/src/gui/DlgAbout.cpp @@ -1,200 +1,200 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** - -#include "EmuShared.h" - -#include "CxbxVersion.h" -#include "DlgAbout.h" -#include "resource/ResCxbx.h" -#include "common\util\CxbxUtil.h" - -#include -#include -#include - -// Array of tab pane handles -std::vector aboutTabPanes; -// Current (active) tab pane index -unsigned int aboutCurrentTab = 0; - -/*! windows dialog procedure */ -static INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - -VOID ShowAboutDialog(HWND hwnd) -{ - /*! show dialog box */ - DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_ABOUT), hwnd, DlgAboutProc); -} - -INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch(uMsg) - { - case WM_INITDIALOG: - { - // Set the dialog icon - HICON hIcon = (HICON)LoadImageW( - GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDI_CXBX), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE - ); - - SendMessageW(hWndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); - - // Build the Tab Control - TCITEM tabInfo; - memset(&tabInfo, 0, sizeof(tabInfo)); - tabInfo.mask = TCIF_TEXT; - tabInfo.cchTextMax = 20; - tabInfo.pszText = "About"; - SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_INSERTITEM, 0, (LPARAM)&tabInfo); - tabInfo.pszText = "Contributors"; - SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_INSERTITEM, 1, (LPARAM)&tabInfo); - tabInfo.pszText = "License"; - SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_INSERTITEM, 2, (LPARAM)&tabInfo); - - // Get tab pane dimensions - RECT tabRect; - GetClientRect(GetDlgItem(hWndDlg, IDC_TAB1), &tabRect); - SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect); - // Tab Pane 1 - char TabPane1Message[270]; - sprintf(TabPane1Message, "\nCxbx-Reloaded\nVersion %s\n© The Cxbx-Reloaded Team" - "\nThis software comes with ABSOLUTELY NO WARRANTY." - "\nThis is free software, and you are welcome to redistribute it" - "\nunder certain conditions; See our website for details.", CxbxVersionStr); - HWND tab = CreateWindowEx - (NULL, "STATIC", TabPane1Message, - WS_CHILD | WS_VISIBLE, - tabRect.left + 10, tabRect.top + 10, - tabRect.right - tabRect.left, - tabRect.bottom - tabRect.top, - GetDlgItem(hWndDlg, IDC_TAB1), (HMENU)1, - GetModuleHandle(nullptr), nullptr - ); - - SendMessage(tab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); - SetParent(tab, hWndDlg); - aboutTabPanes.push_back(tab); - - // Tab Pane 2 - HRSRC rContributors = FindResource(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_CONTRIBUTORS), "TXT"); - - std::string contributors = "\n"; - contributors += std::string( - (char*)LockResource(LoadResource(GetModuleHandle(nullptr), rContributors)), - SizeofResource(GetModuleHandle(nullptr), rContributors) - ); - - unix2dos(contributors); - - tab = CreateWindowEx( - 0, "EDIT", contributors.c_str(), - WS_CHILD | WS_VSCROLL |ES_MULTILINE | ES_READONLY, - tabRect.left + 10, tabRect.top + 10, - tabRect.right - tabRect.left, - tabRect.bottom - tabRect.top, - GetDlgItem(hWndDlg, IDC_TAB1), (HMENU)1, - GetModuleHandle(nullptr), nullptr - ); - - SendMessage(tab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); - SetParent(tab, hWndDlg); - aboutTabPanes.push_back(tab); - - // Tab Pane 3 - HRSRC rCopying = FindResource(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_COPYING), "TXT"); - - std::string copying = "\n"; - copying += std::string( - (char*)LockResource(LoadResource(GetModuleHandle(nullptr), rCopying)), - SizeofResource(GetModuleHandle(nullptr), rCopying) - ); - - unix2dos(copying); - - tab = CreateWindowEx( - 0, "EDIT", copying.c_str(), - WS_CHILD | WS_VSCROLL | ES_MULTILINE | ES_READONLY, - tabRect.left + 10, tabRect.top + 10, - tabRect.right - tabRect.left, - tabRect.bottom - tabRect.top, - GetDlgItem(hWndDlg, IDC_TAB1), (HMENU)1, - GetModuleHandle(nullptr), nullptr - ); - - SendMessage(tab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); - SetParent(tab, hWndDlg); - aboutTabPanes.push_back(tab); - - aboutCurrentTab = 0; - UpdateWindow(hWndDlg); - - } - break; - - case WM_CLOSE: - { - PostMessage(hWndDlg, WM_COMMAND, IDOK, 0); - } - break; - - case WM_COMMAND: - { - HWND hWndButton = GetDlgItem(hWndDlg, LOWORD(wParam)); - - switch (LOWORD(wParam)) - { - case IDOK: - for (auto it = aboutTabPanes.begin(); it != aboutTabPanes.end(); ++it) { - DestroyWindow(*it); - } - - aboutTabPanes.clear(); - EndDialog(hWndDlg, wParam); - break; - } - } - break; - - case WM_NOTIFY: - { - unsigned int index = SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_GETCURSEL, 0, 0); - - // Do nothing if the tab hasnt been changed, or if we try to load a non existant tab - if (index == aboutCurrentTab || index >= aboutTabPanes.size()) { - break; - } - - // Show the selected tab pane - ShowWindow(aboutTabPanes[aboutCurrentTab], SW_HIDE); - ShowWindow(aboutTabPanes[index], SW_SHOW); - - aboutCurrentTab = index; - } - break; - } - return FALSE; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** + +#include "EmuShared.h" + +#include "CxbxVersion.h" +#include "DlgAbout.h" +#include "resource/ResCxbx.h" +#include "common\util\CxbxUtil.h" + +#include +#include +#include + +// Array of tab pane handles +std::vector aboutTabPanes; +// Current (active) tab pane index +unsigned int aboutCurrentTab = 0; + +/*! windows dialog procedure */ +static INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +VOID ShowAboutDialog(HWND hwnd) +{ + /*! show dialog box */ + DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_ABOUT), hwnd, DlgAboutProc); +} + +INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + // Set the dialog icon + HICON hIcon = (HICON)LoadImageW( + GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDI_CXBX), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE + ); + + SendMessageW(hWndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); + + // Build the Tab Control + TCITEM tabInfo; + memset(&tabInfo, 0, sizeof(tabInfo)); + tabInfo.mask = TCIF_TEXT; + tabInfo.cchTextMax = 20; + tabInfo.pszText = "About"; + SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_INSERTITEM, 0, (LPARAM)&tabInfo); + tabInfo.pszText = "Contributors"; + SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_INSERTITEM, 1, (LPARAM)&tabInfo); + tabInfo.pszText = "License"; + SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_INSERTITEM, 2, (LPARAM)&tabInfo); + + // Get tab pane dimensions + RECT tabRect; + GetClientRect(GetDlgItem(hWndDlg, IDC_TAB1), &tabRect); + SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect); + // Tab Pane 1 + char TabPane1Message[270]; + sprintf(TabPane1Message, "\nCxbx-Reloaded\nVersion %s\n© The Cxbx-Reloaded Team" + "\nThis software comes with ABSOLUTELY NO WARRANTY." + "\nThis is free software, and you are welcome to redistribute it" + "\nunder certain conditions; See our website for details.", CxbxVersionStr); + HWND tab = CreateWindowEx + (NULL, "STATIC", TabPane1Message, + WS_CHILD | WS_VISIBLE, + tabRect.left + 10, tabRect.top + 10, + tabRect.right - tabRect.left, + tabRect.bottom - tabRect.top, + GetDlgItem(hWndDlg, IDC_TAB1), (HMENU)1, + GetModuleHandle(nullptr), nullptr + ); + + SendMessage(tab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + SetParent(tab, hWndDlg); + aboutTabPanes.push_back(tab); + + // Tab Pane 2 + HRSRC rContributors = FindResource(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_CONTRIBUTORS), "TXT"); + + std::string contributors = "\n"; + contributors += std::string( + (char*)LockResource(LoadResource(GetModuleHandle(nullptr), rContributors)), + SizeofResource(GetModuleHandle(nullptr), rContributors) + ); + + unix2dos(contributors); + + tab = CreateWindowEx( + 0, "EDIT", contributors.c_str(), + WS_CHILD | WS_VSCROLL |ES_MULTILINE | ES_READONLY, + tabRect.left + 10, tabRect.top + 10, + tabRect.right - tabRect.left, + tabRect.bottom - tabRect.top, + GetDlgItem(hWndDlg, IDC_TAB1), (HMENU)1, + GetModuleHandle(nullptr), nullptr + ); + + SendMessage(tab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + SetParent(tab, hWndDlg); + aboutTabPanes.push_back(tab); + + // Tab Pane 3 + HRSRC rCopying = FindResource(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_COPYING), "TXT"); + + std::string copying = "\n"; + copying += std::string( + (char*)LockResource(LoadResource(GetModuleHandle(nullptr), rCopying)), + SizeofResource(GetModuleHandle(nullptr), rCopying) + ); + + unix2dos(copying); + + tab = CreateWindowEx( + 0, "EDIT", copying.c_str(), + WS_CHILD | WS_VSCROLL | ES_MULTILINE | ES_READONLY, + tabRect.left + 10, tabRect.top + 10, + tabRect.right - tabRect.left, + tabRect.bottom - tabRect.top, + GetDlgItem(hWndDlg, IDC_TAB1), (HMENU)1, + GetModuleHandle(nullptr), nullptr + ); + + SendMessage(tab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + SetParent(tab, hWndDlg); + aboutTabPanes.push_back(tab); + + aboutCurrentTab = 0; + UpdateWindow(hWndDlg); + + } + break; + + case WM_CLOSE: + { + PostMessage(hWndDlg, WM_COMMAND, IDOK, 0); + } + break; + + case WM_COMMAND: + { + HWND hWndButton = GetDlgItem(hWndDlg, LOWORD(wParam)); + + switch (LOWORD(wParam)) + { + case IDOK: + for (auto it = aboutTabPanes.begin(); it != aboutTabPanes.end(); ++it) { + DestroyWindow(*it); + } + + aboutTabPanes.clear(); + EndDialog(hWndDlg, wParam); + break; + } + } + break; + + case WM_NOTIFY: + { + unsigned int index = SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_GETCURSEL, 0, 0); + + // Do nothing if the tab hasnt been changed, or if we try to load a non existant tab + if (index == aboutCurrentTab || index >= aboutTabPanes.size()) { + break; + } + + // Show the selected tab pane + ShowWindow(aboutTabPanes[aboutCurrentTab], SW_HIDE); + ShowWindow(aboutTabPanes[index], SW_SHOW); + + aboutCurrentTab = index; + } + break; + } + return FALSE; +} diff --git a/src/gui/DlgEepromConfig.cpp b/src/gui/DlgEepromConfig.cpp index cb7c27fc9..ed74d4de1 100644 --- a/src/gui/DlgEepromConfig.cpp +++ b/src/gui/DlgEepromConfig.cpp @@ -27,7 +27,7 @@ #define LOG_PREFIX CXBXR_MODULE::GUI -#include +#include #include // For memcpy #include "EmuEEPROM.h" // For EEPROMInfo, EEPROMInfos #include "core\kernel\init\CxbxKrnl.h" @@ -218,9 +218,9 @@ void ShowEepromConfig(HWND hwnd) // Show dialog box DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_EEPROM_CFG), hwnd, DlgEepromConfigProc); -} - -static void RefreshEepromDialog(HWND hWndDlg) +} + +static void RefreshEepromDialog(HWND hWndDlg) { int offset; int starting_offset; @@ -401,14 +401,14 @@ static void RefreshEepromDialog(HWND hWndDlg) std::memcpy(Buffer[5], &pEEPROM_GUI->UserSettings.ParentalControlPassword, ByteLimit[5]); for (auto i : Buffer) { char* CharBuffer = new char[ByteLimit[j] * 2 + 1]; - for (int z = 0, y = 0; z < ByteLimit[j]; z++) { - // Special case for Serial Number: This field is textual + for (int z = 0, y = 0; z < ByteLimit[j]; z++) { + // Special case for Serial Number: This field is textual if (hEditControlArray[j] == GetDlgItem(hWndDlg, IDC_EE_SERIAL_NUMBER)) { - std::sprintf(&CharBuffer[y], "%c", i[z]); + std::sprintf(&CharBuffer[y], "%c", i[z]); y += 1; } else { - std::sprintf(&CharBuffer[y], "%02X", i[z]); - y += 2; + std::sprintf(&CharBuffer[y], "%02X", i[z]); + y += 2; } } SendMessage(hEditControlArray[j], WM_SETTEXT, 0, reinterpret_cast(CharBuffer)); @@ -425,7 +425,7 @@ INT_PTR CALLBACK DlgEepromConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPA { case WM_INITDIALOG: { - RefreshEepromDialog(hWndDlg); + RefreshEepromDialog(hWndDlg); // Reset changes flag g_bHasChanges = false; } @@ -579,19 +579,19 @@ LRESULT CALLBACK ControlSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, break; case WM_CHAR: - { - // Serial Number field is numeric, stored as ASCII text - if (uIdSubclass == IDC_EE_SERIAL_NUMBER) { - if (!((wParam >= '0' && wParam <= '9') + { + // Serial Number field is numeric, stored as ASCII text + if (uIdSubclass == IDC_EE_SERIAL_NUMBER) { + if (!((wParam >= '0' && wParam <= '9') || wParam == VK_CANCEL || wParam == VK_CLEAR || wParam == VK_DELETE - || wParam == VK_BACK)) - { - return FALSE; - } - break; - } + || wParam == VK_BACK)) + { + return FALSE; + } + break; + } // Make sure we only allow hex numbers and some special keys to delete characters if (!((wParam >= '0' && wParam <= '9') diff --git a/src/gui/DlgInputConfig.cpp b/src/gui/DlgInputConfig.cpp index 29575b6e6..e2f24a9d4 100644 --- a/src/gui/DlgInputConfig.cpp +++ b/src/gui/DlgInputConfig.cpp @@ -142,7 +142,7 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR int DeviceType = SendMessage(hHandle, CB_GETITEMDATA, SendMessage(hHandle, CB_GETCURSEL, 0, 0), 0); switch (DeviceType) { - case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): + case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE): case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S): { DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_XID_DUKE_CFG), hWndDlg, DlgXidControllerConfigProc, (DeviceType << 8) | port); diff --git a/src/gui/DlgLoggingConfig.cpp b/src/gui/DlgLoggingConfig.cpp index 0d09a036f..35e36a5f0 100644 --- a/src/gui/DlgLoggingConfig.cpp +++ b/src/gui/DlgLoggingConfig.cpp @@ -21,121 +21,121 @@ // * // * All rights reserved // * -// ****************************************************************** - -#include "Logging.h" -#include "EmuShared.h" -#include "DlgLoggingConfig.h" -#include "resource/ResCxbx.h" -#include "common\IPCHybrid.hpp" - - -static bool g_bHasChanges = false; -static HWND g_ChildWnd = NULL; -INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); - -static int g_DlgIndexes[] = { - // General - IDC_LOG_CXBXR, - IDC_LOG_XBE, - IDC_LOG_INIT, - IDC_LOG_VMEM, - IDC_LOG_PMEM, - IDC_LOG_GUI, - IDC_LOG_EEPR, - IDC_LOG_RSA, - IDC_LOG_POOLMEM, - IDC_LOG_D3D8, - IDC_LOG_D3DST, - IDC_LOG_D3DCVT, - IDC_LOG_DSOUND, - IDC_LOG_XAPI, - IDC_LOG_XACT, - IDC_LOG_XGRP, - IDC_LOG_XONLINE, - IDC_LOG_FS, - IDC_LOG_PSHB, - IDC_LOG_PXSH, - IDC_LOG_VTXSH, - IDC_LOG_VSHCACHE, - IDC_LOG_VTXB, - IDC_LOG_DINP, - IDC_LOG_XINP, - IDC_LOG_SDL, - IDC_LOG_FILE, - IDC_LOG_X86, - IDC_LOG_HLE, - IDC_LOG_NET, - IDC_LOG_MCPX, - IDC_LOG_NV2A, - IDC_LOG_SMC, - IDC_LOG_OHCI, - IDC_LOG_USB, - IDC_LOG_HUB, - IDC_LOG_XIDCTRL, - IDC_LOG_ADM, - IDC_LOG_INPSYS, - IDC_LOG_DSBUFFER, - IDC_LOG_DSSTREAM, - IDC_LOG_DS3DCALC, - IDC_LOG_XMO, - // Kernel - IDC_LOG_KRNL, - IDC_LOG_LOG, - IDC_LOG_XBOX, - IDC_LOG_XBDM, - IDC_LOG_AV, - IDC_LOG_DBG, - IDC_LOG_EX, - IDC_LOG_FSC, - IDC_LOG_HAL, - IDC_LOG_IO, - IDC_LOG_KD, - IDC_LOG_KE, - IDC_LOG_KI, - IDC_LOG_MM, - IDC_LOG_NT, - IDC_LOG_OB, - IDC_LOG_PS, - IDC_LOG_RTL, - IDC_LOG_XC, - IDC_LOG_XE -}; - - -VOID ShowLoggingConfig(HWND hwnd, HWND ChildWnd) -{ - g_ChildWnd = ChildWnd; - DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_LOGGING_CFG), hwnd, DlgLogConfigProc); -} - -INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - unsigned int index; - switch (uMsg) - { - case WM_INITDIALOG: - { - HWND hHandle; - int counter; - int TempLevel; - unsigned int LoggedModules[NUM_INTEGERS_LOG]; - int LogLevel; - bool LogPopupTestCase; - - // Set window icon - SetClassLong(hWndDlg, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX))); - - LoggedModules[0] = g_Settings->m_core.LoggedModules[0]; - LoggedModules[1] = g_Settings->m_core.LoggedModules[1]; - LogLevel = g_Settings->m_core.LogLevel; - LogPopupTestCase = g_Settings->m_core.bLogPopupTestCase; - - hHandle = GetDlgItem(hWndDlg, IDC_EVENT_LV); +// ****************************************************************** + +#include "Logging.h" +#include "EmuShared.h" +#include "DlgLoggingConfig.h" +#include "resource/ResCxbx.h" +#include "common\IPCHybrid.hpp" + + +static bool g_bHasChanges = false; +static HWND g_ChildWnd = NULL; +INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static int g_DlgIndexes[] = { + // General + IDC_LOG_CXBXR, + IDC_LOG_XBE, + IDC_LOG_INIT, + IDC_LOG_VMEM, + IDC_LOG_PMEM, + IDC_LOG_GUI, + IDC_LOG_EEPR, + IDC_LOG_RSA, + IDC_LOG_POOLMEM, + IDC_LOG_D3D8, + IDC_LOG_D3DST, + IDC_LOG_D3DCVT, + IDC_LOG_DSOUND, + IDC_LOG_XAPI, + IDC_LOG_XACT, + IDC_LOG_XGRP, + IDC_LOG_XONLINE, + IDC_LOG_FS, + IDC_LOG_PSHB, + IDC_LOG_PXSH, + IDC_LOG_VTXSH, + IDC_LOG_VSHCACHE, + IDC_LOG_VTXB, + IDC_LOG_DINP, + IDC_LOG_XINP, + IDC_LOG_SDL, + IDC_LOG_FILE, + IDC_LOG_X86, + IDC_LOG_HLE, + IDC_LOG_NET, + IDC_LOG_MCPX, + IDC_LOG_NV2A, + IDC_LOG_SMC, + IDC_LOG_OHCI, + IDC_LOG_USB, + IDC_LOG_HUB, + IDC_LOG_XIDCTRL, + IDC_LOG_ADM, + IDC_LOG_INPSYS, + IDC_LOG_DSBUFFER, + IDC_LOG_DSSTREAM, + IDC_LOG_DS3DCALC, + IDC_LOG_XMO, + // Kernel + IDC_LOG_KRNL, + IDC_LOG_LOG, + IDC_LOG_XBOX, + IDC_LOG_XBDM, + IDC_LOG_AV, + IDC_LOG_DBG, + IDC_LOG_EX, + IDC_LOG_FSC, + IDC_LOG_HAL, + IDC_LOG_IO, + IDC_LOG_KD, + IDC_LOG_KE, + IDC_LOG_KI, + IDC_LOG_MM, + IDC_LOG_NT, + IDC_LOG_OB, + IDC_LOG_PS, + IDC_LOG_RTL, + IDC_LOG_XC, + IDC_LOG_XE +}; + + +VOID ShowLoggingConfig(HWND hwnd, HWND ChildWnd) +{ + g_ChildWnd = ChildWnd; + DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_LOGGING_CFG), hwnd, DlgLogConfigProc); +} + +INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + unsigned int index; + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hHandle; + int counter; + int TempLevel; + unsigned int LoggedModules[NUM_INTEGERS_LOG]; + int LogLevel; + bool LogPopupTestCase; + + // Set window icon + SetClassLong(hWndDlg, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX))); + + LoggedModules[0] = g_Settings->m_core.LoggedModules[0]; + LoggedModules[1] = g_Settings->m_core.LoggedModules[1]; + LogLevel = g_Settings->m_core.LogLevel; + LogPopupTestCase = g_Settings->m_core.bLogPopupTestCase; + + hHandle = GetDlgItem(hWndDlg, IDC_EVENT_LV); TempLevel = to_underlying(LOG_LEVEL::DEBUG); for (auto i : { "Debug" , "Info" ,"Warning", "Error", "Fatal" }) { LRESULT index = SendMessage(hHandle, CB_ADDSTRING, 0, reinterpret_cast(i)); - SendMessage(hHandle, CB_SETITEMDATA, index, TempLevel); + SendMessage(hHandle, CB_SETITEMDATA, index, TempLevel); TempLevel++; } TempLevel = to_underlying(LOG_LEVEL::DEBUG); @@ -144,281 +144,281 @@ INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM SendMessage(hHandle, CB_SETCURSEL, TempLevel, 0); break; } - } - - if (LogPopupTestCase) { - (void)SendMessage(GetDlgItem(hWndDlg, IDC_LOG_POPUP_TESTCASE), BM_SETCHECK, BST_CHECKED, 0); - } - - counter = 0; - for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); index++) { - if (LoggedModules[index / 32] & (1 << (index % 32))) { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); - counter++; - } - else { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); - } - } - - if (counter == to_underlying(CXBXR_MODULE::KRNL)) { - for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); index++) { - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); - } - SendMessage(GetDlgItem(hWndDlg, IDC_LOG_ENABLE_GENERAL), BM_SETCHECK, BST_CHECKED, 0); - } - else if (counter == 0) { - for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); index++) { - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); - } - SendMessage(GetDlgItem(hWndDlg, IDC_LOG_DISABLE_GENERAL), BM_SETCHECK, BST_CHECKED, 0); - } - else { - SendMessage(GetDlgItem(hWndDlg, IDC_LOG_CUSTOM_GENERAL), BM_SETCHECK, BST_CHECKED, 0); - } - - counter = 0; - for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); index++) { - if (LoggedModules[index / 32] & (1 << (index % 32))) { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); - counter++; - } - else { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); - } - } - - if (counter == (to_underlying(CXBXR_MODULE::MAX) - to_underlying(CXBXR_MODULE::KRNL))) { - for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); index++) { - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); - } - SendMessage(GetDlgItem(hWndDlg, IDC_LOG_ENABLE_KERNEL), BM_SETCHECK, BST_CHECKED, 0); - } - else if (counter == 0) { - for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); index++) { - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); - } - SendMessage(GetDlgItem(hWndDlg, IDC_LOG_DISABLE_KERNEL), BM_SETCHECK, BST_CHECKED, 0); - } - else { - SendMessage(GetDlgItem(hWndDlg, IDC_LOG_CUSTOM_KERNEL), BM_SETCHECK, BST_CHECKED, 0); - } - + } + + if (LogPopupTestCase) { + (void)SendMessage(GetDlgItem(hWndDlg, IDC_LOG_POPUP_TESTCASE), BM_SETCHECK, BST_CHECKED, 0); + } + + counter = 0; + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); index++) { + if (LoggedModules[index / 32] & (1 << (index % 32))) { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); + counter++; + } + else { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); + } + } + + if (counter == to_underlying(CXBXR_MODULE::KRNL)) { + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); index++) { + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + } + SendMessage(GetDlgItem(hWndDlg, IDC_LOG_ENABLE_GENERAL), BM_SETCHECK, BST_CHECKED, 0); + } + else if (counter == 0) { + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); index++) { + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + } + SendMessage(GetDlgItem(hWndDlg, IDC_LOG_DISABLE_GENERAL), BM_SETCHECK, BST_CHECKED, 0); + } + else { + SendMessage(GetDlgItem(hWndDlg, IDC_LOG_CUSTOM_GENERAL), BM_SETCHECK, BST_CHECKED, 0); + } + + counter = 0; + for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); index++) { + if (LoggedModules[index / 32] & (1 << (index % 32))) { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); + counter++; + } + else { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); + } + } + + if (counter == (to_underlying(CXBXR_MODULE::MAX) - to_underlying(CXBXR_MODULE::KRNL))) { + for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); index++) { + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + } + SendMessage(GetDlgItem(hWndDlg, IDC_LOG_ENABLE_KERNEL), BM_SETCHECK, BST_CHECKED, 0); + } + else if (counter == 0) { + for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); index++) { + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + } + SendMessage(GetDlgItem(hWndDlg, IDC_LOG_DISABLE_KERNEL), BM_SETCHECK, BST_CHECKED, 0); + } + else { + SendMessage(GetDlgItem(hWndDlg, IDC_LOG_CUSTOM_KERNEL), BM_SETCHECK, BST_CHECKED, 0); + } + // Reset changes flag - g_bHasChanges = false; - } - break; - + g_bHasChanges = false; + } + break; + case WM_CLOSE: - { + { PostMessage(hWndDlg, WM_COMMAND, IDC_LOG_CANCEL, 0); } - break; - - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDC_LOG_CANCEL: - { + break; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_LOG_CANCEL: + { g_ChildWnd = NULL; - EndDialog(hWndDlg, wParam); - } - break; - - case IDC_LOG_ACCEPT: - { - if (g_bHasChanges) { - unsigned int LoggedModules[NUM_INTEGERS_LOG] = { 0 }; - HWND hControl = GetDlgItem(hWndDlg, IDC_EVENT_LV); - int LogLevel = SendMessage(hControl, CB_GETITEMDATA, SendMessage(hControl, CB_GETCURSEL, 0, 0), 0); - + EndDialog(hWndDlg, wParam); + } + break; + + case IDC_LOG_ACCEPT: + { + if (g_bHasChanges) { + unsigned int LoggedModules[NUM_INTEGERS_LOG] = { 0 }; + HWND hControl = GetDlgItem(hWndDlg, IDC_EVENT_LV); + int LogLevel = SendMessage(hControl, CB_GETITEMDATA, SendMessage(hControl, CB_GETCURSEL, 0, 0), 0); + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::MAX); index++) { if (SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_GETCHECK, 0, 0) == BST_CHECKED) { LoggedModules[index / 32] |= (1 << (index % 32)); } - } - + } + bool LogPopupTestCase = false; if (SendMessage(GetDlgItem(hWndDlg, IDC_LOG_POPUP_TESTCASE), BM_GETCHECK, 0, 0) == BST_CHECKED) { LogPopupTestCase = true; } - g_Settings->m_core.LoggedModules[0] = LoggedModules[0]; - g_Settings->m_core.LoggedModules[1] = LoggedModules[1]; - g_Settings->m_core.LogLevel = LogLevel; - g_Settings->m_core.bLogPopupTestCase = LogPopupTestCase; - - // Update the logging variables for the GUI process - log_set_config(LogLevel, LoggedModules, LogPopupTestCase); - log_generate_active_filter_output(CXBXR_MODULE::GUI); - - // Also inform the kernel process if it exists - if (g_ChildWnd) { - // Sync updated log to kernel process to use run-time settings. - g_EmuShared->SetLogLv(&LogLevel); - g_EmuShared->SetLogModules(LoggedModules); - g_EmuShared->SetLogPopupTestCase(LogPopupTestCase); + g_Settings->m_core.LoggedModules[0] = LoggedModules[0]; + g_Settings->m_core.LoggedModules[1] = LoggedModules[1]; + g_Settings->m_core.LogLevel = LogLevel; + g_Settings->m_core.bLogPopupTestCase = LogPopupTestCase; + + // Update the logging variables for the GUI process + log_set_config(LogLevel, LoggedModules, LogPopupTestCase); + log_generate_active_filter_output(CXBXR_MODULE::GUI); + + // Also inform the kernel process if it exists + if (g_ChildWnd) { + // Sync updated log to kernel process to use run-time settings. + g_EmuShared->SetLogLv(&LogLevel); + g_EmuShared->SetLogModules(LoggedModules); + g_EmuShared->SetLogPopupTestCase(LogPopupTestCase); ipc_send_kernel_update(IPC_UPDATE_KERNEL::CONFIG_LOGGING_SYNC, 0, reinterpret_cast(g_ChildWnd)); } - } - PostMessage(hWndDlg, WM_COMMAND, IDC_LOG_CANCEL, 0); - } - break; - - case IDC_EVENT_LV: + } + PostMessage(hWndDlg, WM_COMMAND, IDC_LOG_CANCEL, 0); + } + break; + + case IDC_EVENT_LV: if (HIWORD(wParam) == CBN_SELCHANGE) { g_bHasChanges = true; } - break; - - case IDC_LOG_POPUP_TESTCASE: + break; + + case IDC_LOG_POPUP_TESTCASE: if (HIWORD(wParam) == BN_CLICKED) { g_bHasChanges = true; } - break; - - case IDC_LOG_ENABLE_GENERAL: { - if (HIWORD(wParam) == BN_CLICKED) { - for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); - index++) { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + break; + + case IDC_LOG_ENABLE_GENERAL: { + if (HIWORD(wParam) == BN_CLICKED) { + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); + index++) { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); } g_bHasChanges = true; - } - } - break; - - case IDC_LOG_DISABLE_GENERAL: { - if (HIWORD(wParam) == BN_CLICKED) { - for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); - index++) { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + } + } + break; + + case IDC_LOG_DISABLE_GENERAL: { + if (HIWORD(wParam) == BN_CLICKED) { + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); + index++) { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); } g_bHasChanges = true; - } - } - break; - - case IDC_LOG_ENABLE_KERNEL: { - if (HIWORD(wParam) == BN_CLICKED) { - for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); - index++) { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + } + } + break; + + case IDC_LOG_ENABLE_KERNEL: { + if (HIWORD(wParam) == BN_CLICKED) { + for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); + index++) { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); } - g_bHasChanges = true; - } - } - break; - - case IDC_LOG_DISABLE_KERNEL: { - if (HIWORD(wParam) == BN_CLICKED) { - for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); - index++) { - SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); + g_bHasChanges = true; + } + } + break; + + case IDC_LOG_DISABLE_KERNEL: { + if (HIWORD(wParam) == BN_CLICKED) { + for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); + index++) { + SendMessage(GetDlgItem(hWndDlg, g_DlgIndexes[index]), BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), FALSE); } - g_bHasChanges = true; - } - } - break; - - case IDC_LOG_CUSTOM_GENERAL: { - if (HIWORD(wParam) == BN_CLICKED) { - for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); - index++) { - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), TRUE); + g_bHasChanges = true; + } + } + break; + + case IDC_LOG_CUSTOM_GENERAL: { + if (HIWORD(wParam) == BN_CLICKED) { + for (index = to_underlying(CXBXR_MODULE::CXBXR); index < to_underlying(CXBXR_MODULE::KRNL); + index++) { + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), TRUE); } - g_bHasChanges = true; - } - } - break; - - case IDC_LOG_CUSTOM_KERNEL: { - if (HIWORD(wParam) == BN_CLICKED) { - for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); - index++) { - EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), TRUE); + g_bHasChanges = true; + } + } + break; + + case IDC_LOG_CUSTOM_KERNEL: { + if (HIWORD(wParam) == BN_CLICKED) { + for (index = to_underlying(CXBXR_MODULE::KRNL); index < to_underlying(CXBXR_MODULE::MAX); + index++) { + EnableWindow(GetDlgItem(hWndDlg, g_DlgIndexes[index]), TRUE); } - g_bHasChanges = true; - } - } - break; - - case IDC_LOG_CXBXR: - case IDC_LOG_XBE: - case IDC_LOG_INIT: - case IDC_LOG_VMEM: - case IDC_LOG_PMEM: - case IDC_LOG_GUI: - case IDC_LOG_EEPR: - case IDC_LOG_RSA: - case IDC_LOG_POOLMEM: - case IDC_LOG_D3D8: - case IDC_LOG_D3DST: - case IDC_LOG_D3DCVT: - case IDC_LOG_DSOUND: - case IDC_LOG_DSBUFFER: - case IDC_LOG_DSSTREAM: - case IDC_LOG_DS3DCALC: - case IDC_LOG_XMO: - case IDC_LOG_XAPI: - case IDC_LOG_XACT: - case IDC_LOG_XGRP: - case IDC_LOG_XONLINE: - case IDC_LOG_FS: - case IDC_LOG_PSHB: - case IDC_LOG_PXSH: - case IDC_LOG_VTXSH: - case IDC_LOG_VSHCACHE: - case IDC_LOG_VTXB: - case IDC_LOG_DINP: - case IDC_LOG_XINP: - case IDC_LOG_SDL: - case IDC_LOG_FILE: - case IDC_LOG_X86: - case IDC_LOG_HLE: - case IDC_LOG_NET: - case IDC_LOG_MCPX: - case IDC_LOG_NV2A: - case IDC_LOG_SMC: - case IDC_LOG_OHCI: - case IDC_LOG_USB: - case IDC_LOG_HUB: - case IDC_LOG_XIDCTRL: - case IDC_LOG_ADM: - case IDC_LOG_INPSYS: - case IDC_LOG_KRNL: - case IDC_LOG_LOG: - case IDC_LOG_XBOX: - case IDC_LOG_XBDM: - case IDC_LOG_AV: - case IDC_LOG_DBG: - case IDC_LOG_EX: - case IDC_LOG_FSC: - case IDC_LOG_HAL: - case IDC_LOG_IO: - case IDC_LOG_KD: - case IDC_LOG_KE: - case IDC_LOG_KI: - case IDC_LOG_MM: - case IDC_LOG_NT: - case IDC_LOG_OB: - case IDC_LOG_PS: - case IDC_LOG_RTL: - case IDC_LOG_XC: - case IDC_LOG_XE: + g_bHasChanges = true; + } + } + break; + + case IDC_LOG_CXBXR: + case IDC_LOG_XBE: + case IDC_LOG_INIT: + case IDC_LOG_VMEM: + case IDC_LOG_PMEM: + case IDC_LOG_GUI: + case IDC_LOG_EEPR: + case IDC_LOG_RSA: + case IDC_LOG_POOLMEM: + case IDC_LOG_D3D8: + case IDC_LOG_D3DST: + case IDC_LOG_D3DCVT: + case IDC_LOG_DSOUND: + case IDC_LOG_DSBUFFER: + case IDC_LOG_DSSTREAM: + case IDC_LOG_DS3DCALC: + case IDC_LOG_XMO: + case IDC_LOG_XAPI: + case IDC_LOG_XACT: + case IDC_LOG_XGRP: + case IDC_LOG_XONLINE: + case IDC_LOG_FS: + case IDC_LOG_PSHB: + case IDC_LOG_PXSH: + case IDC_LOG_VTXSH: + case IDC_LOG_VSHCACHE: + case IDC_LOG_VTXB: + case IDC_LOG_DINP: + case IDC_LOG_XINP: + case IDC_LOG_SDL: + case IDC_LOG_FILE: + case IDC_LOG_X86: + case IDC_LOG_HLE: + case IDC_LOG_NET: + case IDC_LOG_MCPX: + case IDC_LOG_NV2A: + case IDC_LOG_SMC: + case IDC_LOG_OHCI: + case IDC_LOG_USB: + case IDC_LOG_HUB: + case IDC_LOG_XIDCTRL: + case IDC_LOG_ADM: + case IDC_LOG_INPSYS: + case IDC_LOG_KRNL: + case IDC_LOG_LOG: + case IDC_LOG_XBOX: + case IDC_LOG_XBDM: + case IDC_LOG_AV: + case IDC_LOG_DBG: + case IDC_LOG_EX: + case IDC_LOG_FSC: + case IDC_LOG_HAL: + case IDC_LOG_IO: + case IDC_LOG_KD: + case IDC_LOG_KE: + case IDC_LOG_KI: + case IDC_LOG_MM: + case IDC_LOG_NT: + case IDC_LOG_OB: + case IDC_LOG_PS: + case IDC_LOG_RTL: + case IDC_LOG_XC: + case IDC_LOG_XE: if (HIWORD(wParam) == BN_CLICKED) { g_bHasChanges = true; } - break; - } - } - break; - - } - return FALSE; -} + break; + } + } + break; + + } + return FALSE; +} diff --git a/src/gui/DlgLoggingConfig.h b/src/gui/DlgLoggingConfig.h index 8870292ec..fb44a32c5 100644 --- a/src/gui/DlgLoggingConfig.h +++ b/src/gui/DlgLoggingConfig.h @@ -21,11 +21,11 @@ // * // * All rights reserved // * -// ****************************************************************** - +// ****************************************************************** + #ifndef DLGLOGGINGCONFIG_H -#define DLGLOGGINGCONFIG_H - -VOID ShowLoggingConfig(HWND hwnd, HWND ChildWnd); - -#endif +#define DLGLOGGINGCONFIG_H + +VOID ShowLoggingConfig(HWND hwnd, HWND ChildWnd); + +#endif diff --git a/src/gui/WinMain.cpp b/src/gui/WinMain.cpp index 49c0835e7..d47d8e97e 100644 --- a/src/gui/WinMain.cpp +++ b/src/gui/WinMain.cpp @@ -1,151 +1,151 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2006 Aaron Robinson -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::CXBXR - -#include "WndMain.h" - -#include "AddressRanges.h" // For VerifyWow64() -#include "VerifyAddressRanges.h" // For VerifyBaseAddr() -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\support\Emu.h" -#include "EmuShared.h" -#include "common\Settings.hpp" -#include -#include "common/util/cliConverter.hpp" -#include "common/util/cliConfig.hpp" - - -// Enable Visual Styles -#pragma comment(linker,"\"/manifestdependency:type='win32' \ -name = 'Microsoft.Windows.Common-Controls' version = '6.0.0.0' \ -processorArchitecture = '*' publicKeyToken = '6595b64144ccf1df' language = '*'\"") - -/*! program entry point */ -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - hActiveModule = hInstance; // == GetModuleHandle(NULL); // Points to GUI (Cxbx.exe) ImageBase - std::string tempStr; - - // First detect if we are running on WoW64, if not, prevent Cxbx-Reloaded from starting - // Cxbx-Reloaded needs access to high memory, only exposed to WoW64. - if (!VerifyWow64()) { - PopupError(nullptr, "Cxbx-Reloaded can only run under WoW64\nThis means either a 64-bit version of Windows or Wine with a 64-bit prefix"); - return EXIT_FAILURE; - } - -#ifndef CXBXR_EMU - /*! verify Cxbx.exe is loaded to base address 0x00010000 */ - if (!VerifyBaseAddr()) { - PopupError(nullptr, "Cxbx.exe is not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)"); - return EXIT_FAILURE; - } -#endif - - if (!cli_config::GenConfig(__argv, __argc)) { - PopupError(nullptr, "Couldn't convert parsed command line!"); - return EXIT_FAILURE; - } - - /*! initialize shared memory */ - if (!EmuShared::Init(cli_config::GetSessionID())) { - PopupError(nullptr, "Could not map shared memory!"); - return EXIT_FAILURE; - } - - if (!HandleFirstLaunch()) { - EmuShared::Cleanup(); - return EXIT_FAILURE; - } - - if (cli_config::hasKey("load")) { -#ifndef CXBXR_EMU - CxbxKrnlEmulate(0, nullptr); - EmuShared::Cleanup(); - return EXIT_SUCCESS; -#else - PopupError(nullptr, "Emulation must be launched from cxbxr-ldr.exe!"); - EmuShared::Cleanup(); - return EXIT_FAILURE; -#endif - } - - // If 2nd GUI executable is launched, load settings file for GUI for editable support. - if (g_Settings == nullptr) { - if (!CreateSettings()) { - EmuShared::Cleanup(); - return EXIT_FAILURE; - } - } - - // Possible optional output for GUI - log_generate_active_filter_output(CXBXR_MODULE::INIT); - - INITCOMMONCONTROLSEX icc; - icc.dwSize = sizeof(icc); - icc.dwICC = ICC_WIN95_CLASSES; - InitCommonControlsEx(&icc); - - WndMain *MainWindow = new WndMain(hInstance); - - // NOTE: CxbxInitFilePaths must be initalize AFTER WndMain for data directory option from user. - /* Initialize Cxbx File Paths */ - CxbxInitFilePaths(); - - /*! wait for window to be created, or failure */ - while(!MainWindow->isCreated() && MainWindow->ProcessMessages()) - { - Sleep(20); - } - - /*! optionally open xbe and start emulation, if command line parameter was specified */ - if(cli_config::ConfigSize() > 1 && false == MainWindow->HasError() && cli_config::GetValue(cli_config::arg1, &tempStr)) - { - tempStr = std::filesystem::absolute(std::filesystem::path(tempStr)).string(); - MainWindow->OpenXbe(tempStr.c_str()); - - MainWindow->StartEmulation(MainWindow->GetHwnd()); - } - - /*! wait for window to be closed, or failure */ - while(!MainWindow->HasError() && MainWindow->ProcessMessages()) - { - Sleep(10); - } - - /*! if an error occurred, notify user */ - if(MainWindow->HasError()) { - PopupError(nullptr, MainWindow->GetError().c_str()); - } - - delete MainWindow; - - /*! cleanup shared memory */ - EmuShared::Cleanup(); - - return EXIT_SUCCESS; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2006 Aaron Robinson +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::CXBXR + +#include "WndMain.h" + +#include "AddressRanges.h" // For VerifyWow64() +#include "VerifyAddressRanges.h" // For VerifyBaseAddr() +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\Emu.h" +#include "EmuShared.h" +#include "common\Settings.hpp" +#include +#include "common/util/cliConverter.hpp" +#include "common/util/cliConfig.hpp" + + +// Enable Visual Styles +#pragma comment(linker,"\"/manifestdependency:type='win32' \ +name = 'Microsoft.Windows.Common-Controls' version = '6.0.0.0' \ +processorArchitecture = '*' publicKeyToken = '6595b64144ccf1df' language = '*'\"") + +/*! program entry point */ +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + hActiveModule = hInstance; // == GetModuleHandle(NULL); // Points to GUI (Cxbx.exe) ImageBase + std::string tempStr; + + // First detect if we are running on WoW64, if not, prevent Cxbx-Reloaded from starting + // Cxbx-Reloaded needs access to high memory, only exposed to WoW64. + if (!VerifyWow64()) { + PopupError(nullptr, "Cxbx-Reloaded can only run under WoW64\nThis means either a 64-bit version of Windows or Wine with a 64-bit prefix"); + return EXIT_FAILURE; + } + +#ifndef CXBXR_EMU + /*! verify Cxbx.exe is loaded to base address 0x00010000 */ + if (!VerifyBaseAddr()) { + PopupError(nullptr, "Cxbx.exe is not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)"); + return EXIT_FAILURE; + } +#endif + + if (!cli_config::GenConfig(__argv, __argc)) { + PopupError(nullptr, "Couldn't convert parsed command line!"); + return EXIT_FAILURE; + } + + /*! initialize shared memory */ + if (!EmuShared::Init(cli_config::GetSessionID())) { + PopupError(nullptr, "Could not map shared memory!"); + return EXIT_FAILURE; + } + + if (!HandleFirstLaunch()) { + EmuShared::Cleanup(); + return EXIT_FAILURE; + } + + if (cli_config::hasKey("load")) { +#ifndef CXBXR_EMU + CxbxKrnlEmulate(0, nullptr); + EmuShared::Cleanup(); + return EXIT_SUCCESS; +#else + PopupError(nullptr, "Emulation must be launched from cxbxr-ldr.exe!"); + EmuShared::Cleanup(); + return EXIT_FAILURE; +#endif + } + + // If 2nd GUI executable is launched, load settings file for GUI for editable support. + if (g_Settings == nullptr) { + if (!CreateSettings()) { + EmuShared::Cleanup(); + return EXIT_FAILURE; + } + } + + // Possible optional output for GUI + log_generate_active_filter_output(CXBXR_MODULE::INIT); + + INITCOMMONCONTROLSEX icc; + icc.dwSize = sizeof(icc); + icc.dwICC = ICC_WIN95_CLASSES; + InitCommonControlsEx(&icc); + + WndMain *MainWindow = new WndMain(hInstance); + + // NOTE: CxbxInitFilePaths must be initalize AFTER WndMain for data directory option from user. + /* Initialize Cxbx File Paths */ + CxbxInitFilePaths(); + + /*! wait for window to be created, or failure */ + while(!MainWindow->isCreated() && MainWindow->ProcessMessages()) + { + Sleep(20); + } + + /*! optionally open xbe and start emulation, if command line parameter was specified */ + if(cli_config::ConfigSize() > 1 && false == MainWindow->HasError() && cli_config::GetValue(cli_config::arg1, &tempStr)) + { + tempStr = std::filesystem::absolute(std::filesystem::path(tempStr)).string(); + MainWindow->OpenXbe(tempStr.c_str()); + + MainWindow->StartEmulation(MainWindow->GetHwnd()); + } + + /*! wait for window to be closed, or failure */ + while(!MainWindow->HasError() && MainWindow->ProcessMessages()) + { + Sleep(10); + } + + /*! if an error occurred, notify user */ + if(MainWindow->HasError()) { + PopupError(nullptr, MainWindow->GetError().c_str()); + } + + delete MainWindow; + + /*! cleanup shared memory */ + EmuShared::Cleanup(); + + return EXIT_SUCCESS; +} diff --git a/src/gui/WndMain.cpp b/src/gui/WndMain.cpp index 32adfa5a8..888f9ad8a 100644 --- a/src/gui/WndMain.cpp +++ b/src/gui/WndMain.cpp @@ -1,2505 +1,2505 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2003 Aaron Robinson -// * (c) 2017-2018 RadWolfie -// * (c) 2018 wutno (#/g/punk - Rizon) -// * -// * All rights reserved -// * -// ****************************************************************** - -#define LOG_PREFIX CXBXR_MODULE::GUI - -#include "Logging.h" -#include "WndMain.h" -#include "DlgAbout.h" -#include "DlgInputConfig.h" -#include "DlgVideoConfig.h" -#include "DlgAudioConfig.h" -#include "DlgNetworkConfig.h" -#include "DlgEepromConfig.h" -#include "DlgLoggingConfig.h" -#include "common\xbe\XbePrinter.h" // For DumpInformation -#include "EmuShared.h" -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxSetPixelContainerHeader -#include "core\hle\D3D8\XbConvert.h" // For EmuPC2XB_D3DFormat -#include "common\Settings.hpp" -#include "common/util/cliConfig.hpp" - -#include "core\kernel\init\CxbxKrnl.h" // For CxbxExec -#include "resource/ResCxbx.h" -#include "CxbxVersion.h" -#include "Shlwapi.h" - -#undef GetSystemMetrics // Force remove DirectX 8's multimon.h defined function (redirect to xGetSystemMetrics). -#include // For GetSystemMetrics - -#include -#include - -#include // for std::stringstream -#include -#include -#include // for _O_TEXT -#include "common\util\hasher.h" - -#define XBOX_LED_FLASH_PERIOD 176 // if you know a more accurate value, put it here - -static int gameLogoWidth, gameLogoHeight; -static int splashLogoWidth, splashLogoHeight; - -bool g_SaveOnExit = true; - -void ClearSymbolCache(const char sStorageLocation[MAX_PATH]) -{ - std::string cacheDir = std::string(sStorageLocation) + "\\SymbolCache\\"; - std::string fullpath = cacheDir + "*.ini"; - - WIN32_FIND_DATA data; - HANDLE hFind = FindFirstFile(fullpath.c_str(), &data); - - if (hFind != INVALID_HANDLE_VALUE) { - BOOL bContinue = TRUE; - do { - if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - fullpath = cacheDir + data.cFileName; - - if (!std::filesystem::remove(fullpath)) { - break; - } - } - - bContinue = FindNextFile(hFind, &data); - } while (bContinue); - - FindClose(hFind); - } - - printf("Cleared HLE Cache\n"); -} - -void WndMain::InitializeSettings() -{ - g_Settings->Delete(); - g_SaveOnExit = false; -} - -#define TIMERID_ACTIVE_EMULATION 0 -#define TIMERID_LED 1 - -void WndMain::ResizeWindow(HWND hwnd, bool bForGUI) -{ - RECT desktopRect; - RECT windowRect; - - // Set the window size back to it's GUI dimensions - m_w = 640; - m_h = 480; - if (!bForGUI) { - // For emulation, get the configured window dimensions - Settings::s_video XBVideoConf; - g_EmuShared->GetVideoSettings(&XBVideoConf); - - const char* resolution = XBVideoConf.szVideoResolution; - if (2 != sscanf(resolution, "%d x %d", &m_w, &m_h)) { - EmuLog(LOG_LEVEL::DEBUG, "Couldn't parse resolution : %s.", resolution); - } - } - - // TODO : Acknowledge DPI scaling here - GetWindowRect(GetDesktopWindow(), &desktopRect); - - // Limit width/height to desktop resolution - int dWidth = desktopRect.right - desktopRect.left; - int dHeight = desktopRect.bottom - desktopRect.top; - if (m_w > dWidth) - m_w = dWidth; - if (m_h > dHeight) - m_h = dHeight; - - if (bForGUI && m_prevWindowLoc.x != -1 && m_prevWindowLoc.y != -1) { - // Restore to previous Window location - m_x = m_prevWindowLoc.x; - m_y = m_prevWindowLoc.y; - } - else { - // Center to desktop - m_x = desktopRect.left + ((dWidth - m_w) / 2); - m_y = desktopRect.top + ((dHeight - m_h) / 2); - } - - // Resize the window so it's client area can contain the requested resolution - windowRect = { m_x, m_y, m_x + m_w, m_y + m_h }; - AdjustWindowRectEx(&windowRect, GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, GetWindowLong(hwnd, GWL_EXSTYLE)); - // TODO : For DPI screens, replace AdjustWindowRectEx by DwmGetWindowAttribute using DWMWA_EXTENDED_FRAME_BOUNDS - SetWindowPos(hwnd, 0, - windowRect.left, - windowRect.top, - windowRect.right - windowRect.left, - windowRect.bottom - windowRect.top, - SWP_NOOWNERZORDER | SWP_NOZORDER); -} - -WndMain::WndMain(HINSTANCE x_hInstance) : - Wnd(x_hInstance) - , m_bCreated(false) - , m_Xbe(nullptr) - , m_bXbeChanged(false) - , m_bIsStarted(false) - , m_hwndChild(nullptr) - , m_hDebuggerProc(nullptr) - , m_hDebuggerMonitorThread() - , m_prevWindowLoc({ -1, -1 }) - , m_LogKrnl_status(false) -{ - // initialize members - { - m_classname = "WndMain"; - m_wndname = "Cxbx-Reloaded " + std::string(CxbxVersionStr); - } - - // load configuration from settings file - { - // NOTE: Settings has already been initalized/load from file before WndMain constructed. - - g_Settings->Verify(); - - // NOTE: This is a requirement for pre-verification from GUI. Used in CxbxInitFilePaths function. - g_EmuShared->SetStorageLocation(g_Settings->GetDataLocation().c_str()); - - unsigned int i = 0; - do { - if (g_Settings->m_gui.szRecentXbeFiles[i].size() == 0) { - break; - } - i++; - } while (i < RECENT_XBE_LIST_MAX); - - m_dwRecentXbe = i; - } -} - -WndMain::~WndMain() -{ - // save configuration to registry - if (g_SaveOnExit) { - g_Settings->Save(); - } - - // Close opened debugger monitor if there is one - DebuggerMonitorClose(); - - // cleanup allocations - { - delete m_Xbe; - delete g_Settings; - } -} - -// window message processing procedure -LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch(uMsg) - { - case WM_CREATE: - { - // initialize menu - { - HMENU hMenu = LoadMenu(m_hInstance, MAKEINTRESOURCE(IDR_MAINMENU)); - - SetMenu(hwnd, hMenu); - } - - // Set window size to GUI dimensions - ResizeWindow(hwnd, /*bForGUI=*/true); - - // initialize back buffer - { - HDC hDC = GetDC(hwnd); - - m_SplashBmp = CreateCompatibleBitmap(hDC, m_w, m_h); - - // create Xbox LED bitmap - { - m_xBmp = GetSystemMetrics(SM_CXMENUCHECK); - m_yBmp = GetSystemMetrics(SM_CYMENUCHECK); - m_LedDC = CreateCompatibleDC(hDC); - m_LedBmp = CreateCompatibleBitmap(hDC, m_xBmp, m_yBmp); - m_Brushes[XBOX_LED_COLOUR_OFF] = CreateSolidBrush(RGB(0, 0, 0)); - m_Brushes[XBOX_LED_COLOUR_GREEN] = CreateSolidBrush(RGB(0, 255, 0)); - m_Brushes[XBOX_LED_COLOUR_RED] = CreateSolidBrush(RGB(255, 0, 0)); - m_Brushes[XBOX_LED_COLOUR_ORANGE] = CreateSolidBrush(RGB(255, 165, 0)); - m_Pens[XBOX_LED_COLOUR_OFF] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); - m_Pens[XBOX_LED_COLOUR_GREEN] = CreatePen(PS_SOLID, 1, RGB(0, 255, 0)); - m_Pens[XBOX_LED_COLOUR_RED] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); - m_Pens[XBOX_LED_COLOUR_ORANGE] = CreatePen(PS_SOLID, 1, RGB(255, 165, 0)); - DrawLedBitmap(hwnd, true); - } - - splashLogoWidth = 500; - splashLogoHeight = 113; - m_SplashBmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDR_JPEG_SPLASH), IMAGE_BITMAP, 0, 0, 0); - - m_LogoBmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDB_LOGO), IMAGE_BITMAP, 0, 0, 0); - - m_SplashDC = CreateCompatibleDC(hDC); - m_LogoDC = CreateCompatibleDC(hDC); - - m_OrigBmp = (HBITMAP)SelectObject(m_SplashDC, m_SplashBmp); - m_OrigLogo = (HBITMAP)SelectObject(m_LogoDC, m_LogoBmp); - - m_BackgroundColor = CreateSolidBrush(RGB(48, 48, 48)); - - if(hDC != NULL) - ReleaseDC(hwnd, hDC); - } - - SetClassLong(hwnd, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX))); - DragAcceptFiles(hwnd, TRUE); - - // Allow Drag and Drop if Cxbx is run with elevated privileges on Windows Vista and above - - ChangeWindowMessageFilterEx(hwnd, WM_DROPFILES, MSGFLT_ALLOW, nullptr); - ChangeWindowMessageFilterEx(hwnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr); - ChangeWindowMessageFilterEx(hwnd, 0x0049, MSGFLT_ALLOW, nullptr); - - m_bCreated = true; - } - break; - - // NOTE: WM_PARENTNOTIFY was triggered by kernel process' graphic window creation. - case WM_PARENTNOTIFY: - { - switch(LOWORD(wParam)) - { - case WM_CREATE: - { - if (m_hwndChild == NULL) { - m_FPS_status = 0.0f; - m_MSpF_status = 0.0f; - m_LedSeq_status_block = (XBOX_LED_COLOUR_GREEN << 24) | - (XBOX_LED_COLOUR_GREEN << 16) | - (XBOX_LED_COLOUR_GREEN << 8) | - (XBOX_LED_COLOUR_GREEN); - SetTimer(hwnd, TIMERID_ACTIVE_EMULATION, 1000, (TIMERPROC)nullptr); - SetTimer(hwnd, TIMERID_LED, XBOX_LED_FLASH_PERIOD, (TIMERPROC)nullptr); - m_hwndChild = GetWindow(hwnd, GW_CHILD); - UpdateCaption(); - RefreshMenus(); - } - else { - m_hwndChild = GetWindow(hwnd, GW_CHILD); - } - } - break; - - case WM_DESTROY: - { - // (HWND)HIWORD(wParam) seems to be NULL, so we can't compare to m_hwndChild - if (m_hwndChild != NULL) { // Let's hope this signal originated from the only child window - KillTimer(hwnd, TIMERID_ACTIVE_EMULATION); - KillTimer(hwnd, TIMERID_LED); - m_hwndChild = NULL; - StopEmulation(); - DrawLedBitmap(hwnd, true); - } - } - case WM_COMMAND: - { - switch (HIWORD(wParam)) { - case ID_GUI_STATUS_LLE_FLAGS: - m_FlagsLLE_status = static_cast(lParam); - break; - - case ID_GUI_STATUS_XBOX_LED_COLOUR: - m_LedSeq_status_block = static_cast(lParam); - break; - - case ID_GUI_STATUS_LOG_ENABLED: - m_LogKrnl_status = static_cast(lParam); - UpdateLogStatus(); - break; - - // NOTE: If anything need to set before kernel process start do anything, do it here. - case ID_GUI_STATUS_KRNL_IS_READY: { - Crash_Manager_Data* pCMD = (Crash_Manager_Data*)malloc(sizeof(Crash_Manager_Data)); - pCMD->pWndMain = this; - pCMD->dwChildProcID = lParam; // lParam is process ID. - std::thread(CrashMonitorWrapper, pCMD).detach(); - - g_EmuShared->SetIsEmulating(true); // NOTE: Putting in here raise to low or medium risk due to debugger will launch itself. (Current workaround) - g_EmuShared->SetIsReady(true); - } - break; - } - } - break; - } - }; - break; // added per PVS suggestion. - - case WM_TIMER: - { - switch (wParam) - { - case TIMERID_ACTIVE_EMULATION: - { - RefreshAllStatus(); - } - break; - - case TIMERID_LED: - { - DrawLedBitmap(hwnd, false); - } - break; - } - } - break; - - case WM_SYSKEYDOWN: - { - if(m_hwndChild != NULL) - { - SendMessage(m_hwndChild, uMsg, wParam, lParam); - } - else - { - return DefWindowProc(hwnd, uMsg, wParam, lParam); - } - }; - break; // added per PVS suggestion. - - case WM_PAINT: - { - static bool s_bInitMenu = true; - - // initialize menus if they haven't been initialized already - if(s_bInitMenu) - { - UpdateRecentFiles(); - - RefreshMenus(); - - UpdateDebugConsoles(); - - s_bInitMenu = false; - } - - PAINTSTRUCT ps; - - BeginPaint(hwnd, &ps); - - HDC hDC = GetDC(hwnd); - - // draw splash / logo / status - { - static const int nLogoBmpW = 100, nLogoBmpH = 17; - - RECT bkRect = ps.rcPaint; - - bkRect.bottom -= nLogoBmpH + 10; - - FillRect(hDC, &bkRect, m_BackgroundColor); - - bkRect.top = bkRect.bottom; - bkRect.bottom += nLogoBmpH + 10; - - FillRect(hDC, &bkRect, m_Brushes[0]); - - BitBlt(hDC, m_w/2 - splashLogoWidth/2, m_h/2 - splashLogoHeight, splashLogoWidth, splashLogoHeight, m_SplashDC, 0, 0, SRCCOPY); - - BitBlt(hDC, m_w - gameLogoWidth - 3, m_h - nLogoBmpH - 12 - gameLogoHeight, gameLogoWidth, gameLogoHeight, m_GameLogoDC, 0, 0, SRCCOPY); - - BitBlt(hDC, m_w-nLogoBmpW-4, m_h-nLogoBmpH-4, nLogoBmpW, nLogoBmpH, m_LogoDC, 0, 0, SRCCOPY); - - int nHeight = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72); - - HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_ROMAN, "Verdana"); - - HGDIOBJ tmpObj = SelectObject(hDC, hFont); - - SetBkColor(hDC, RGB(0,0,0)); - - SetTextColor(hDC, RGB(255,255,255)); - - char buffer[255]; - - if(m_Xbe != nullptr && m_Xbe->HasError()) - sprintf(buffer, "%s Loaded!", m_Xbe->m_szAsciiTitle); - else - sprintf(buffer, "%s", "Disclaimer: Cxbx-Reloaded has no affiliation with Microsoft"); - - RECT rect = {0, m_h-15-5, m_w-100-4-69, m_h-5}; - - ExtTextOut(hDC, 4, m_h-15-5, ETO_OPAQUE, &rect, buffer, strlen(buffer), 0); - - SelectObject(hDC, tmpObj); - - DeleteObject(hFont); - } - - if(hDC != NULL) - ReleaseDC(hwnd, hDC); - - EndPaint(hwnd, &ps); - } - break; - - case WM_KEYDOWN: - { - switch(wParam) - { - case VK_F5: - { - // Start emulation normally - if (!m_bIsStarted) { - // Try to open the most recent Xbe if none is opened yet : - if (m_Xbe == nullptr) - OpenMRU(0); - - if (m_Xbe != nullptr) - StartEmulation(hwnd); - - break; - } - // fall through - } - - case VK_F6: - { - // Stop emulation - if (m_bIsStarted) - { - StopEmulation(); - break; - } - // fall through - } - - case VK_F7: - { - // Open the dashboard xbe - if (!m_bIsStarted) - { - if (m_Xbe != nullptr) { CloseXbe(); } - - OpenDashboard(); - break; - } - // fall through - } - - case VK_F9: - { - // Start emulation with the debugger - if (!m_bIsStarted) { - // Try to open the most recent Xbe if none is opened yet - if (m_Xbe == nullptr) - OpenMRU(0); - - if (m_Xbe != nullptr) - StartEmulation(hwnd, debuggerOn); - - break; - } - // fall through - } - - default: - { - if(m_hwndChild != NULL) - { - SendMessage(m_hwndChild, uMsg, wParam, lParam); - } - else - { - DefWindowProc(hwnd, uMsg, wParam, lParam); - } - } - } - } - break; - - case WM_DROPFILES: - { - if(!m_bIsStarted) { - char DroppedXbeFilename[MAX_PATH]; - DragQueryFile((HDROP)wParam, 0, DroppedXbeFilename, MAX_PATH); - OpenXbe(DroppedXbeFilename); - } - } - break; - - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case ID_FILE_OPEN_XBE: - { - OPENFILENAME ofn = { 0 }; - - char filename[MAX_PATH] = { 0 }; - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Xbox Executables (*.xbe)\0*.xbe\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - - if (GetOpenFileName(&ofn) == TRUE) - { - OpenXbe(ofn.lpstrFile); - } - } - break; - - case ID_FILE_CLOSE_XBE: - CloseXbe(); - break; - - - case ID_FILE_OPEN_DASHBOARD: - { - if (m_Xbe != nullptr) { CloseXbe(); } - - OpenDashboard(); - } - break; - - case ID_FILE_SAVEXBEFILE: - { - if (m_XbeFilename[0] == '\0') - SaveXbeAs(); - else - SaveXbe(m_XbeFilename); - } - break; - - case ID_FILE_SAVEXBEFILEAS: - SaveXbeAs(); - break; - - case ID_FILE_RXBE_0: - case ID_FILE_RXBE_1: - case ID_FILE_RXBE_2: - case ID_FILE_RXBE_3: - case ID_FILE_RXBE_4: - case ID_FILE_RXBE_5: - case ID_FILE_RXBE_6: - case ID_FILE_RXBE_7: - case ID_FILE_RXBE_8: - case ID_FILE_RXBE_9: - { - OpenMRU(LOWORD(wParam) - ID_FILE_RXBE_0); - } - break; - - case ID_FILE_EXIT: - SendMessage(hwnd, WM_CLOSE, 0, 0); - break; - - case ID_EDIT_LOGOBITMAP_EXPORT: - { - OPENFILENAME ofn = { 0 }; - - char filename[MAX_PATH] = "logo.bmp"; - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Bitmap Image Files (*.bmp)\0*.bmp\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.lpstrDefExt = "bmp"; - ofn.lpstrTitle = "Export Logo Bitmap"; - ofn.Flags = OFN_PATHMUSTEXIST; - - if (GetSaveFileName(&ofn) == TRUE) - { - // ask permission to overwrite if file already exists - if (_access(ofn.lpstrFile, 0) != -1) - { - if (PopupQuestionEx(m_hwnd, LOG_LEVEL::WARNING, PopupButtons::YesNo, PopupReturn::No, - "Overwrite existing file?") != PopupReturn::Yes) - return TRUE; - } - - // export logo bitmap - { - uint8_t i_gray[100 * 17]; - - m_Xbe->ExportLogoBitmap(i_gray); - - if (false == m_Xbe->HasError()) - { - FILE *LogoBitmap = fopen(ofn.lpstrFile, "wb"); - - // write bitmap header - { - BITMAPFILEHEADER bmfh; - - bmfh.bfType = *(uint16_t*)"BM"; - bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - sizeof(RGBQUAD) + (100 * 17) * sizeof(RGBTRIPLE) + 2; - bmfh.bfReserved1 = 0; - bmfh.bfReserved2 = 0; - bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - sizeof(RGBQUAD); - - fwrite(&bmfh, sizeof(bmfh), 1, LogoBitmap); - } - - // write bitmap info - { - BITMAPINFO bmi; - - bmi.bmiHeader.biSize = sizeof(BITMAPINFO) - sizeof(RGBQUAD); - bmi.bmiHeader.biWidth = 100; - bmi.bmiHeader.biHeight = -17; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 24; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = 0; - bmi.bmiHeader.biXPelsPerMeter = 0; - bmi.bmiHeader.biYPelsPerMeter = 0; - bmi.bmiHeader.biClrUsed = 0; - bmi.bmiHeader.biClrImportant = 0; - - fwrite(&bmi, sizeof(bmi) - 4, 1, LogoBitmap); - } - - // write bitmap data - { - RGBTRIPLE bmp_data[100 * 17]; - - for (uint32_t v = 0; v < 100 * 17; v++) - { - bmp_data[v].rgbtRed = i_gray[v]; - bmp_data[v].rgbtGreen = i_gray[v]; - bmp_data[v].rgbtBlue = i_gray[v]; - } - - fwrite(bmp_data, 100 * 17 * sizeof(RGBTRIPLE), 1, LogoBitmap); - } - - // write bitmap padding - { - uint16_t pad = 0; - - fwrite(&pad, 2, 1, LogoBitmap); - } - - fclose(LogoBitmap); - } - - if (m_Xbe->HasError()) - PopupError(m_hwnd, m_Xbe->GetError().c_str()); - else - { - char buffer[255]; - - sprintf(buffer, "%s's logo bitmap was successfully exported.", m_Xbe->m_szAsciiTitle); - - PopupInfo(m_hwnd, buffer); - } - } - } - } - break; - - case ID_EDIT_LOGOBITMAP_IMPORT: - { - OPENFILENAME ofn = { 0 }; - - char filename[MAX_PATH] = "*.bmp"; - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Bitmap Image Files (*.bmp)\0*.bmp\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.lpstrDefExt = "bmp"; - ofn.lpstrTitle = "Import Logo Bitmap"; - ofn.Flags = OFN_PATHMUSTEXIST; - - if (GetOpenFileName(&ofn) == TRUE) - { - // import logo bitmap - { - uint8_t i_gray[100 * 17]; - - // read bitmap file - { - FILE *logo = fopen(ofn.lpstrFile, "rb"); - - char *bmp_err = 0; - - // read bitmap header - if (!bmp_err) - { - BITMAPFILEHEADER bmfh; - - fread(&bmfh, sizeof(bmfh), 1, logo); - - if (bmfh.bfType != *(uint16_t*)"BM") - bmp_err = "Invalid bitmap file...\n\nonly allows 24 bit bitmaps (100 by 17 pixels) with row order swapped"; - else if (bmfh.bfSize != sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - sizeof(RGBQUAD) + (100 * 17) * sizeof(RGBTRIPLE) + 2) - bmp_err = "Invalid bitmap file...\n\nonly allows 24 bit bitmaps (100 by 17 pixels) with row order swapped"; - } - - // read bitmap info - if (!bmp_err) - { - BITMAPINFO bmi; - - fread(&bmi, sizeof(bmi) - 4, 1, logo); - - if (bmi.bmiHeader.biWidth != 100 || bmi.bmiHeader.biHeight != -17) - bmp_err = "Invalid bitmap file...\n\nonly allows 24 bit bitmaps (100 by 17 pixels) with row order swapped"; - } - - // read bitmap data - if (!bmp_err) - { - RGBTRIPLE bmp_data[100 * 17]; - - fread(bmp_data, 100 * 17 * sizeof(RGBTRIPLE), 1, logo); - - for (uint32_t c = 0; c < 100 * 17; c++) - i_gray[c] = (char)(((float)bmp_data[c].rgbtRed + (float)bmp_data[c].rgbtGreen + (float)bmp_data[c].rgbtBlue) / 3.0); - } - - fclose(logo); - - if (bmp_err != 0) - { - PopupError(m_hwnd, bmp_err); - break; - } - } - - m_Xbe->ImportLogoBitmap(i_gray); - - if (m_Xbe->HasError()) - { - PopupError(m_hwnd, m_Xbe->GetError().c_str()); - - if (m_Xbe->HasFatalError()) - { - CloseXbe(); - } - else - { - m_Xbe->ClearError(); - } - - } - else - { - m_bXbeChanged = true; - - LoadLogo(); - - char buffer[255]; - - sprintf(buffer, "%s's logo bitmap was successfully updated.", m_Xbe->m_szAsciiTitle); - - PopupInfo(m_hwnd, buffer); - } - } - } - } - break; - - case ID_EDIT_PATCH_ALLOW64MB: - { - m_bXbeChanged = true; - - m_Xbe->m_Header.dwInitFlags.bLimit64MB = !m_Xbe->m_Header.dwInitFlags.bLimit64MB; - - RefreshMenus(); - - if (m_Xbe->m_Header.dwInitFlags.bLimit64MB) - printf("WndMain: %s was patched to limit to 64MB of memory usage.\n", m_Xbe->m_szAsciiTitle); - else - printf("WndMain: %s was patched to allow >64MB of memory usage.\n", m_Xbe->m_szAsciiTitle); - } - break; - - case ID_EDIT_PATCH_DEBUGMODE: - { - m_bXbeChanged = true; - - // patch to/from debug mode - if ((m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL) > 0x01000000) - { - // we're in debug mode, so switch over to retail - uint32_t ep = m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL; // decode from debug mode - uint32_t kt = m_Xbe->m_Header.dwKernelImageThunkAddr ^ XOR_KT_DEBUG; // decode from debug mode - - m_Xbe->m_Header.dwEntryAddr = ep ^ XOR_EP_DEBUG; // encode to retail mode - m_Xbe->m_Header.dwKernelImageThunkAddr = kt ^ XOR_KT_RETAIL; // encode to retail mode - } - else - { - // we're in retail mode, so switch to debug - uint32_t ep = m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_DEBUG; // decode from retail mode - uint32_t kt = m_Xbe->m_Header.dwKernelImageThunkAddr ^ XOR_KT_RETAIL; // decode from retail mode - - m_Xbe->m_Header.dwEntryAddr = ep ^ XOR_EP_RETAIL; // encode to debug mode - m_Xbe->m_Header.dwKernelImageThunkAddr = kt ^ XOR_KT_DEBUG; // encode to debug mode - } - - RefreshMenus(); - - bool res = (m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL) > 0x01000000; - - if (res) - printf("WndMain: %s was converted to debug mode.\n", m_Xbe->m_szAsciiTitle); - else - printf("WndMain: %s was converted to retail mode.\n", m_Xbe->m_szAsciiTitle); - } - break; - - case ID_EDIT_DUMPXBEINFOTO_FILE: - { - OPENFILENAME ofn = { 0 }; - - char filename[MAX_PATH] = "Xbe.txt"; - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Text Documents (*.txt)\0*.txt\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.lpstrDefExt = "txt"; - ofn.Flags = OFN_PATHMUSTEXIST; - - if (GetSaveFileName(&ofn) == TRUE) - { - // ask permission to overwrite if file exists - if (_access(ofn.lpstrFile, 0) != -1) - { - if (PopupQuestion(m_hwnd, "Overwrite existing file?") != PopupReturn::Yes) - return TRUE; - } - - // dump xbe information to file - { - std::string Xbe_info = DumpInformation(m_Xbe); - if (m_Xbe->HasError()) { - PopupError(m_hwnd, m_Xbe->GetError().c_str()); - } - else { - std::ofstream Xbe_dump_file(ofn.lpstrFile); - if (Xbe_dump_file.is_open()) { - Xbe_dump_file << Xbe_info; - Xbe_dump_file.close(); - char buffer[255]; - sprintf(buffer, "%s's .xbe info was successfully dumped.", m_Xbe->m_szAsciiTitle); - PopupInfo(m_hwnd, buffer); - } - else { - PopupError(m_hwnd, "Could not open Xbe text file."); - } - } - } - } - } - break; - - case ID_EDIT_DUMPXBEINFOTO_DEBUGCONSOLE: - { - std::string Xbe_info = DumpInformation(m_Xbe); - if (m_Xbe->HasError()) { - PopupError(m_hwnd, m_Xbe->GetError().c_str()); - } - else { - std::cout << Xbe_info; - char buffer[255]; - sprintf(buffer, "%s's .xbe info was successfully dumped to console.", m_Xbe->m_szAsciiTitle); - printf("WndMain: %s\n", buffer); - } - } - break; - - case ID_SETTINGS_CONFIG_INPUT: - ShowInputConfig(hwnd, m_hwndChild); - break; - - case ID_SETTINGS_CONFIG_VIDEO: - ShowVideoConfig(hwnd); - break; - - case ID_SETTINGS_CONFIG_AUDIO: - ShowAudioConfig(hwnd); - break; - - case ID_SETTINGS_CONFIG_NETWORK: - ShowNetworkConfig(hwnd); - break; - - case ID_SETTINGS_CONFIG_EEPROM: - { - if (m_bIsStarted) { - // We don't allow changing the contents of the eeprom while a game is running, mostly because we lack a "pause emulation" - // function necessary to modify the contents safely (the game itself can modify the eeprom) - PopupError(hwnd, "Cannot modify eeprom file while a title is running"); - break; - } - ShowEepromConfig(hwnd); - } - break; - - case ID_SETTINGS_CONFIG_LOGGING: - { - ShowLoggingConfig(hwnd, m_hwndChild); - } - break; - - case ID_SETTINGS_CONFIG_DLOCCUSTOM: - { - char szDir[MAX_PATH]; - - BROWSEINFO bInfo; - bInfo.hwndOwner = NULL; - bInfo.pidlRoot = nullptr; - bInfo.pszDisplayName = szDir; - bInfo.lpszTitle = "Please, select a folder"; - bInfo.ulFlags = BIF_NEWDIALOGSTYLE, BIF_EDITBOX, BIF_VALIDATE; - bInfo.lpfn = nullptr; - bInfo.lParam = 0; - bInfo.iImage = -1; - - LPITEMIDLIST lpItem = SHBrowseForFolder(&bInfo); - - if (lpItem != nullptr) - { - SHGetPathFromIDList(lpItem, szDir); - - // -14 is for \\Cxbx-Reloaded string to be include later down below. - size_t szLen = strnlen(szDir, MAX_PATH - 14); - if (szLen == 0) { - PopupError(hwnd, "You've selected an invalid folder... Go back and try again."); - break; - } - else if (szLen == MAX_PATH - 14) { - PopupError(hwnd, "You've selected a folder path which is too long... Go back and try again."); - break; - } - - std::string szDirTemp = std::string(szDir) + std::string("\\Cxbx-Reloaded"); - - if (szDirTemp.size() > MAX_PATH) { - PopupError(hwnd, "Directory path is too long. Go back and choose a shorter path."); - break; - } - - int result = SHCreateDirectoryEx(nullptr, szDirTemp.c_str(), nullptr); - if ((result != ERROR_SUCCESS) && (result != ERROR_ALREADY_EXISTS)) { - PopupError(hwnd, "You don't have write permissions on that directory..."); - break; - } - - g_Settings->m_gui.DataStorageToggle = CXBX_DATA_CUSTOM; - g_Settings->m_gui.szCustomLocation = szDirTemp; - RefreshMenus(); - } - } - break; - - case ID_SETTINGS_CONFIG_DLOCAPPDATA: - { - g_Settings->m_gui.DataStorageToggle = CXBX_DATA_APPDATA; - RefreshMenus(); - } - break; - - case ID_SETTINGS_CONFIG_DLOCEXECDIR: - { - g_Settings->m_gui.DataStorageToggle = CXBX_DATA_EXECDIR; - RefreshMenus(); - } - break; - - case ID_CACHE_CLEARHLECACHE_ALL: - { - ClearSymbolCache(g_Settings->GetDataLocation().c_str()); - PopupInfo(m_hwnd, "The entire Symbol Cache has been cleared."); - } - break; - - case ID_CACHE_CLEARHLECACHE_CURRENT: - { - std::string cacheDir = g_Settings->GetDataLocation() + "\\SymbolCache\\"; - - // Hash the loaded XBE's header, use it as a filename - uint64_t uiHash = ComputeHash((void*)&m_Xbe->m_Header, sizeof(Xbe::Header)); - std::stringstream sstream; - std::string szTitleName(m_Xbe->m_szAsciiTitle); - m_Xbe->PurgeBadChar(szTitleName); - sstream << cacheDir << szTitleName << "-" << std::hex << uiHash << ".ini"; - std::string fullpath = sstream.str(); - - if (std::filesystem::remove(fullpath)) { - PopupInfo(m_hwnd, "This title's Symbol Cache entry has been cleared."); - } - } - break; - - case ID_SETTINGS_INITIALIZE: - { - PopupReturn ret = PopupWarningEx(m_hwnd, PopupButtons::YesNo, PopupReturn::No, - "Warning: This will reset all Cxbx-Reloaded settings to their default values.\nAre you sure you want to proceed?", "Cxbx-Reloaded"); - - if (ret == PopupReturn::Yes) { - InitializeSettings(); - PopupInfo(m_hwnd, "Cxbx-Reloaded has been initialized and will now close."); - SendMessage(hwnd, WM_CLOSE, 0, 0); - } - } - break; - - case ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE: - { - if (g_Settings->m_core.KrnlDebugMode == DM_NONE || g_Settings->m_core.KrnlDebugMode == DM_FILE) { - g_Settings->m_core.KrnlDebugMode = DM_CONSOLE; - } - else { - g_Settings->m_core.KrnlDebugMode = DM_NONE; - } - PopupInfo(m_hwnd, "This will not take effect until the next time emulation is started."); - - RefreshMenus(); - - UpdateDebugConsoles(); - } - break; - - case ID_EMULATION_DEBUGOUTPUTKERNEL_FILE: - { - if (g_Settings->m_core.KrnlDebugMode == DM_FILE) { - g_Settings->m_core.KrnlDebugMode = DM_NONE; - - RefreshMenus(); - - UpdateDebugConsoles(); - } - else - { - OPENFILENAME ofn = { 0 }; - - char filename[MAX_PATH] = "KrnlDebug.txt"; - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Text Documents (*.txt)\0*.txt\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.lpstrDefExt = "txt"; - ofn.Flags = OFN_PATHMUSTEXIST; - - if (GetSaveFileName(&ofn) != FALSE) - { - PopupInfo(m_hwnd, "This will not take effect until emulation is (re)started."); - - strncpy(g_Settings->m_core.szKrnlDebug, ofn.lpstrFile, MAX_PATH - 1); - - g_Settings->m_core.KrnlDebugMode = DM_FILE; - - RefreshMenus(); - - UpdateDebugConsoles(); - } - } - } - break; - - case ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE: - { - if (g_Settings->m_gui.CxbxDebugMode == DM_NONE || g_Settings->m_gui.CxbxDebugMode == DM_FILE) { - g_Settings->m_gui.CxbxDebugMode = DM_CONSOLE; - } - else { - g_Settings->m_gui.CxbxDebugMode = DM_NONE; - } - RefreshMenus(); - - UpdateDebugConsoles(); - } - break; - - case ID_EMULATION_DEBUGOUTPUTGUI_FILE: - { - if (g_Settings->m_gui.CxbxDebugMode == DM_FILE) - { - g_Settings->m_gui.CxbxDebugMode = DM_NONE; - - RefreshMenus(); - - UpdateDebugConsoles(); - } - else - { - OPENFILENAME ofn = { 0 }; - - char filename[MAX_PATH] = "CxbxDebug.txt"; - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Text Documents (*.txt)\0*.txt\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.lpstrDefExt = "txt"; - ofn.Flags = OFN_PATHMUSTEXIST; - - if (GetSaveFileName(&ofn) != FALSE) - { - g_Settings->m_gui.szCxbxDebugFile = ofn.lpstrFile; - - g_Settings->m_gui.CxbxDebugMode = DM_FILE; - - RefreshMenus(); - - UpdateDebugConsoles(); - } - - } - } - break; - - case ID_EMULATION_LLE_JIT: - { - g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_JIT; - RefreshMenus(); - } - break; - - case ID_EMULATION_LLE_APU: - { - g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_APU; - RefreshMenus(); - } - break; - - case ID_EMULATION_LLE_GPU: - { - g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_GPU; - RefreshMenus(); - } - break; -#if 0 // Reenable this when LLE USB actually works - case ID_EMULATION_LLE_USB: - { - g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_USB; - RefreshMenus(); - } - break; -#endif - case ID_USELOADEREXEC: - { - g_Settings->m_core.bUseLoaderExec = !g_Settings->m_core.bUseLoaderExec; - RefreshMenus(); - } - break; - - case ID_EMULATION_START: - if (m_Xbe != nullptr) - { - StartEmulation(hwnd); - } - break; - - case ID_EMULATION_STARTDEBUGGER: - if (m_Xbe != nullptr) - { - StartEmulation(hwnd, debuggerOn); - } - break; - - case ID_EMULATION_STOP: - StopEmulation(); - break; - - case ID_HACKS_DISABLEPIXELSHADERS: - g_Settings->m_hacks.DisablePixelShaders = !g_Settings->m_hacks.DisablePixelShaders; - RefreshMenus(); - break; - - case ID_HACKS_RUNXBOXTHREADSONALLCORES: - if (g_Settings->m_hacks.UseAllCores == false) { - PopupReturn ret = PopupWarningEx(hwnd, PopupButtons::YesNo, PopupReturn::No, - "Activating this hack will make the emulator more likely to crash and/or hang." - "\nPlease do not report issues with games while this hack is active. Are you sure you want to turn it on?"); - if (ret != PopupReturn::Yes) { - break; - } - } - g_Settings->m_hacks.UseAllCores = !g_Settings->m_hacks.UseAllCores; - RefreshMenus(); - break; - - case ID_HACKS_SKIPRDTSCPATCHING: - g_Settings->m_hacks.SkipRdtscPatching = !g_Settings->m_hacks.SkipRdtscPatching; - RefreshMenus(); - break; - - case ID_SETTINGS_IGNOREINVALIDXBESIG: - g_Settings->m_gui.bIgnoreInvalidXbeSig = !g_Settings->m_gui.bIgnoreInvalidXbeSig; - RefreshMenus(); - break; - - case ID_SETTINGS_IGNOREINVALIDXBESEC: - g_Settings->m_gui.bIgnoreInvalidXbeSec = !g_Settings->m_gui.bIgnoreInvalidXbeSec; - RefreshMenus(); - break; - - case ID_SETTINGS_ALLOWADMINPRIVILEGE: - g_Settings->m_core.allowAdminPrivilege = !g_Settings->m_core.allowAdminPrivilege; - RefreshMenus(); - break; - - case ID_HELP_ABOUT: - { - ShowAboutDialog(hwnd); - } - break; - - case ID_HELP_HOMEPAGE: - ShellExecute(NULL, "open", "https://github.com/Cxbx-Reloaded/Cxbx-Reloaded", nullptr, nullptr, SW_SHOWNORMAL); - break; - - } - - break; - } - - case WM_MOVE: - { - // Redraw the window on move, prevents corrupt background image that happens - // when windows doesn't call the WM_DRAW event when the window is moved too quickly. - RedrawWindow(hwnd, nullptr, NULL, RDW_INVALIDATE); - break; - } - - case WM_CLOSE: - { - if(m_Xbe != nullptr) - CloseXbe(); - - if(m_Xbe == nullptr) - DestroyWindow(hwnd); - } - break; - - case WM_DESTROY: - { - FreeConsole(); - - HDC hDC = GetDC(hwnd); - - SelectObject(m_LogoDC, m_OrigLogo); - - SelectObject(m_SplashDC, m_OrigBmp); - - SelectObject(m_GameLogoDC, m_OrigGameLogo); - - SelectObject(m_LedDC, m_OriLed); - - DeleteObject(m_LogoDC); - - DeleteObject(m_SplashDC); - - DeleteObject(m_GameLogoDC); - - DeleteObject(m_LedDC); - - DeleteObject(m_LogoBmp); - - DeleteObject(m_SplashBmp); - - DeleteObject(m_BackgroundColor); - - DeleteObject(m_GameLogoBMP); - - DeleteObject(m_LedBmp); - - DeleteObject(m_Brushes[XBOX_LED_COLOUR_OFF]); - - DeleteObject(m_Brushes[XBOX_LED_COLOUR_GREEN]); - - DeleteObject(m_Brushes[XBOX_LED_COLOUR_RED]); - - DeleteObject(m_Brushes[XBOX_LED_COLOUR_ORANGE]); - - DeleteObject(m_Pens[XBOX_LED_COLOUR_OFF]); - - DeleteObject(m_Pens[XBOX_LED_COLOUR_GREEN]); - - DeleteObject(m_Pens[XBOX_LED_COLOUR_RED]); - - DeleteObject(m_Pens[XBOX_LED_COLOUR_ORANGE]); - - ReleaseDC(hwnd, hDC); - - delete m_Xbe; - - m_Xbe = nullptr; - - PostQuitMessage(0); - } - break; - - default: - return DefWindowProc(hwnd, uMsg, wParam, lParam); - } - - return 0; -} - -// suggest a file name -void WndMain::SuggestFilename(const char *x_orig_filename, char *x_filename, char x_extension[4]) -{ - if(strrchr(x_orig_filename, '\\') != nullptr) - { - strcpy(x_filename, x_orig_filename); - char *loc = strrchr(x_filename, '.'); - if (loc != nullptr) - strncpy(loc, &x_extension[0], 4); - } -} - -// occurs when an xbe is loaded.. -void WndMain::XbeLoaded() -{ - LoadLogo(); - - LoadGameLogo(); - - UpdateCaption(); - RefreshMenus(); - - InvalidateRgn(m_hwnd, NULL, TRUE); - - printf("WndMain: %s loaded.\n", m_Xbe->m_szAsciiTitle); -} - -// load logo bitmap -void WndMain::LoadLogo() -{ - uint8_t i_gray[100*17]; - - m_Xbe->ExportLogoBitmap(i_gray); - - if(m_Xbe->HasError()) - { - PopupError(m_hwnd, m_Xbe->GetError().c_str()); - - if (m_Xbe->HasFatalError()) - { - CloseXbe(); - } - - return; - } - - uint32_t v=0; - for(uint32_t y=0;y<17;y++) - { - for(uint32_t x=0;x<100;x++) - { - SetPixel(m_LogoDC, x, y, RGB(i_gray[v], i_gray[v], i_gray[v])); - v++; - } - } - - RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); -} - -// TODO : Move these types to a more appropriate place -// Source : https://msdn.microsoft.com/en-us/library/windows/desktop/bb943984(v=vs.85).aspx -struct DDS_PIXELFORMAT { - DWORD dwSize; - DWORD dwFlags; - DWORD dwFourCC; - DWORD dwRGBBitCount; - DWORD dwRBitMask; - DWORD dwGBitMask; - DWORD dwBBitMask; - DWORD dwABitMask; -}; - -// Source : https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx -typedef struct { - DWORD dwSize; - DWORD dwFlags; - DWORD dwHeight; - DWORD dwWidth; - DWORD dwPitchOrLinearSize; - DWORD dwDepth; - DWORD dwMipMapCount; - DWORD dwReserved1[11]; - DDS_PIXELFORMAT ddspf; - DWORD dwCaps; - DWORD dwCaps2; - DWORD dwCaps3; - DWORD dwCaps4; - DWORD dwReserved2; -} DDS_HEADER; - -// load game logo bitmap -void WndMain::LoadGameLogo() -{ - // Export Game Logo bitmap (XTIMAG or XSIMAG) - uint8_t *pSection = (uint8_t *)m_Xbe->FindSection("$$XTIMAGE"); // Check for XTIMAGE - if (!pSection) { - pSection = (uint8_t *)m_Xbe->FindSection("$$XSIMAGE"); // if XTIMAGE isn't present, check for XSIMAGE (smaller) - if (!pSection) { - return; - } - } - - gameLogoWidth = 0; - gameLogoHeight = 0; - - uint8_t *ImageData = nullptr; - XTL::X_D3DPixelContainer XboxPixelContainer = {}; - XTL::X_D3DPixelContainer *pXboxPixelContainer = &XboxPixelContainer; - - switch (*(DWORD*)pSection) { - case MAKEFOURCC('D', 'D', 'S', ' '): { - DDS_HEADER *pDDSHeader = (DDS_HEADER *)(pSection + sizeof(DWORD)); - D3DFORMAT Format = D3DFMT_UNKNOWN; - if (pDDSHeader->ddspf.dwFlags & DDPF_FOURCC) { - switch (pDDSHeader->ddspf.dwFourCC) { - case MAKEFOURCC('D', 'X', 'T', '1'): Format = D3DFMT_DXT1; break; - case MAKEFOURCC('D', 'X', 'T', '3'): Format = D3DFMT_DXT3; break; - case MAKEFOURCC('D', 'X', 'T', '5'): Format = D3DFMT_DXT5; break; - } - } - else { - // TODO : Determine D3D format based on pDDSHeader->ddspf.dwABitMask, .dwRBitMask, .dwGBitMask and .dwBBitMask - } - - if (Format == D3DFMT_UNKNOWN) - return; - - ImageData = (uint8_t *)(pSection + sizeof(DWORD) + pDDSHeader->dwSize); - //gameLogoHeight = pDDSHeader->dwHeight; - //gameLogoWidth = pDDSHeader->dwWidth; - - // TODO : Use PixelCopy code here to decode. For now, fake it : - CxbxSetPixelContainerHeader(&XboxPixelContainer, - 0, // Common - could be X_D3DCOMMON_TYPE_TEXTURE - (UINT)pDDSHeader->dwWidth, - (UINT)pDDSHeader->dwHeight, - 1, - EmuPC2XB_D3DFormat(Format), - 2, - (UINT)pDDSHeader->dwPitchOrLinearSize); - break; - } - case MAKEFOURCC('X', 'P', 'R', '0'): - case MAKEFOURCC('X', 'P', 'R', '1'): { - struct Xbe::XprHeader *pXprHeader = (struct Xbe::XprHeader*)pSection; - - unsigned int SizeOfResourceHeaders = pXprHeader->dwXprHeaderSize - sizeof(Xbe::XprHeader); - unsigned int SizeOfResourceData = pXprHeader->dwXprTotalSize - pXprHeader->dwXprHeaderSize; - - uint8_t *ResourceHeaders = pSection + sizeof(Xbe::XprHeader); - uint8_t *ResourceData = ResourceHeaders + SizeOfResourceHeaders; - - pXboxPixelContainer = (XTL::X_D3DPixelContainer*)ResourceHeaders; - ImageData = ResourceData; - - break; - } - default: { - return; - } - } - - void *bitmapData = ConvertD3DTextureToARGB(pXboxPixelContainer, ImageData, &gameLogoWidth, &gameLogoHeight); - if (!bitmapData) - return; - - HDC hDC = GetDC(m_hwnd); - m_GameLogoBMP = CreateCompatibleBitmap(hDC, gameLogoWidth, gameLogoHeight); - - // create bitmap - { - BITMAPINFO BmpInfo; - - BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFO) - sizeof(RGBQUAD); - BmpInfo.bmiHeader.biWidth = gameLogoWidth; - BmpInfo.bmiHeader.biHeight = 0 - (long)gameLogoHeight; // If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner. - BmpInfo.bmiHeader.biPlanes = 1; - BmpInfo.bmiHeader.biBitCount = 32; - BmpInfo.bmiHeader.biCompression = BI_RGB; - BmpInfo.bmiHeader.biSizeImage = 0; - BmpInfo.bmiHeader.biXPelsPerMeter = 0; - BmpInfo.bmiHeader.biYPelsPerMeter = 0; - BmpInfo.bmiHeader.biClrUsed = 0; - BmpInfo.bmiHeader.biClrImportant = 0; - - SetDIBits(hDC, m_GameLogoBMP, 0, gameLogoHeight, bitmapData, &BmpInfo, DIB_RGB_COLORS); - } - - m_GameLogoDC = CreateCompatibleDC(hDC); - m_OrigGameLogo = (HBITMAP)SelectObject(m_GameLogoDC, m_GameLogoBMP); - - RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); - - if (hDC != NULL) - ReleaseDC(m_hwnd, hDC); - - free(bitmapData); -} - - -// refresh menu items -void WndMain::RefreshMenus() -{ - bool XbeLoaded = (m_Xbe != nullptr); - bool Running = (m_hwndChild != NULL); // TODO : Use m_bIsStarted? - UINT MF_WhenXbeLoaded = XbeLoaded ? MF_ENABLED : MF_GRAYED; - UINT MF_WhenXbeLoadedNotRunning = (XbeLoaded && !Running) ? MF_ENABLED : MF_GRAYED; - UINT MF_WhenXbeLoadedAndRunning = (XbeLoaded && Running) ? MF_ENABLED : MF_GRAYED; - - // disable/enable appropriate menus - { - HMENU menu = GetMenu(m_hwnd); - - // file menu - { - HMENU file_menu = GetSubMenu(menu, 0); - - // enable/disable close .xbe file - EnableMenuItem(file_menu, ID_FILE_CLOSE_XBE, MF_BYCOMMAND | MF_WhenXbeLoaded); - - // enable/disable save .xbe file - EnableMenuItem(file_menu, ID_FILE_SAVEXBEFILE, MF_BYCOMMAND | MF_WhenXbeLoaded); - - // enable/disable save .xbe file as - EnableMenuItem(file_menu, ID_FILE_SAVEXBEFILEAS, MF_BYCOMMAND | MF_WhenXbeLoaded); - - // recent xbe files menu - { - HMENU rxbe_menu = GetSubMenu(file_menu, 7); - - int max = m_dwRecentXbe; - for(int v=0;v64 MB" if appropriate - if(m_Xbe != nullptr) - { - UINT chk_flag = (m_Xbe->m_Header.dwInitFlags.bLimit64MB) ? MF_UNCHECKED : MF_CHECKED; - - CheckMenuItem(pach_menu, ID_EDIT_PATCH_ALLOW64MB, chk_flag); - } - - // check "debug mode" if appropriate - if(m_Xbe != nullptr) - { - UINT chk_flag = ((m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL) > 0x01000000) ? MF_CHECKED : MF_UNCHECKED; - - CheckMenuItem(pach_menu, ID_EDIT_PATCH_DEBUGMODE, chk_flag); - } - } - } - - // view menu - { - HMENU view_menu = GetSubMenu(menu, 2); - HMENU emul_debg = GetSubMenu(view_menu, 0); - HMENU emul_krnl = GetSubMenu(view_menu, 1); - - switch (g_Settings->m_core.KrnlDebugMode) { - case DM_CONSOLE: - CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE, MF_CHECKED); - CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_FILE, MF_UNCHECKED); - break; - - case DM_FILE: - CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE, MF_UNCHECKED); - CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_FILE, MF_CHECKED); - break; - - default: - CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE, MF_UNCHECKED); - CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_FILE, MF_UNCHECKED); - break; - } - - switch (g_Settings->m_gui.CxbxDebugMode) { - case DM_CONSOLE: - CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE, MF_CHECKED); - CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_FILE, MF_UNCHECKED); - break; - - case DM_FILE: - CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE, MF_UNCHECKED); - CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_FILE, MF_CHECKED); - break; - - default: - CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE, MF_UNCHECKED); - CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_FILE, MF_UNCHECKED); - break; - } - UpdateLogStatus(); - } - - // settings menu - { - HMENU settings_menu = GetSubMenu(menu, 3); - - // enable/disable clear current hle cache - EnableMenuItem(settings_menu, ID_CACHE_CLEARHLECACHE_CURRENT, MF_BYCOMMAND | MF_WhenXbeLoadedNotRunning); - - UINT chk_flag = (g_Settings->m_core.FlagsLLE & LLE_JIT) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_EMULATION_LLE_JIT, chk_flag); - - chk_flag = (g_Settings->m_core.FlagsLLE & LLE_APU) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_EMULATION_LLE_APU, chk_flag); - - chk_flag = (g_Settings->m_core.FlagsLLE & LLE_GPU) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_EMULATION_LLE_GPU, chk_flag); - - //chk_flag = (g_Settings->m_core.FlagsLLE & LLE_USB) ? MF_CHECKED : MF_UNCHECKED; // Reenable this when LLE USB actually works - //CheckMenuItem(settings_menu, ID_EMULATION_LLE_USB, chk_flag); - - chk_flag = g_Settings->m_core.bUseLoaderExec ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_USELOADEREXEC, chk_flag); - - chk_flag = (g_Settings->m_hacks.DisablePixelShaders) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_HACKS_DISABLEPIXELSHADERS, chk_flag); - - chk_flag = (g_Settings->m_hacks.UseAllCores) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_HACKS_RUNXBOXTHREADSONALLCORES, chk_flag); - - chk_flag = (g_Settings->m_hacks.SkipRdtscPatching) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_HACKS_SKIPRDTSCPATCHING, chk_flag); - - switch (g_Settings->m_gui.DataStorageToggle) { - case CXBX_DATA_APPDATA: - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_CHECKED); - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCEXECDIR, MF_UNCHECKED); - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCCUSTOM, MF_UNCHECKED); - break; - - case CXBX_DATA_EXECDIR: - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_UNCHECKED); - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCEXECDIR, MF_CHECKED); - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCCUSTOM, MF_UNCHECKED); - break; - - case CXBX_DATA_CUSTOM: - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_UNCHECKED); - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCEXECDIR, MF_UNCHECKED); - CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCCUSTOM, MF_CHECKED); - break; - } - - chk_flag = (g_Settings->m_gui.bIgnoreInvalidXbeSig) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_SETTINGS_IGNOREINVALIDXBESIG, chk_flag); - - chk_flag = (g_Settings->m_gui.bIgnoreInvalidXbeSec) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_SETTINGS_IGNOREINVALIDXBESEC, chk_flag); - - chk_flag = (g_Settings->m_core.allowAdminPrivilege) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_SETTINGS_ALLOWADMINPRIVILEGE, chk_flag); - } - - // emulation menu - { - HMENU emul_menu = GetSubMenu(menu, 4); - - // enable emulation start - EnableMenuItem(emul_menu, ID_EMULATION_START, MF_BYCOMMAND | MF_WhenXbeLoadedNotRunning); - - // enable emulation with debugging - EnableMenuItem(emul_menu, ID_EMULATION_STARTDEBUGGER, MF_BYCOMMAND | MF_WhenXbeLoadedNotRunning); - - // enable emulation stop - EnableMenuItem(emul_menu, ID_EMULATION_STOP, MF_BYCOMMAND | MF_WhenXbeLoadedAndRunning); - } - } - // NOTE: Must force draw menu bar since sometime status doesn't show the new change. - DrawMenuBar(m_hwnd); -} - -// update debug consoles -void WndMain::UpdateDebugConsoles() -{ -#ifdef _WINDOWS_ - HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); -#endif - switch (g_Settings->m_gui.CxbxDebugMode) { - case DM_CONSOLE: - if (AllocConsole()) { - std::freopen("CONOUT$", "wt", stdout); - - SetConsoleTitle("Cxbx-Reloaded : Debug Console"); - - SetConsoleTextAttribute(stdHandle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); - - std::printf("%s", "WndMain: Debug console allocated.\n"); - - SetForegroundWindow(m_hwnd); - } - break; - - case DM_FILE: - - std::freopen(g_Settings->m_gui.szCxbxDebugFile.c_str(), "wt", stdout); - FreeConsole(); - - std::printf("%s", "WndMain: Debug console allocated.\n"); - break; - - default: - - if (GetConsoleWindow() != NULL) { - std::fclose(stdout); - FreeConsole(); - } - std::freopen("nul", "w", stdout); - - break; - } - - // NOTE: This is a Windows fix for ability to get std::cout to output onto console/file. - // Not sure if linux/unix is affected too. -#ifdef _WINDOWS_ - if (stdHandle != INVALID_HANDLE_VALUE) { - int fileDesc = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); - - if (fileDesc != -1) { - FILE* file = _fdopen(fileDesc, "wt"); - - if (file != nullptr) { - int dup2Result = _dup2(_fileno(file), _fileno(stdout)); - if (dup2Result == 0) { - std::setvbuf(stdout, nullptr, _IONBF, 0); - } - std::fclose(file); - } - } - } - std::wcout.clear(); - std::cout.clear(); -#endif -} - -// update recent files menu -void WndMain::UpdateRecentFiles() -{ - HMENU FileMenu = GetSubMenu(GetMenu(m_hwnd), 0); - HMENU RXbeMenu = GetSubMenu(FileMenu, 7); - - // clear existing menu items - { - int v, max; - - max = GetMenuItemCount(RXbeMenu); - for(v=0;vm_gui.szRecentXbeFiles[v].c_str()); - AppendMenu(RXbeMenu, MF_STRING, ID_FILE_RXBE_0 + v, szBuffer); - } - } -} - -extern std::string FormatTitleId(uint32_t title_id); - -void WndMain::UpdateCaption() -{ - char AsciiTitle[MAX_PATH]; - - int i = sprintf(AsciiTitle, "Cxbx-Reloaded %s", CxbxVersionStr); - if (m_Xbe != nullptr) { - if (m_bIsStarted) { - i += sprintf(AsciiTitle + i, " : Emulating "); - } - else { - i += sprintf(AsciiTitle + i, " : Loaded "); - } - - i += sprintf(AsciiTitle + i, "%s v1.%02d (%s)", FormatTitleId(m_Xbe->m_Certificate.dwTitleId).c_str(), m_Xbe->m_Certificate.dwVersion, m_Xbe->m_szAsciiTitle); - - UpdateFpsStatus(); - UpdateLogStatus(); - - } - - SetWindowText(m_hwnd, AsciiTitle); -} - -void WndMain::UpdateFpsStatus() -{ - // Append FPS menu text - HMENU hMenu = GetMenu(m_hwnd); - MENUITEMINFO mii; - mii.cbSize = sizeof mii; - mii.fMask = MIIM_STRING; - char sMenu[32]; - mii.dwTypeData = &sMenu[0]; - - if (m_bIsStarted) { - if (g_EmuShared != nullptr) { - g_EmuShared->GetCurrentFPS(&m_FPS_status); - - if (m_FPS_status == 0.0f) { - m_MSpF_status = 0.0f; - } - else { - m_MSpF_status = (float)(1000.0 / m_FPS_status); - } - std::sprintf(sMenu, "FPS: %.2f MS / F : %.2f", m_FPS_status, m_MSpF_status); - } - } - else { - // Hide FPS if we're not currently emulating - std::sprintf(sMenu, " "); - } - - SetMenuItemInfo(hMenu, ID_FPS, FALSE, &mii); -} - -void WndMain::UpdateLogStatus() -{ - // Append FPS menu text - char sMenu[32]; - HMENU hMenu = GetMenu(m_hwnd); - MENUITEMINFO mii; - mii.cbSize = sizeof mii; - mii.fMask = MIIM_STRING; - mii.dwTypeData = &sMenu[0]; - - std::strcpy(sMenu, "LOG:"); - - if (g_Settings->m_gui.CxbxDebugMode != DebugMode::DM_NONE) { - std::strcat(sMenu, "G"); - } - - if (m_bIsStarted && m_LogKrnl_status) { - std::strcat(sMenu, "K"); - } - - SetMenuItemInfo(hMenu, ID_LOG, FALSE, &mii); -} - -void WndMain::RefreshAllStatus() -{ - UpdateFpsStatus(); - UpdateLogStatus(); - DrawMenuBar(m_hwnd); -} - -// open an xbe file -void WndMain::OpenXbe(const char *x_filename) -{ - if (m_Xbe != nullptr) { - CloseXbe(); - if (m_Xbe != nullptr) - return; - } - - strcpy(m_XbeFilename, x_filename); - - m_Xbe = new Xbe(m_XbeFilename, true); - - if(m_Xbe->HasError()) - { - // Save the error message as a separate string. This fixes a corruption in the message "Disclaimer: Cxbx-Reloaded has no - // affiliation with Microsoft" that would occur if loading an xbe and then launching the dashboard with the "Open dashboard" - // option but it's not installed - - std::string ErrorMessage = m_Xbe->GetError(); - - delete m_Xbe; m_Xbe = nullptr; - - RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); - - PopupError(m_hwnd, ErrorMessage.c_str()); - - UpdateCaption(); - - return; - } - - std::string errorMsg; - - if (!g_Settings->m_gui.bIgnoreInvalidXbeSig && !m_Xbe->CheckSignature()) { - errorMsg += "- XBE signature check failed!\n"; - } - - if (!g_Settings->m_gui.bIgnoreInvalidXbeSec) { - for (uint32_t sectionIndex = 0; sectionIndex < m_Xbe->m_Header.dwSections; sectionIndex++) { - if (!m_Xbe->CheckSectionIntegrity(sectionIndex)) { - errorMsg += "- One or more XBE section(s) are corrupted!\n"; - - // if we find a corrupted section, we won't bother checking the remaining sections since we know - // already at this point that the xbe is invalid - break; - } - } - } - - if (!errorMsg.empty()) { - errorMsg += ("\nThis is dangerous, as maliciously modified Xbox applications could take control of your system." - "\nPlease do not report issues for this application.\n" - "\nAre you sure you want to continue?"); - - PopupReturn ret = PopupWarningEx(m_hwnd, PopupButtons::YesNo, PopupReturn::No, errorMsg.c_str()); - if (ret != PopupReturn::Yes) - { - delete m_Xbe; m_Xbe = nullptr; - - RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); - - UpdateCaption(); - - return; - } - } - - // save this xbe to the list of recent xbe files - if(m_XbeFilename[0] != '\0') { - bool found = false; - - // if this filename already exists, temporarily remove it - for(int c=0, r=0;cm_gui.szRecentXbeFiles[c].c_str(), m_XbeFilename) == 0) { - found = true; - r++; - } - - if(r != c) { - if(g_Settings->m_gui.szRecentXbeFiles[r].c_str() == 0 || r > m_dwRecentXbe - 1) { - g_Settings->m_gui.szRecentXbeFiles[c] = ""; - } - else { - g_Settings->m_gui.szRecentXbeFiles[c] = g_Settings->m_gui.szRecentXbeFiles[r]; - } - } - } - - if (found) { - m_dwRecentXbe--; - } - - // move all items down one, removing the last one if necessary - for (int v = RECENT_XBE_LIST_MAX - 1;v > 0; v--) { - - if(g_Settings->m_gui.szRecentXbeFiles[v-1].size() == 0) { - g_Settings->m_gui.szRecentXbeFiles[v] = ""; - } - else { - g_Settings->m_gui.szRecentXbeFiles[v] = g_Settings->m_gui.szRecentXbeFiles[v - 1]; - } - } - - // add new item as first index - g_Settings->m_gui.szRecentXbeFiles[0] = m_XbeFilename; - - if (m_dwRecentXbe < RECENT_XBE_LIST_MAX) { - m_dwRecentXbe++; - } - } - - UpdateRecentFiles(); - - XbeLoaded(); -} - -// close xbe file -void WndMain::CloseXbe() -{ - if (m_bIsStarted) - StopEmulation(); - - if(m_bXbeChanged) - { - PopupReturn ret = PopupQuestion(m_hwnd, "Changes have been made, do you wish to save?"); - - if(ret == PopupReturn::Yes) - SaveXbeAs(); - else if(ret == PopupReturn::Cancel) - return; - } - - printf("WndMain: %s unloaded.\n", m_Xbe->m_szAsciiTitle); - - m_bXbeChanged = false; - - delete m_Xbe; m_Xbe = nullptr; - - UpdateCaption(); - RefreshMenus(); - - DebuggerMonitorClose(); - - // clear logo bitmap - { - uint32_t v=0; - for(uint32_t y=0;y<17;y++) - { - for(uint32_t x=0;x<100;x++) - { - SetPixel(m_LogoDC, x, y, RGB(0, 0, 0)); - v++; - } - } - } - - // clear game logo bitmap - SelectObject(m_GameLogoDC, m_OrigGameLogo); - DeleteObject(m_GameLogoDC); - DeleteObject(m_GameLogoBMP); - - RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); -} - -void WndMain::OpenMRU(int mru) -{ - HMENU menu = GetMenu(m_hwnd); - HMENU file_menu = GetSubMenu(menu, 0); - HMENU rxbe_menu = GetSubMenu(file_menu, 7); - - char szBuffer[270]; - - GetMenuString(rxbe_menu, ID_FILE_RXBE_0 + mru, szBuffer, 269, MF_BYCOMMAND); - - char *szFilename = (char*)((uint32_t)szBuffer + 5); // +5 skips over "&%d : " prefix (see UpdateRecentFiles) - - OpenXbe(szFilename); -} - -// Open the dashboard xbe if found -void WndMain::OpenDashboard() -{ - std::string DashboardPath = g_Settings->GetDataLocation() + std::string("\\EmuDisk\\Partition2\\xboxdash.xbe"); - OpenXbe(DashboardPath.c_str()); -} - -// save xbe file -void WndMain::SaveXbe(const char *x_filename) -{ - // ask permission to overwrite if the file already exists - if(_access(x_filename, 0) != -1) - { - if(PopupQuestionEx(m_hwnd, LOG_LEVEL::INFO, PopupButtons::YesNo, PopupReturn::No, "Overwrite existing file?") != PopupReturn::Yes) - return; - } - - // export xbe file - { - m_Xbe->Export(x_filename); - - if(m_Xbe->HasError()) - PopupError(m_hwnd, m_Xbe->GetError().c_str()); - else - { - char buffer[255]; - - sprintf(buffer, "%s was successfully saved.", m_Xbe->m_szAsciiTitle); - - PopupInfo(m_hwnd, buffer); - - m_bXbeChanged = false; - } - } -} - -// save xbe as -void WndMain::SaveXbeAs() -{ - OPENFILENAME ofn = {0}; - - char filename[MAX_PATH] = "default.xbe"; - - SuggestFilename(m_XbeFilename, filename, ".xbe"); - - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = m_hwnd; - ofn.lpstrFilter = "Xbox Executables (*.xbe)\0*.xbe\0"; - ofn.lpstrFile = filename; - ofn.nMaxFile = MAX_PATH; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = nullptr; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = nullptr; - ofn.lpstrDefExt = "xbe"; - ofn.Flags = OFN_PATHMUSTEXIST; - - if(GetSaveFileName(&ofn) == TRUE) - SaveXbe(ofn.lpstrFile); -} - -// Only grant access to GUI end. -namespace cli_config { -extern void SetValue(const std::string key, const std::string value); -extern void SetValue(const std::string key, const char* value); -extern void SetValue(const std::string key, const void* value); -extern void SetValue(const std::string key, int value); -} -// start emulation -void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState /*= debuggerOff*/) -{ - bool isEmulating = false; - - g_EmuShared->GetIsEmulating(&isEmulating); - - if (isEmulating) { - PopupError(m_hwnd, "A title is currently emulating, please stop emulation before attempting to start again."); - return; - } - - // Reset to default - g_EmuShared->Reset(); - m_FPS_status = 0.0f; - m_MSpF_status = 0.0f; - m_FlagsLLE_status = g_Settings->m_core.FlagsLLE; - m_LogKrnl_status = g_Settings->m_core.KrnlDebugMode != DebugMode::DM_NONE; - - // register all emulator settings to kernel process - g_Settings->SyncToEmulator(); - - // Preserve previous GUI window location. - HWND hOwner = GetParent(m_hwnd); - RECT curWindowPos; - GetWindowRect((hOwner != nullptr ? hOwner : m_hwnd), &curWindowPos); - m_prevWindowLoc.x = curWindowPos.left; - m_prevWindowLoc.y = curWindowPos.top; - ScreenToClient((hOwner != nullptr ? hOwner : m_hwnd), &m_prevWindowLoc); - m_prevWindowLoc.x = curWindowPos.left - m_prevWindowLoc.x; - m_prevWindowLoc.y = curWindowPos.top - m_prevWindowLoc.y; - - // Set the window size to emulation dimensions (The configured 'display' resolution) - // Note : Doing this here assures the emulation process will use - // the configured dimensions (because if done inside the emulation - // process, that doesn't resize correctly sometimes) - // We always resize, as we no longer tie host resolution to Xbox resolution - // 'Higher Resolution' rendering is handled as a scale factor. - ResizeWindow(m_hwnd, /*bForGUI*/false); - - // shell exe - { - - char szExeFileName[MAX_PATH]; - GetModuleFileName(GetModuleHandle(nullptr), szExeFileName, MAX_PATH); - if (g_Settings->m_core.bUseLoaderExec) { - PathRemoveFileSpec(szExeFileName); - PathAppend(szExeFileName, "\\cxbxr-ldr.exe"); - } - - bool AttachLocalDebugger = (LocalDebuggerState == debuggerOn); - g_EmuShared->SetDebuggingFlag(&AttachLocalDebugger); - - /* Main process to generate emulation command line begin. */ - // If we are adding more arguments, this is the place to do so. - cli_config::SetValue(cli_config::exec, szExeFileName); - cli_config::SetLoad(m_XbeFilename); - cli_config::SetValue(cli_config::hwnd, hwndParent); - cli_config::SetValue(cli_config::debug_mode, g_Settings->m_core.KrnlDebugMode); - if (g_Settings->m_core.KrnlDebugMode == DM_FILE) { - cli_config::SetValue(cli_config::debug_file, g_Settings->m_core.szKrnlDebug); - } - else { - cli_config::SetValue(cli_config::debug_file, ""); - } - /* Main process to generate emulation command line end. */ - - if (AttachLocalDebugger) { - - // Check then close existing debugger monitor. - DebuggerMonitorClose(); - - if (!CxbxExec(true, &m_hDebuggerProc, true)) { - PopupError(m_hwnd, "Failed to start emulation with the debugger.\n\nYou will need to build CxbxDebugger manually."); - - printf("WndMain: %s debugger shell failed.\n", m_Xbe->m_szAsciiTitle); - } - else { - m_bIsStarted = true; - printf("WndMain: %s emulation started with debugger.\n", m_Xbe->m_szAsciiTitle); - m_hDebuggerMonitorThread = std::thread(DebuggerMonitor, this); // create the debugger monitoring thread - } - } - else { - - if (!CxbxExec(false, nullptr, false)) { - PopupError(m_hwnd, "Emulation failed.\n\n If this message repeats, the Xbe is not supported."); - - printf("WndMain: %s shell failed.\n", m_Xbe->m_szAsciiTitle); - } - else { - m_bIsStarted = true; - printf("WndMain: %s emulation started.\n", m_Xbe->m_szAsciiTitle); - } - } - } -} - -// stop emulation -void WndMain::StopEmulation() -{ - m_bIsStarted = false; - if (m_hwndChild != NULL) { - if (IsWindow(m_hwndChild)) { - SendMessage(m_hwndChild, WM_CLOSE, 0, 0); - } - - m_hwndChild = NULL; - } - - UpdateCaption(); - RefreshMenus(); - - // Set the window size back to it's GUI dimensions - ResizeWindow(m_hwnd, /*bForGUI=*/true); - - g_EmuShared->SetIsEmulating(false); -} - -// wrapper function to call CrashMonitor -DWORD WndMain::CrashMonitorWrapper(LPVOID lpParam) -{ - CxbxSetThreadName("Cxbx Crash Monitor"); - - Crash_Manager_Data* pCMD = (Crash_Manager_Data*)lpParam; - static_cast(pCMD->pWndMain)->CrashMonitor(pCMD->dwChildProcID); - free(lpParam); - - return 0; -} - -// monitor for crashes -void WndMain::CrashMonitor(DWORD dwChildProcID) -{ - int iBootFlags; - DWORD dwExitCode = 0; - - // If we do receive valid process ID, let's do the next step. - if (dwChildProcID != 0) { - - HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, dwChildProcID); - - // If we do receive valid handle, let's do the next step. - if (hProcess != NULL) { - - WaitForSingleObject(hProcess, INFINITE); - - GetExitCodeProcess(hProcess, &dwExitCode); - CloseHandle(hProcess); - - g_EmuShared->GetBootFlags(&iBootFlags); - - if (!iBootFlags) { - if (dwExitCode == EXIT_SUCCESS) {// StopEmulation - return; - } - // Or else, it's a crash - } - else { - - // multi-xbe - // destroy this thread and start a new one - return; - } - } - } - - // Crash clean up. - - KillTimer(m_hwnd, TIMERID_ACTIVE_EMULATION); - KillTimer(m_hwnd, TIMERID_LED); - m_hwndChild = NULL; - m_bIsStarted = false; - g_EmuShared->SetIsEmulating(false); - UpdateCaption(); - RefreshMenus(); - DrawLedBitmap(m_hwnd, true); -} - -// monitor for Debugger to close then set as "available" (For limit to 1 debugger per Cxbx GUI.) -DWORD WndMain::DebuggerMonitor(LPVOID lpVoid) -{ - CxbxSetThreadName("Cxbx Debugger Monitor"); - WndMain* pThis = static_cast(lpVoid); - - if (pThis->m_hDebuggerProc != nullptr) { - - // Peform a wait until Debugger is closed. - WaitForSingleObject(pThis->m_hDebuggerProc, INFINITE); - - if (pThis->m_hDebuggerProc != nullptr) { - CloseHandle(pThis->m_hDebuggerProc); - pThis->m_hDebuggerProc = nullptr; - } - } - - if (pThis->m_hDebuggerMonitorThread.joinable()) { - pThis->m_hDebuggerMonitorThread.detach(); - } - - return 0; -} -void WndMain::DebuggerMonitorClose() -{ - if (m_hDebuggerProc != nullptr) { - HANDLE hDebuggerProcTemp = m_hDebuggerProc; - std::thread hDebuggerMonitorThreadTemp = std::thread(std::move(m_hDebuggerMonitorThread)); - - // Set member to null pointer before terminate, this way debugger monitor thread will remain thread-safe. - m_hDebuggerProc = nullptr; - - - TerminateProcess(hDebuggerProcTemp, EXIT_SUCCESS); - CloseHandle(hDebuggerProcTemp); - - hDebuggerMonitorThreadTemp.join(); - } -} - -// draw Xbox LED bitmap -void WndMain::DrawLedBitmap(HWND hwnd, bool bdefault) -{ - HMENU hMenu = GetMenu(hwnd); - int ActiveLEDColor; - - MENUITEMINFO mii = { 0 }; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE | MIIM_BITMAP | MIIM_STRING; - char flagString[10] = "LLE-"; - mii.dwTypeData = &flagString[0]; - mii.fType = MFT_RIGHTJUSTIFY; - mii.hbmpItem = m_LedBmp; - - // When so requested, or when not emulating, draw a black bitmap and hide LLE flags string - if (bdefault || !m_bIsStarted) { - ActiveLEDColor = XBOX_LED_COLOUR_OFF; - sprintf(flagString, " "); - } - else { // draw colored bitmap - static int LedSequenceOffset = 0; - unsigned int FlagsLLE = 0; - - // Select active color and cycle through all 4 phases in the sequence - ActiveLEDColor = m_LedSeq_status[LedSequenceOffset & 3]; - ++LedSequenceOffset; - - // Set LLE flags string based on selected LLE flags - if (m_FlagsLLE_status & LLE_APU) { - strcat(flagString, "A"); - } - if (m_FlagsLLE_status & LLE_GPU) { - strcat(flagString, "G"); - } - if (m_FlagsLLE_status & LLE_USB) { - strcat(flagString, "U"); - } - if (m_FlagsLLE_status & LLE_JIT) { - strcat(flagString, "J"); - } - if (m_FlagsLLE_status == 0) { - sprintf(flagString, "HLE"); - } - } - - SelectObject(m_LedDC, m_Brushes[ActiveLEDColor]); - SelectObject(m_LedDC, m_Pens[ActiveLEDColor]); - - m_OriLed = (HBITMAP)SelectObject(m_LedDC, m_LedBmp); - Rectangle(m_LedDC, 0, 0, m_xBmp, m_yBmp); - m_LedBmp = (HBITMAP)SelectObject(m_LedDC, m_OriLed); - - SetMenuItemInfo(hMenu, ID_LED, FALSE, &mii); - - DrawMenuBar(hwnd); - - return; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2003 Aaron Robinson +// * (c) 2017-2018 RadWolfie +// * (c) 2018 wutno (#/g/punk - Rizon) +// * +// * All rights reserved +// * +// ****************************************************************** + +#define LOG_PREFIX CXBXR_MODULE::GUI + +#include "Logging.h" +#include "WndMain.h" +#include "DlgAbout.h" +#include "DlgInputConfig.h" +#include "DlgVideoConfig.h" +#include "DlgAudioConfig.h" +#include "DlgNetworkConfig.h" +#include "DlgEepromConfig.h" +#include "DlgLoggingConfig.h" +#include "common\xbe\XbePrinter.h" // For DumpInformation +#include "EmuShared.h" +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For CxbxSetPixelContainerHeader +#include "core\hle\D3D8\XbConvert.h" // For EmuPC2XB_D3DFormat +#include "common\Settings.hpp" +#include "common/util/cliConfig.hpp" + +#include "core\kernel\init\CxbxKrnl.h" // For CxbxExec +#include "resource/ResCxbx.h" +#include "CxbxVersion.h" +#include "Shlwapi.h" + +#undef GetSystemMetrics // Force remove DirectX 8's multimon.h defined function (redirect to xGetSystemMetrics). +#include // For GetSystemMetrics + +#include +#include + +#include // for std::stringstream +#include +#include +#include // for _O_TEXT +#include "common\util\hasher.h" + +#define XBOX_LED_FLASH_PERIOD 176 // if you know a more accurate value, put it here + +static int gameLogoWidth, gameLogoHeight; +static int splashLogoWidth, splashLogoHeight; + +bool g_SaveOnExit = true; + +void ClearSymbolCache(const char sStorageLocation[MAX_PATH]) +{ + std::string cacheDir = std::string(sStorageLocation) + "\\SymbolCache\\"; + std::string fullpath = cacheDir + "*.ini"; + + WIN32_FIND_DATA data; + HANDLE hFind = FindFirstFile(fullpath.c_str(), &data); + + if (hFind != INVALID_HANDLE_VALUE) { + BOOL bContinue = TRUE; + do { + if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + fullpath = cacheDir + data.cFileName; + + if (!std::filesystem::remove(fullpath)) { + break; + } + } + + bContinue = FindNextFile(hFind, &data); + } while (bContinue); + + FindClose(hFind); + } + + printf("Cleared HLE Cache\n"); +} + +void WndMain::InitializeSettings() +{ + g_Settings->Delete(); + g_SaveOnExit = false; +} + +#define TIMERID_ACTIVE_EMULATION 0 +#define TIMERID_LED 1 + +void WndMain::ResizeWindow(HWND hwnd, bool bForGUI) +{ + RECT desktopRect; + RECT windowRect; + + // Set the window size back to it's GUI dimensions + m_w = 640; + m_h = 480; + if (!bForGUI) { + // For emulation, get the configured window dimensions + Settings::s_video XBVideoConf; + g_EmuShared->GetVideoSettings(&XBVideoConf); + + const char* resolution = XBVideoConf.szVideoResolution; + if (2 != sscanf(resolution, "%d x %d", &m_w, &m_h)) { + EmuLog(LOG_LEVEL::DEBUG, "Couldn't parse resolution : %s.", resolution); + } + } + + // TODO : Acknowledge DPI scaling here + GetWindowRect(GetDesktopWindow(), &desktopRect); + + // Limit width/height to desktop resolution + int dWidth = desktopRect.right - desktopRect.left; + int dHeight = desktopRect.bottom - desktopRect.top; + if (m_w > dWidth) + m_w = dWidth; + if (m_h > dHeight) + m_h = dHeight; + + if (bForGUI && m_prevWindowLoc.x != -1 && m_prevWindowLoc.y != -1) { + // Restore to previous Window location + m_x = m_prevWindowLoc.x; + m_y = m_prevWindowLoc.y; + } + else { + // Center to desktop + m_x = desktopRect.left + ((dWidth - m_w) / 2); + m_y = desktopRect.top + ((dHeight - m_h) / 2); + } + + // Resize the window so it's client area can contain the requested resolution + windowRect = { m_x, m_y, m_x + m_w, m_y + m_h }; + AdjustWindowRectEx(&windowRect, GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, GetWindowLong(hwnd, GWL_EXSTYLE)); + // TODO : For DPI screens, replace AdjustWindowRectEx by DwmGetWindowAttribute using DWMWA_EXTENDED_FRAME_BOUNDS + SetWindowPos(hwnd, 0, + windowRect.left, + windowRect.top, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + SWP_NOOWNERZORDER | SWP_NOZORDER); +} + +WndMain::WndMain(HINSTANCE x_hInstance) : + Wnd(x_hInstance) + , m_bCreated(false) + , m_Xbe(nullptr) + , m_bXbeChanged(false) + , m_bIsStarted(false) + , m_hwndChild(nullptr) + , m_hDebuggerProc(nullptr) + , m_hDebuggerMonitorThread() + , m_prevWindowLoc({ -1, -1 }) + , m_LogKrnl_status(false) +{ + // initialize members + { + m_classname = "WndMain"; + m_wndname = "Cxbx-Reloaded " + std::string(CxbxVersionStr); + } + + // load configuration from settings file + { + // NOTE: Settings has already been initalized/load from file before WndMain constructed. + + g_Settings->Verify(); + + // NOTE: This is a requirement for pre-verification from GUI. Used in CxbxInitFilePaths function. + g_EmuShared->SetStorageLocation(g_Settings->GetDataLocation().c_str()); + + unsigned int i = 0; + do { + if (g_Settings->m_gui.szRecentXbeFiles[i].size() == 0) { + break; + } + i++; + } while (i < RECENT_XBE_LIST_MAX); + + m_dwRecentXbe = i; + } +} + +WndMain::~WndMain() +{ + // save configuration to registry + if (g_SaveOnExit) { + g_Settings->Save(); + } + + // Close opened debugger monitor if there is one + DebuggerMonitorClose(); + + // cleanup allocations + { + delete m_Xbe; + delete g_Settings; + } +} + +// window message processing procedure +LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: + { + // initialize menu + { + HMENU hMenu = LoadMenu(m_hInstance, MAKEINTRESOURCE(IDR_MAINMENU)); + + SetMenu(hwnd, hMenu); + } + + // Set window size to GUI dimensions + ResizeWindow(hwnd, /*bForGUI=*/true); + + // initialize back buffer + { + HDC hDC = GetDC(hwnd); + + m_SplashBmp = CreateCompatibleBitmap(hDC, m_w, m_h); + + // create Xbox LED bitmap + { + m_xBmp = GetSystemMetrics(SM_CXMENUCHECK); + m_yBmp = GetSystemMetrics(SM_CYMENUCHECK); + m_LedDC = CreateCompatibleDC(hDC); + m_LedBmp = CreateCompatibleBitmap(hDC, m_xBmp, m_yBmp); + m_Brushes[XBOX_LED_COLOUR_OFF] = CreateSolidBrush(RGB(0, 0, 0)); + m_Brushes[XBOX_LED_COLOUR_GREEN] = CreateSolidBrush(RGB(0, 255, 0)); + m_Brushes[XBOX_LED_COLOUR_RED] = CreateSolidBrush(RGB(255, 0, 0)); + m_Brushes[XBOX_LED_COLOUR_ORANGE] = CreateSolidBrush(RGB(255, 165, 0)); + m_Pens[XBOX_LED_COLOUR_OFF] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + m_Pens[XBOX_LED_COLOUR_GREEN] = CreatePen(PS_SOLID, 1, RGB(0, 255, 0)); + m_Pens[XBOX_LED_COLOUR_RED] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); + m_Pens[XBOX_LED_COLOUR_ORANGE] = CreatePen(PS_SOLID, 1, RGB(255, 165, 0)); + DrawLedBitmap(hwnd, true); + } + + splashLogoWidth = 500; + splashLogoHeight = 113; + m_SplashBmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDR_JPEG_SPLASH), IMAGE_BITMAP, 0, 0, 0); + + m_LogoBmp = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDB_LOGO), IMAGE_BITMAP, 0, 0, 0); + + m_SplashDC = CreateCompatibleDC(hDC); + m_LogoDC = CreateCompatibleDC(hDC); + + m_OrigBmp = (HBITMAP)SelectObject(m_SplashDC, m_SplashBmp); + m_OrigLogo = (HBITMAP)SelectObject(m_LogoDC, m_LogoBmp); + + m_BackgroundColor = CreateSolidBrush(RGB(48, 48, 48)); + + if(hDC != NULL) + ReleaseDC(hwnd, hDC); + } + + SetClassLong(hwnd, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX))); + DragAcceptFiles(hwnd, TRUE); + + // Allow Drag and Drop if Cxbx is run with elevated privileges on Windows Vista and above + + ChangeWindowMessageFilterEx(hwnd, WM_DROPFILES, MSGFLT_ALLOW, nullptr); + ChangeWindowMessageFilterEx(hwnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr); + ChangeWindowMessageFilterEx(hwnd, 0x0049, MSGFLT_ALLOW, nullptr); + + m_bCreated = true; + } + break; + + // NOTE: WM_PARENTNOTIFY was triggered by kernel process' graphic window creation. + case WM_PARENTNOTIFY: + { + switch(LOWORD(wParam)) + { + case WM_CREATE: + { + if (m_hwndChild == NULL) { + m_FPS_status = 0.0f; + m_MSpF_status = 0.0f; + m_LedSeq_status_block = (XBOX_LED_COLOUR_GREEN << 24) | + (XBOX_LED_COLOUR_GREEN << 16) | + (XBOX_LED_COLOUR_GREEN << 8) | + (XBOX_LED_COLOUR_GREEN); + SetTimer(hwnd, TIMERID_ACTIVE_EMULATION, 1000, (TIMERPROC)nullptr); + SetTimer(hwnd, TIMERID_LED, XBOX_LED_FLASH_PERIOD, (TIMERPROC)nullptr); + m_hwndChild = GetWindow(hwnd, GW_CHILD); + UpdateCaption(); + RefreshMenus(); + } + else { + m_hwndChild = GetWindow(hwnd, GW_CHILD); + } + } + break; + + case WM_DESTROY: + { + // (HWND)HIWORD(wParam) seems to be NULL, so we can't compare to m_hwndChild + if (m_hwndChild != NULL) { // Let's hope this signal originated from the only child window + KillTimer(hwnd, TIMERID_ACTIVE_EMULATION); + KillTimer(hwnd, TIMERID_LED); + m_hwndChild = NULL; + StopEmulation(); + DrawLedBitmap(hwnd, true); + } + } + case WM_COMMAND: + { + switch (HIWORD(wParam)) { + case ID_GUI_STATUS_LLE_FLAGS: + m_FlagsLLE_status = static_cast(lParam); + break; + + case ID_GUI_STATUS_XBOX_LED_COLOUR: + m_LedSeq_status_block = static_cast(lParam); + break; + + case ID_GUI_STATUS_LOG_ENABLED: + m_LogKrnl_status = static_cast(lParam); + UpdateLogStatus(); + break; + + // NOTE: If anything need to set before kernel process start do anything, do it here. + case ID_GUI_STATUS_KRNL_IS_READY: { + Crash_Manager_Data* pCMD = (Crash_Manager_Data*)malloc(sizeof(Crash_Manager_Data)); + pCMD->pWndMain = this; + pCMD->dwChildProcID = lParam; // lParam is process ID. + std::thread(CrashMonitorWrapper, pCMD).detach(); + + g_EmuShared->SetIsEmulating(true); // NOTE: Putting in here raise to low or medium risk due to debugger will launch itself. (Current workaround) + g_EmuShared->SetIsReady(true); + } + break; + } + } + break; + } + }; + break; // added per PVS suggestion. + + case WM_TIMER: + { + switch (wParam) + { + case TIMERID_ACTIVE_EMULATION: + { + RefreshAllStatus(); + } + break; + + case TIMERID_LED: + { + DrawLedBitmap(hwnd, false); + } + break; + } + } + break; + + case WM_SYSKEYDOWN: + { + if(m_hwndChild != NULL) + { + SendMessage(m_hwndChild, uMsg, wParam, lParam); + } + else + { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + }; + break; // added per PVS suggestion. + + case WM_PAINT: + { + static bool s_bInitMenu = true; + + // initialize menus if they haven't been initialized already + if(s_bInitMenu) + { + UpdateRecentFiles(); + + RefreshMenus(); + + UpdateDebugConsoles(); + + s_bInitMenu = false; + } + + PAINTSTRUCT ps; + + BeginPaint(hwnd, &ps); + + HDC hDC = GetDC(hwnd); + + // draw splash / logo / status + { + static const int nLogoBmpW = 100, nLogoBmpH = 17; + + RECT bkRect = ps.rcPaint; + + bkRect.bottom -= nLogoBmpH + 10; + + FillRect(hDC, &bkRect, m_BackgroundColor); + + bkRect.top = bkRect.bottom; + bkRect.bottom += nLogoBmpH + 10; + + FillRect(hDC, &bkRect, m_Brushes[0]); + + BitBlt(hDC, m_w/2 - splashLogoWidth/2, m_h/2 - splashLogoHeight, splashLogoWidth, splashLogoHeight, m_SplashDC, 0, 0, SRCCOPY); + + BitBlt(hDC, m_w - gameLogoWidth - 3, m_h - nLogoBmpH - 12 - gameLogoHeight, gameLogoWidth, gameLogoHeight, m_GameLogoDC, 0, 0, SRCCOPY); + + BitBlt(hDC, m_w-nLogoBmpW-4, m_h-nLogoBmpH-4, nLogoBmpW, nLogoBmpH, m_LogoDC, 0, 0, SRCCOPY); + + int nHeight = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72); + + HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_ROMAN, "Verdana"); + + HGDIOBJ tmpObj = SelectObject(hDC, hFont); + + SetBkColor(hDC, RGB(0,0,0)); + + SetTextColor(hDC, RGB(255,255,255)); + + char buffer[255]; + + if(m_Xbe != nullptr && m_Xbe->HasError()) + sprintf(buffer, "%s Loaded!", m_Xbe->m_szAsciiTitle); + else + sprintf(buffer, "%s", "Disclaimer: Cxbx-Reloaded has no affiliation with Microsoft"); + + RECT rect = {0, m_h-15-5, m_w-100-4-69, m_h-5}; + + ExtTextOut(hDC, 4, m_h-15-5, ETO_OPAQUE, &rect, buffer, strlen(buffer), 0); + + SelectObject(hDC, tmpObj); + + DeleteObject(hFont); + } + + if(hDC != NULL) + ReleaseDC(hwnd, hDC); + + EndPaint(hwnd, &ps); + } + break; + + case WM_KEYDOWN: + { + switch(wParam) + { + case VK_F5: + { + // Start emulation normally + if (!m_bIsStarted) { + // Try to open the most recent Xbe if none is opened yet : + if (m_Xbe == nullptr) + OpenMRU(0); + + if (m_Xbe != nullptr) + StartEmulation(hwnd); + + break; + } + // fall through + } + + case VK_F6: + { + // Stop emulation + if (m_bIsStarted) + { + StopEmulation(); + break; + } + // fall through + } + + case VK_F7: + { + // Open the dashboard xbe + if (!m_bIsStarted) + { + if (m_Xbe != nullptr) { CloseXbe(); } + + OpenDashboard(); + break; + } + // fall through + } + + case VK_F9: + { + // Start emulation with the debugger + if (!m_bIsStarted) { + // Try to open the most recent Xbe if none is opened yet + if (m_Xbe == nullptr) + OpenMRU(0); + + if (m_Xbe != nullptr) + StartEmulation(hwnd, debuggerOn); + + break; + } + // fall through + } + + default: + { + if(m_hwndChild != NULL) + { + SendMessage(m_hwndChild, uMsg, wParam, lParam); + } + else + { + DefWindowProc(hwnd, uMsg, wParam, lParam); + } + } + } + } + break; + + case WM_DROPFILES: + { + if(!m_bIsStarted) { + char DroppedXbeFilename[MAX_PATH]; + DragQueryFile((HDROP)wParam, 0, DroppedXbeFilename, MAX_PATH); + OpenXbe(DroppedXbeFilename); + } + } + break; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case ID_FILE_OPEN_XBE: + { + OPENFILENAME ofn = { 0 }; + + char filename[MAX_PATH] = { 0 }; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Xbox Executables (*.xbe)\0*.xbe\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + if (GetOpenFileName(&ofn) == TRUE) + { + OpenXbe(ofn.lpstrFile); + } + } + break; + + case ID_FILE_CLOSE_XBE: + CloseXbe(); + break; + + + case ID_FILE_OPEN_DASHBOARD: + { + if (m_Xbe != nullptr) { CloseXbe(); } + + OpenDashboard(); + } + break; + + case ID_FILE_SAVEXBEFILE: + { + if (m_XbeFilename[0] == '\0') + SaveXbeAs(); + else + SaveXbe(m_XbeFilename); + } + break; + + case ID_FILE_SAVEXBEFILEAS: + SaveXbeAs(); + break; + + case ID_FILE_RXBE_0: + case ID_FILE_RXBE_1: + case ID_FILE_RXBE_2: + case ID_FILE_RXBE_3: + case ID_FILE_RXBE_4: + case ID_FILE_RXBE_5: + case ID_FILE_RXBE_6: + case ID_FILE_RXBE_7: + case ID_FILE_RXBE_8: + case ID_FILE_RXBE_9: + { + OpenMRU(LOWORD(wParam) - ID_FILE_RXBE_0); + } + break; + + case ID_FILE_EXIT: + SendMessage(hwnd, WM_CLOSE, 0, 0); + break; + + case ID_EDIT_LOGOBITMAP_EXPORT: + { + OPENFILENAME ofn = { 0 }; + + char filename[MAX_PATH] = "logo.bmp"; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Bitmap Image Files (*.bmp)\0*.bmp\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.lpstrDefExt = "bmp"; + ofn.lpstrTitle = "Export Logo Bitmap"; + ofn.Flags = OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&ofn) == TRUE) + { + // ask permission to overwrite if file already exists + if (_access(ofn.lpstrFile, 0) != -1) + { + if (PopupQuestionEx(m_hwnd, LOG_LEVEL::WARNING, PopupButtons::YesNo, PopupReturn::No, + "Overwrite existing file?") != PopupReturn::Yes) + return TRUE; + } + + // export logo bitmap + { + uint8_t i_gray[100 * 17]; + + m_Xbe->ExportLogoBitmap(i_gray); + + if (false == m_Xbe->HasError()) + { + FILE *LogoBitmap = fopen(ofn.lpstrFile, "wb"); + + // write bitmap header + { + BITMAPFILEHEADER bmfh; + + bmfh.bfType = *(uint16_t*)"BM"; + bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - sizeof(RGBQUAD) + (100 * 17) * sizeof(RGBTRIPLE) + 2; + bmfh.bfReserved1 = 0; + bmfh.bfReserved2 = 0; + bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - sizeof(RGBQUAD); + + fwrite(&bmfh, sizeof(bmfh), 1, LogoBitmap); + } + + // write bitmap info + { + BITMAPINFO bmi; + + bmi.bmiHeader.biSize = sizeof(BITMAPINFO) - sizeof(RGBQUAD); + bmi.bmiHeader.biWidth = 100; + bmi.bmiHeader.biHeight = -17; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 24; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = 0; + bmi.bmiHeader.biXPelsPerMeter = 0; + bmi.bmiHeader.biYPelsPerMeter = 0; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + + fwrite(&bmi, sizeof(bmi) - 4, 1, LogoBitmap); + } + + // write bitmap data + { + RGBTRIPLE bmp_data[100 * 17]; + + for (uint32_t v = 0; v < 100 * 17; v++) + { + bmp_data[v].rgbtRed = i_gray[v]; + bmp_data[v].rgbtGreen = i_gray[v]; + bmp_data[v].rgbtBlue = i_gray[v]; + } + + fwrite(bmp_data, 100 * 17 * sizeof(RGBTRIPLE), 1, LogoBitmap); + } + + // write bitmap padding + { + uint16_t pad = 0; + + fwrite(&pad, 2, 1, LogoBitmap); + } + + fclose(LogoBitmap); + } + + if (m_Xbe->HasError()) + PopupError(m_hwnd, m_Xbe->GetError().c_str()); + else + { + char buffer[255]; + + sprintf(buffer, "%s's logo bitmap was successfully exported.", m_Xbe->m_szAsciiTitle); + + PopupInfo(m_hwnd, buffer); + } + } + } + } + break; + + case ID_EDIT_LOGOBITMAP_IMPORT: + { + OPENFILENAME ofn = { 0 }; + + char filename[MAX_PATH] = "*.bmp"; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Bitmap Image Files (*.bmp)\0*.bmp\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.lpstrDefExt = "bmp"; + ofn.lpstrTitle = "Import Logo Bitmap"; + ofn.Flags = OFN_PATHMUSTEXIST; + + if (GetOpenFileName(&ofn) == TRUE) + { + // import logo bitmap + { + uint8_t i_gray[100 * 17]; + + // read bitmap file + { + FILE *logo = fopen(ofn.lpstrFile, "rb"); + + char *bmp_err = 0; + + // read bitmap header + if (!bmp_err) + { + BITMAPFILEHEADER bmfh; + + fread(&bmfh, sizeof(bmfh), 1, logo); + + if (bmfh.bfType != *(uint16_t*)"BM") + bmp_err = "Invalid bitmap file...\n\nonly allows 24 bit bitmaps (100 by 17 pixels) with row order swapped"; + else if (bmfh.bfSize != sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) - sizeof(RGBQUAD) + (100 * 17) * sizeof(RGBTRIPLE) + 2) + bmp_err = "Invalid bitmap file...\n\nonly allows 24 bit bitmaps (100 by 17 pixels) with row order swapped"; + } + + // read bitmap info + if (!bmp_err) + { + BITMAPINFO bmi; + + fread(&bmi, sizeof(bmi) - 4, 1, logo); + + if (bmi.bmiHeader.biWidth != 100 || bmi.bmiHeader.biHeight != -17) + bmp_err = "Invalid bitmap file...\n\nonly allows 24 bit bitmaps (100 by 17 pixels) with row order swapped"; + } + + // read bitmap data + if (!bmp_err) + { + RGBTRIPLE bmp_data[100 * 17]; + + fread(bmp_data, 100 * 17 * sizeof(RGBTRIPLE), 1, logo); + + for (uint32_t c = 0; c < 100 * 17; c++) + i_gray[c] = (char)(((float)bmp_data[c].rgbtRed + (float)bmp_data[c].rgbtGreen + (float)bmp_data[c].rgbtBlue) / 3.0); + } + + fclose(logo); + + if (bmp_err != 0) + { + PopupError(m_hwnd, bmp_err); + break; + } + } + + m_Xbe->ImportLogoBitmap(i_gray); + + if (m_Xbe->HasError()) + { + PopupError(m_hwnd, m_Xbe->GetError().c_str()); + + if (m_Xbe->HasFatalError()) + { + CloseXbe(); + } + else + { + m_Xbe->ClearError(); + } + + } + else + { + m_bXbeChanged = true; + + LoadLogo(); + + char buffer[255]; + + sprintf(buffer, "%s's logo bitmap was successfully updated.", m_Xbe->m_szAsciiTitle); + + PopupInfo(m_hwnd, buffer); + } + } + } + } + break; + + case ID_EDIT_PATCH_ALLOW64MB: + { + m_bXbeChanged = true; + + m_Xbe->m_Header.dwInitFlags.bLimit64MB = !m_Xbe->m_Header.dwInitFlags.bLimit64MB; + + RefreshMenus(); + + if (m_Xbe->m_Header.dwInitFlags.bLimit64MB) + printf("WndMain: %s was patched to limit to 64MB of memory usage.\n", m_Xbe->m_szAsciiTitle); + else + printf("WndMain: %s was patched to allow >64MB of memory usage.\n", m_Xbe->m_szAsciiTitle); + } + break; + + case ID_EDIT_PATCH_DEBUGMODE: + { + m_bXbeChanged = true; + + // patch to/from debug mode + if ((m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL) > 0x01000000) + { + // we're in debug mode, so switch over to retail + uint32_t ep = m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL; // decode from debug mode + uint32_t kt = m_Xbe->m_Header.dwKernelImageThunkAddr ^ XOR_KT_DEBUG; // decode from debug mode + + m_Xbe->m_Header.dwEntryAddr = ep ^ XOR_EP_DEBUG; // encode to retail mode + m_Xbe->m_Header.dwKernelImageThunkAddr = kt ^ XOR_KT_RETAIL; // encode to retail mode + } + else + { + // we're in retail mode, so switch to debug + uint32_t ep = m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_DEBUG; // decode from retail mode + uint32_t kt = m_Xbe->m_Header.dwKernelImageThunkAddr ^ XOR_KT_RETAIL; // decode from retail mode + + m_Xbe->m_Header.dwEntryAddr = ep ^ XOR_EP_RETAIL; // encode to debug mode + m_Xbe->m_Header.dwKernelImageThunkAddr = kt ^ XOR_KT_DEBUG; // encode to debug mode + } + + RefreshMenus(); + + bool res = (m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL) > 0x01000000; + + if (res) + printf("WndMain: %s was converted to debug mode.\n", m_Xbe->m_szAsciiTitle); + else + printf("WndMain: %s was converted to retail mode.\n", m_Xbe->m_szAsciiTitle); + } + break; + + case ID_EDIT_DUMPXBEINFOTO_FILE: + { + OPENFILENAME ofn = { 0 }; + + char filename[MAX_PATH] = "Xbe.txt"; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Text Documents (*.txt)\0*.txt\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.lpstrDefExt = "txt"; + ofn.Flags = OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&ofn) == TRUE) + { + // ask permission to overwrite if file exists + if (_access(ofn.lpstrFile, 0) != -1) + { + if (PopupQuestion(m_hwnd, "Overwrite existing file?") != PopupReturn::Yes) + return TRUE; + } + + // dump xbe information to file + { + std::string Xbe_info = DumpInformation(m_Xbe); + if (m_Xbe->HasError()) { + PopupError(m_hwnd, m_Xbe->GetError().c_str()); + } + else { + std::ofstream Xbe_dump_file(ofn.lpstrFile); + if (Xbe_dump_file.is_open()) { + Xbe_dump_file << Xbe_info; + Xbe_dump_file.close(); + char buffer[255]; + sprintf(buffer, "%s's .xbe info was successfully dumped.", m_Xbe->m_szAsciiTitle); + PopupInfo(m_hwnd, buffer); + } + else { + PopupError(m_hwnd, "Could not open Xbe text file."); + } + } + } + } + } + break; + + case ID_EDIT_DUMPXBEINFOTO_DEBUGCONSOLE: + { + std::string Xbe_info = DumpInformation(m_Xbe); + if (m_Xbe->HasError()) { + PopupError(m_hwnd, m_Xbe->GetError().c_str()); + } + else { + std::cout << Xbe_info; + char buffer[255]; + sprintf(buffer, "%s's .xbe info was successfully dumped to console.", m_Xbe->m_szAsciiTitle); + printf("WndMain: %s\n", buffer); + } + } + break; + + case ID_SETTINGS_CONFIG_INPUT: + ShowInputConfig(hwnd, m_hwndChild); + break; + + case ID_SETTINGS_CONFIG_VIDEO: + ShowVideoConfig(hwnd); + break; + + case ID_SETTINGS_CONFIG_AUDIO: + ShowAudioConfig(hwnd); + break; + + case ID_SETTINGS_CONFIG_NETWORK: + ShowNetworkConfig(hwnd); + break; + + case ID_SETTINGS_CONFIG_EEPROM: + { + if (m_bIsStarted) { + // We don't allow changing the contents of the eeprom while a game is running, mostly because we lack a "pause emulation" + // function necessary to modify the contents safely (the game itself can modify the eeprom) + PopupError(hwnd, "Cannot modify eeprom file while a title is running"); + break; + } + ShowEepromConfig(hwnd); + } + break; + + case ID_SETTINGS_CONFIG_LOGGING: + { + ShowLoggingConfig(hwnd, m_hwndChild); + } + break; + + case ID_SETTINGS_CONFIG_DLOCCUSTOM: + { + char szDir[MAX_PATH]; + + BROWSEINFO bInfo; + bInfo.hwndOwner = NULL; + bInfo.pidlRoot = nullptr; + bInfo.pszDisplayName = szDir; + bInfo.lpszTitle = "Please, select a folder"; + bInfo.ulFlags = BIF_NEWDIALOGSTYLE, BIF_EDITBOX, BIF_VALIDATE; + bInfo.lpfn = nullptr; + bInfo.lParam = 0; + bInfo.iImage = -1; + + LPITEMIDLIST lpItem = SHBrowseForFolder(&bInfo); + + if (lpItem != nullptr) + { + SHGetPathFromIDList(lpItem, szDir); + + // -14 is for \\Cxbx-Reloaded string to be include later down below. + size_t szLen = strnlen(szDir, MAX_PATH - 14); + if (szLen == 0) { + PopupError(hwnd, "You've selected an invalid folder... Go back and try again."); + break; + } + else if (szLen == MAX_PATH - 14) { + PopupError(hwnd, "You've selected a folder path which is too long... Go back and try again."); + break; + } + + std::string szDirTemp = std::string(szDir) + std::string("\\Cxbx-Reloaded"); + + if (szDirTemp.size() > MAX_PATH) { + PopupError(hwnd, "Directory path is too long. Go back and choose a shorter path."); + break; + } + + int result = SHCreateDirectoryEx(nullptr, szDirTemp.c_str(), nullptr); + if ((result != ERROR_SUCCESS) && (result != ERROR_ALREADY_EXISTS)) { + PopupError(hwnd, "You don't have write permissions on that directory..."); + break; + } + + g_Settings->m_gui.DataStorageToggle = CXBX_DATA_CUSTOM; + g_Settings->m_gui.szCustomLocation = szDirTemp; + RefreshMenus(); + } + } + break; + + case ID_SETTINGS_CONFIG_DLOCAPPDATA: + { + g_Settings->m_gui.DataStorageToggle = CXBX_DATA_APPDATA; + RefreshMenus(); + } + break; + + case ID_SETTINGS_CONFIG_DLOCEXECDIR: + { + g_Settings->m_gui.DataStorageToggle = CXBX_DATA_EXECDIR; + RefreshMenus(); + } + break; + + case ID_CACHE_CLEARHLECACHE_ALL: + { + ClearSymbolCache(g_Settings->GetDataLocation().c_str()); + PopupInfo(m_hwnd, "The entire Symbol Cache has been cleared."); + } + break; + + case ID_CACHE_CLEARHLECACHE_CURRENT: + { + std::string cacheDir = g_Settings->GetDataLocation() + "\\SymbolCache\\"; + + // Hash the loaded XBE's header, use it as a filename + uint64_t uiHash = ComputeHash((void*)&m_Xbe->m_Header, sizeof(Xbe::Header)); + std::stringstream sstream; + std::string szTitleName(m_Xbe->m_szAsciiTitle); + m_Xbe->PurgeBadChar(szTitleName); + sstream << cacheDir << szTitleName << "-" << std::hex << uiHash << ".ini"; + std::string fullpath = sstream.str(); + + if (std::filesystem::remove(fullpath)) { + PopupInfo(m_hwnd, "This title's Symbol Cache entry has been cleared."); + } + } + break; + + case ID_SETTINGS_INITIALIZE: + { + PopupReturn ret = PopupWarningEx(m_hwnd, PopupButtons::YesNo, PopupReturn::No, + "Warning: This will reset all Cxbx-Reloaded settings to their default values.\nAre you sure you want to proceed?", "Cxbx-Reloaded"); + + if (ret == PopupReturn::Yes) { + InitializeSettings(); + PopupInfo(m_hwnd, "Cxbx-Reloaded has been initialized and will now close."); + SendMessage(hwnd, WM_CLOSE, 0, 0); + } + } + break; + + case ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE: + { + if (g_Settings->m_core.KrnlDebugMode == DM_NONE || g_Settings->m_core.KrnlDebugMode == DM_FILE) { + g_Settings->m_core.KrnlDebugMode = DM_CONSOLE; + } + else { + g_Settings->m_core.KrnlDebugMode = DM_NONE; + } + PopupInfo(m_hwnd, "This will not take effect until the next time emulation is started."); + + RefreshMenus(); + + UpdateDebugConsoles(); + } + break; + + case ID_EMULATION_DEBUGOUTPUTKERNEL_FILE: + { + if (g_Settings->m_core.KrnlDebugMode == DM_FILE) { + g_Settings->m_core.KrnlDebugMode = DM_NONE; + + RefreshMenus(); + + UpdateDebugConsoles(); + } + else + { + OPENFILENAME ofn = { 0 }; + + char filename[MAX_PATH] = "KrnlDebug.txt"; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Text Documents (*.txt)\0*.txt\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.lpstrDefExt = "txt"; + ofn.Flags = OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&ofn) != FALSE) + { + PopupInfo(m_hwnd, "This will not take effect until emulation is (re)started."); + + strncpy(g_Settings->m_core.szKrnlDebug, ofn.lpstrFile, MAX_PATH - 1); + + g_Settings->m_core.KrnlDebugMode = DM_FILE; + + RefreshMenus(); + + UpdateDebugConsoles(); + } + } + } + break; + + case ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE: + { + if (g_Settings->m_gui.CxbxDebugMode == DM_NONE || g_Settings->m_gui.CxbxDebugMode == DM_FILE) { + g_Settings->m_gui.CxbxDebugMode = DM_CONSOLE; + } + else { + g_Settings->m_gui.CxbxDebugMode = DM_NONE; + } + RefreshMenus(); + + UpdateDebugConsoles(); + } + break; + + case ID_EMULATION_DEBUGOUTPUTGUI_FILE: + { + if (g_Settings->m_gui.CxbxDebugMode == DM_FILE) + { + g_Settings->m_gui.CxbxDebugMode = DM_NONE; + + RefreshMenus(); + + UpdateDebugConsoles(); + } + else + { + OPENFILENAME ofn = { 0 }; + + char filename[MAX_PATH] = "CxbxDebug.txt"; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Text Documents (*.txt)\0*.txt\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.lpstrDefExt = "txt"; + ofn.Flags = OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&ofn) != FALSE) + { + g_Settings->m_gui.szCxbxDebugFile = ofn.lpstrFile; + + g_Settings->m_gui.CxbxDebugMode = DM_FILE; + + RefreshMenus(); + + UpdateDebugConsoles(); + } + + } + } + break; + + case ID_EMULATION_LLE_JIT: + { + g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_JIT; + RefreshMenus(); + } + break; + + case ID_EMULATION_LLE_APU: + { + g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_APU; + RefreshMenus(); + } + break; + + case ID_EMULATION_LLE_GPU: + { + g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_GPU; + RefreshMenus(); + } + break; +#if 0 // Reenable this when LLE USB actually works + case ID_EMULATION_LLE_USB: + { + g_Settings->m_core.FlagsLLE = g_Settings->m_core.FlagsLLE ^ LLE_USB; + RefreshMenus(); + } + break; +#endif + case ID_USELOADEREXEC: + { + g_Settings->m_core.bUseLoaderExec = !g_Settings->m_core.bUseLoaderExec; + RefreshMenus(); + } + break; + + case ID_EMULATION_START: + if (m_Xbe != nullptr) + { + StartEmulation(hwnd); + } + break; + + case ID_EMULATION_STARTDEBUGGER: + if (m_Xbe != nullptr) + { + StartEmulation(hwnd, debuggerOn); + } + break; + + case ID_EMULATION_STOP: + StopEmulation(); + break; + + case ID_HACKS_DISABLEPIXELSHADERS: + g_Settings->m_hacks.DisablePixelShaders = !g_Settings->m_hacks.DisablePixelShaders; + RefreshMenus(); + break; + + case ID_HACKS_RUNXBOXTHREADSONALLCORES: + if (g_Settings->m_hacks.UseAllCores == false) { + PopupReturn ret = PopupWarningEx(hwnd, PopupButtons::YesNo, PopupReturn::No, + "Activating this hack will make the emulator more likely to crash and/or hang." + "\nPlease do not report issues with games while this hack is active. Are you sure you want to turn it on?"); + if (ret != PopupReturn::Yes) { + break; + } + } + g_Settings->m_hacks.UseAllCores = !g_Settings->m_hacks.UseAllCores; + RefreshMenus(); + break; + + case ID_HACKS_SKIPRDTSCPATCHING: + g_Settings->m_hacks.SkipRdtscPatching = !g_Settings->m_hacks.SkipRdtscPatching; + RefreshMenus(); + break; + + case ID_SETTINGS_IGNOREINVALIDXBESIG: + g_Settings->m_gui.bIgnoreInvalidXbeSig = !g_Settings->m_gui.bIgnoreInvalidXbeSig; + RefreshMenus(); + break; + + case ID_SETTINGS_IGNOREINVALIDXBESEC: + g_Settings->m_gui.bIgnoreInvalidXbeSec = !g_Settings->m_gui.bIgnoreInvalidXbeSec; + RefreshMenus(); + break; + + case ID_SETTINGS_ALLOWADMINPRIVILEGE: + g_Settings->m_core.allowAdminPrivilege = !g_Settings->m_core.allowAdminPrivilege; + RefreshMenus(); + break; + + case ID_HELP_ABOUT: + { + ShowAboutDialog(hwnd); + } + break; + + case ID_HELP_HOMEPAGE: + ShellExecute(NULL, "open", "https://github.com/Cxbx-Reloaded/Cxbx-Reloaded", nullptr, nullptr, SW_SHOWNORMAL); + break; + + } + + break; + } + + case WM_MOVE: + { + // Redraw the window on move, prevents corrupt background image that happens + // when windows doesn't call the WM_DRAW event when the window is moved too quickly. + RedrawWindow(hwnd, nullptr, NULL, RDW_INVALIDATE); + break; + } + + case WM_CLOSE: + { + if(m_Xbe != nullptr) + CloseXbe(); + + if(m_Xbe == nullptr) + DestroyWindow(hwnd); + } + break; + + case WM_DESTROY: + { + FreeConsole(); + + HDC hDC = GetDC(hwnd); + + SelectObject(m_LogoDC, m_OrigLogo); + + SelectObject(m_SplashDC, m_OrigBmp); + + SelectObject(m_GameLogoDC, m_OrigGameLogo); + + SelectObject(m_LedDC, m_OriLed); + + DeleteObject(m_LogoDC); + + DeleteObject(m_SplashDC); + + DeleteObject(m_GameLogoDC); + + DeleteObject(m_LedDC); + + DeleteObject(m_LogoBmp); + + DeleteObject(m_SplashBmp); + + DeleteObject(m_BackgroundColor); + + DeleteObject(m_GameLogoBMP); + + DeleteObject(m_LedBmp); + + DeleteObject(m_Brushes[XBOX_LED_COLOUR_OFF]); + + DeleteObject(m_Brushes[XBOX_LED_COLOUR_GREEN]); + + DeleteObject(m_Brushes[XBOX_LED_COLOUR_RED]); + + DeleteObject(m_Brushes[XBOX_LED_COLOUR_ORANGE]); + + DeleteObject(m_Pens[XBOX_LED_COLOUR_OFF]); + + DeleteObject(m_Pens[XBOX_LED_COLOUR_GREEN]); + + DeleteObject(m_Pens[XBOX_LED_COLOUR_RED]); + + DeleteObject(m_Pens[XBOX_LED_COLOUR_ORANGE]); + + ReleaseDC(hwnd, hDC); + + delete m_Xbe; + + m_Xbe = nullptr; + + PostQuitMessage(0); + } + break; + + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return 0; +} + +// suggest a file name +void WndMain::SuggestFilename(const char *x_orig_filename, char *x_filename, char x_extension[4]) +{ + if(strrchr(x_orig_filename, '\\') != nullptr) + { + strcpy(x_filename, x_orig_filename); + char *loc = strrchr(x_filename, '.'); + if (loc != nullptr) + strncpy(loc, &x_extension[0], 4); + } +} + +// occurs when an xbe is loaded.. +void WndMain::XbeLoaded() +{ + LoadLogo(); + + LoadGameLogo(); + + UpdateCaption(); + RefreshMenus(); + + InvalidateRgn(m_hwnd, NULL, TRUE); + + printf("WndMain: %s loaded.\n", m_Xbe->m_szAsciiTitle); +} + +// load logo bitmap +void WndMain::LoadLogo() +{ + uint8_t i_gray[100*17]; + + m_Xbe->ExportLogoBitmap(i_gray); + + if(m_Xbe->HasError()) + { + PopupError(m_hwnd, m_Xbe->GetError().c_str()); + + if (m_Xbe->HasFatalError()) + { + CloseXbe(); + } + + return; + } + + uint32_t v=0; + for(uint32_t y=0;y<17;y++) + { + for(uint32_t x=0;x<100;x++) + { + SetPixel(m_LogoDC, x, y, RGB(i_gray[v], i_gray[v], i_gray[v])); + v++; + } + } + + RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); +} + +// TODO : Move these types to a more appropriate place +// Source : https://msdn.microsoft.com/en-us/library/windows/desktop/bb943984(v=vs.85).aspx +struct DDS_PIXELFORMAT { + DWORD dwSize; + DWORD dwFlags; + DWORD dwFourCC; + DWORD dwRGBBitCount; + DWORD dwRBitMask; + DWORD dwGBitMask; + DWORD dwBBitMask; + DWORD dwABitMask; +}; + +// Source : https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx +typedef struct { + DWORD dwSize; + DWORD dwFlags; + DWORD dwHeight; + DWORD dwWidth; + DWORD dwPitchOrLinearSize; + DWORD dwDepth; + DWORD dwMipMapCount; + DWORD dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + DWORD dwCaps; + DWORD dwCaps2; + DWORD dwCaps3; + DWORD dwCaps4; + DWORD dwReserved2; +} DDS_HEADER; + +// load game logo bitmap +void WndMain::LoadGameLogo() +{ + // Export Game Logo bitmap (XTIMAG or XSIMAG) + uint8_t *pSection = (uint8_t *)m_Xbe->FindSection("$$XTIMAGE"); // Check for XTIMAGE + if (!pSection) { + pSection = (uint8_t *)m_Xbe->FindSection("$$XSIMAGE"); // if XTIMAGE isn't present, check for XSIMAGE (smaller) + if (!pSection) { + return; + } + } + + gameLogoWidth = 0; + gameLogoHeight = 0; + + uint8_t *ImageData = nullptr; + XTL::X_D3DPixelContainer XboxPixelContainer = {}; + XTL::X_D3DPixelContainer *pXboxPixelContainer = &XboxPixelContainer; + + switch (*(DWORD*)pSection) { + case MAKEFOURCC('D', 'D', 'S', ' '): { + DDS_HEADER *pDDSHeader = (DDS_HEADER *)(pSection + sizeof(DWORD)); + D3DFORMAT Format = D3DFMT_UNKNOWN; + if (pDDSHeader->ddspf.dwFlags & DDPF_FOURCC) { + switch (pDDSHeader->ddspf.dwFourCC) { + case MAKEFOURCC('D', 'X', 'T', '1'): Format = D3DFMT_DXT1; break; + case MAKEFOURCC('D', 'X', 'T', '3'): Format = D3DFMT_DXT3; break; + case MAKEFOURCC('D', 'X', 'T', '5'): Format = D3DFMT_DXT5; break; + } + } + else { + // TODO : Determine D3D format based on pDDSHeader->ddspf.dwABitMask, .dwRBitMask, .dwGBitMask and .dwBBitMask + } + + if (Format == D3DFMT_UNKNOWN) + return; + + ImageData = (uint8_t *)(pSection + sizeof(DWORD) + pDDSHeader->dwSize); + //gameLogoHeight = pDDSHeader->dwHeight; + //gameLogoWidth = pDDSHeader->dwWidth; + + // TODO : Use PixelCopy code here to decode. For now, fake it : + CxbxSetPixelContainerHeader(&XboxPixelContainer, + 0, // Common - could be X_D3DCOMMON_TYPE_TEXTURE + (UINT)pDDSHeader->dwWidth, + (UINT)pDDSHeader->dwHeight, + 1, + EmuPC2XB_D3DFormat(Format), + 2, + (UINT)pDDSHeader->dwPitchOrLinearSize); + break; + } + case MAKEFOURCC('X', 'P', 'R', '0'): + case MAKEFOURCC('X', 'P', 'R', '1'): { + struct Xbe::XprHeader *pXprHeader = (struct Xbe::XprHeader*)pSection; + + unsigned int SizeOfResourceHeaders = pXprHeader->dwXprHeaderSize - sizeof(Xbe::XprHeader); + unsigned int SizeOfResourceData = pXprHeader->dwXprTotalSize - pXprHeader->dwXprHeaderSize; + + uint8_t *ResourceHeaders = pSection + sizeof(Xbe::XprHeader); + uint8_t *ResourceData = ResourceHeaders + SizeOfResourceHeaders; + + pXboxPixelContainer = (XTL::X_D3DPixelContainer*)ResourceHeaders; + ImageData = ResourceData; + + break; + } + default: { + return; + } + } + + void *bitmapData = ConvertD3DTextureToARGB(pXboxPixelContainer, ImageData, &gameLogoWidth, &gameLogoHeight); + if (!bitmapData) + return; + + HDC hDC = GetDC(m_hwnd); + m_GameLogoBMP = CreateCompatibleBitmap(hDC, gameLogoWidth, gameLogoHeight); + + // create bitmap + { + BITMAPINFO BmpInfo; + + BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFO) - sizeof(RGBQUAD); + BmpInfo.bmiHeader.biWidth = gameLogoWidth; + BmpInfo.bmiHeader.biHeight = 0 - (long)gameLogoHeight; // If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner. + BmpInfo.bmiHeader.biPlanes = 1; + BmpInfo.bmiHeader.biBitCount = 32; + BmpInfo.bmiHeader.biCompression = BI_RGB; + BmpInfo.bmiHeader.biSizeImage = 0; + BmpInfo.bmiHeader.biXPelsPerMeter = 0; + BmpInfo.bmiHeader.biYPelsPerMeter = 0; + BmpInfo.bmiHeader.biClrUsed = 0; + BmpInfo.bmiHeader.biClrImportant = 0; + + SetDIBits(hDC, m_GameLogoBMP, 0, gameLogoHeight, bitmapData, &BmpInfo, DIB_RGB_COLORS); + } + + m_GameLogoDC = CreateCompatibleDC(hDC); + m_OrigGameLogo = (HBITMAP)SelectObject(m_GameLogoDC, m_GameLogoBMP); + + RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); + + if (hDC != NULL) + ReleaseDC(m_hwnd, hDC); + + free(bitmapData); +} + + +// refresh menu items +void WndMain::RefreshMenus() +{ + bool XbeLoaded = (m_Xbe != nullptr); + bool Running = (m_hwndChild != NULL); // TODO : Use m_bIsStarted? + UINT MF_WhenXbeLoaded = XbeLoaded ? MF_ENABLED : MF_GRAYED; + UINT MF_WhenXbeLoadedNotRunning = (XbeLoaded && !Running) ? MF_ENABLED : MF_GRAYED; + UINT MF_WhenXbeLoadedAndRunning = (XbeLoaded && Running) ? MF_ENABLED : MF_GRAYED; + + // disable/enable appropriate menus + { + HMENU menu = GetMenu(m_hwnd); + + // file menu + { + HMENU file_menu = GetSubMenu(menu, 0); + + // enable/disable close .xbe file + EnableMenuItem(file_menu, ID_FILE_CLOSE_XBE, MF_BYCOMMAND | MF_WhenXbeLoaded); + + // enable/disable save .xbe file + EnableMenuItem(file_menu, ID_FILE_SAVEXBEFILE, MF_BYCOMMAND | MF_WhenXbeLoaded); + + // enable/disable save .xbe file as + EnableMenuItem(file_menu, ID_FILE_SAVEXBEFILEAS, MF_BYCOMMAND | MF_WhenXbeLoaded); + + // recent xbe files menu + { + HMENU rxbe_menu = GetSubMenu(file_menu, 7); + + int max = m_dwRecentXbe; + for(int v=0;v64 MB" if appropriate + if(m_Xbe != nullptr) + { + UINT chk_flag = (m_Xbe->m_Header.dwInitFlags.bLimit64MB) ? MF_UNCHECKED : MF_CHECKED; + + CheckMenuItem(pach_menu, ID_EDIT_PATCH_ALLOW64MB, chk_flag); + } + + // check "debug mode" if appropriate + if(m_Xbe != nullptr) + { + UINT chk_flag = ((m_Xbe->m_Header.dwEntryAddr ^ XOR_EP_RETAIL) > 0x01000000) ? MF_CHECKED : MF_UNCHECKED; + + CheckMenuItem(pach_menu, ID_EDIT_PATCH_DEBUGMODE, chk_flag); + } + } + } + + // view menu + { + HMENU view_menu = GetSubMenu(menu, 2); + HMENU emul_debg = GetSubMenu(view_menu, 0); + HMENU emul_krnl = GetSubMenu(view_menu, 1); + + switch (g_Settings->m_core.KrnlDebugMode) { + case DM_CONSOLE: + CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE, MF_CHECKED); + CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_FILE, MF_UNCHECKED); + break; + + case DM_FILE: + CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE, MF_UNCHECKED); + CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_FILE, MF_CHECKED); + break; + + default: + CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE, MF_UNCHECKED); + CheckMenuItem(emul_krnl, ID_EMULATION_DEBUGOUTPUTKERNEL_FILE, MF_UNCHECKED); + break; + } + + switch (g_Settings->m_gui.CxbxDebugMode) { + case DM_CONSOLE: + CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE, MF_CHECKED); + CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_FILE, MF_UNCHECKED); + break; + + case DM_FILE: + CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE, MF_UNCHECKED); + CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_FILE, MF_CHECKED); + break; + + default: + CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE, MF_UNCHECKED); + CheckMenuItem(emul_debg, ID_EMULATION_DEBUGOUTPUTGUI_FILE, MF_UNCHECKED); + break; + } + UpdateLogStatus(); + } + + // settings menu + { + HMENU settings_menu = GetSubMenu(menu, 3); + + // enable/disable clear current hle cache + EnableMenuItem(settings_menu, ID_CACHE_CLEARHLECACHE_CURRENT, MF_BYCOMMAND | MF_WhenXbeLoadedNotRunning); + + UINT chk_flag = (g_Settings->m_core.FlagsLLE & LLE_JIT) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_EMULATION_LLE_JIT, chk_flag); + + chk_flag = (g_Settings->m_core.FlagsLLE & LLE_APU) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_EMULATION_LLE_APU, chk_flag); + + chk_flag = (g_Settings->m_core.FlagsLLE & LLE_GPU) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_EMULATION_LLE_GPU, chk_flag); + + //chk_flag = (g_Settings->m_core.FlagsLLE & LLE_USB) ? MF_CHECKED : MF_UNCHECKED; // Reenable this when LLE USB actually works + //CheckMenuItem(settings_menu, ID_EMULATION_LLE_USB, chk_flag); + + chk_flag = g_Settings->m_core.bUseLoaderExec ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_USELOADEREXEC, chk_flag); + + chk_flag = (g_Settings->m_hacks.DisablePixelShaders) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_HACKS_DISABLEPIXELSHADERS, chk_flag); + + chk_flag = (g_Settings->m_hacks.UseAllCores) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_HACKS_RUNXBOXTHREADSONALLCORES, chk_flag); + + chk_flag = (g_Settings->m_hacks.SkipRdtscPatching) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_HACKS_SKIPRDTSCPATCHING, chk_flag); + + switch (g_Settings->m_gui.DataStorageToggle) { + case CXBX_DATA_APPDATA: + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_CHECKED); + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCEXECDIR, MF_UNCHECKED); + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCCUSTOM, MF_UNCHECKED); + break; + + case CXBX_DATA_EXECDIR: + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_UNCHECKED); + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCEXECDIR, MF_CHECKED); + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCCUSTOM, MF_UNCHECKED); + break; + + case CXBX_DATA_CUSTOM: + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_UNCHECKED); + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCEXECDIR, MF_UNCHECKED); + CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCCUSTOM, MF_CHECKED); + break; + } + + chk_flag = (g_Settings->m_gui.bIgnoreInvalidXbeSig) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_SETTINGS_IGNOREINVALIDXBESIG, chk_flag); + + chk_flag = (g_Settings->m_gui.bIgnoreInvalidXbeSec) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_SETTINGS_IGNOREINVALIDXBESEC, chk_flag); + + chk_flag = (g_Settings->m_core.allowAdminPrivilege) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(settings_menu, ID_SETTINGS_ALLOWADMINPRIVILEGE, chk_flag); + } + + // emulation menu + { + HMENU emul_menu = GetSubMenu(menu, 4); + + // enable emulation start + EnableMenuItem(emul_menu, ID_EMULATION_START, MF_BYCOMMAND | MF_WhenXbeLoadedNotRunning); + + // enable emulation with debugging + EnableMenuItem(emul_menu, ID_EMULATION_STARTDEBUGGER, MF_BYCOMMAND | MF_WhenXbeLoadedNotRunning); + + // enable emulation stop + EnableMenuItem(emul_menu, ID_EMULATION_STOP, MF_BYCOMMAND | MF_WhenXbeLoadedAndRunning); + } + } + // NOTE: Must force draw menu bar since sometime status doesn't show the new change. + DrawMenuBar(m_hwnd); +} + +// update debug consoles +void WndMain::UpdateDebugConsoles() +{ +#ifdef _WINDOWS_ + HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + switch (g_Settings->m_gui.CxbxDebugMode) { + case DM_CONSOLE: + if (AllocConsole()) { + std::freopen("CONOUT$", "wt", stdout); + + SetConsoleTitle("Cxbx-Reloaded : Debug Console"); + + SetConsoleTextAttribute(stdHandle, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); + + std::printf("%s", "WndMain: Debug console allocated.\n"); + + SetForegroundWindow(m_hwnd); + } + break; + + case DM_FILE: + + std::freopen(g_Settings->m_gui.szCxbxDebugFile.c_str(), "wt", stdout); + FreeConsole(); + + std::printf("%s", "WndMain: Debug console allocated.\n"); + break; + + default: + + if (GetConsoleWindow() != NULL) { + std::fclose(stdout); + FreeConsole(); + } + std::freopen("nul", "w", stdout); + + break; + } + + // NOTE: This is a Windows fix for ability to get std::cout to output onto console/file. + // Not sure if linux/unix is affected too. +#ifdef _WINDOWS_ + if (stdHandle != INVALID_HANDLE_VALUE) { + int fileDesc = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); + + if (fileDesc != -1) { + FILE* file = _fdopen(fileDesc, "wt"); + + if (file != nullptr) { + int dup2Result = _dup2(_fileno(file), _fileno(stdout)); + if (dup2Result == 0) { + std::setvbuf(stdout, nullptr, _IONBF, 0); + } + std::fclose(file); + } + } + } + std::wcout.clear(); + std::cout.clear(); +#endif +} + +// update recent files menu +void WndMain::UpdateRecentFiles() +{ + HMENU FileMenu = GetSubMenu(GetMenu(m_hwnd), 0); + HMENU RXbeMenu = GetSubMenu(FileMenu, 7); + + // clear existing menu items + { + int v, max; + + max = GetMenuItemCount(RXbeMenu); + for(v=0;vm_gui.szRecentXbeFiles[v].c_str()); + AppendMenu(RXbeMenu, MF_STRING, ID_FILE_RXBE_0 + v, szBuffer); + } + } +} + +extern std::string FormatTitleId(uint32_t title_id); + +void WndMain::UpdateCaption() +{ + char AsciiTitle[MAX_PATH]; + + int i = sprintf(AsciiTitle, "Cxbx-Reloaded %s", CxbxVersionStr); + if (m_Xbe != nullptr) { + if (m_bIsStarted) { + i += sprintf(AsciiTitle + i, " : Emulating "); + } + else { + i += sprintf(AsciiTitle + i, " : Loaded "); + } + + i += sprintf(AsciiTitle + i, "%s v1.%02d (%s)", FormatTitleId(m_Xbe->m_Certificate.dwTitleId).c_str(), m_Xbe->m_Certificate.dwVersion, m_Xbe->m_szAsciiTitle); + + UpdateFpsStatus(); + UpdateLogStatus(); + + } + + SetWindowText(m_hwnd, AsciiTitle); +} + +void WndMain::UpdateFpsStatus() +{ + // Append FPS menu text + HMENU hMenu = GetMenu(m_hwnd); + MENUITEMINFO mii; + mii.cbSize = sizeof mii; + mii.fMask = MIIM_STRING; + char sMenu[32]; + mii.dwTypeData = &sMenu[0]; + + if (m_bIsStarted) { + if (g_EmuShared != nullptr) { + g_EmuShared->GetCurrentFPS(&m_FPS_status); + + if (m_FPS_status == 0.0f) { + m_MSpF_status = 0.0f; + } + else { + m_MSpF_status = (float)(1000.0 / m_FPS_status); + } + std::sprintf(sMenu, "FPS: %.2f MS / F : %.2f", m_FPS_status, m_MSpF_status); + } + } + else { + // Hide FPS if we're not currently emulating + std::sprintf(sMenu, " "); + } + + SetMenuItemInfo(hMenu, ID_FPS, FALSE, &mii); +} + +void WndMain::UpdateLogStatus() +{ + // Append FPS menu text + char sMenu[32]; + HMENU hMenu = GetMenu(m_hwnd); + MENUITEMINFO mii; + mii.cbSize = sizeof mii; + mii.fMask = MIIM_STRING; + mii.dwTypeData = &sMenu[0]; + + std::strcpy(sMenu, "LOG:"); + + if (g_Settings->m_gui.CxbxDebugMode != DebugMode::DM_NONE) { + std::strcat(sMenu, "G"); + } + + if (m_bIsStarted && m_LogKrnl_status) { + std::strcat(sMenu, "K"); + } + + SetMenuItemInfo(hMenu, ID_LOG, FALSE, &mii); +} + +void WndMain::RefreshAllStatus() +{ + UpdateFpsStatus(); + UpdateLogStatus(); + DrawMenuBar(m_hwnd); +} + +// open an xbe file +void WndMain::OpenXbe(const char *x_filename) +{ + if (m_Xbe != nullptr) { + CloseXbe(); + if (m_Xbe != nullptr) + return; + } + + strcpy(m_XbeFilename, x_filename); + + m_Xbe = new Xbe(m_XbeFilename, true); + + if(m_Xbe->HasError()) + { + // Save the error message as a separate string. This fixes a corruption in the message "Disclaimer: Cxbx-Reloaded has no + // affiliation with Microsoft" that would occur if loading an xbe and then launching the dashboard with the "Open dashboard" + // option but it's not installed + + std::string ErrorMessage = m_Xbe->GetError(); + + delete m_Xbe; m_Xbe = nullptr; + + RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); + + PopupError(m_hwnd, ErrorMessage.c_str()); + + UpdateCaption(); + + return; + } + + std::string errorMsg; + + if (!g_Settings->m_gui.bIgnoreInvalidXbeSig && !m_Xbe->CheckSignature()) { + errorMsg += "- XBE signature check failed!\n"; + } + + if (!g_Settings->m_gui.bIgnoreInvalidXbeSec) { + for (uint32_t sectionIndex = 0; sectionIndex < m_Xbe->m_Header.dwSections; sectionIndex++) { + if (!m_Xbe->CheckSectionIntegrity(sectionIndex)) { + errorMsg += "- One or more XBE section(s) are corrupted!\n"; + + // if we find a corrupted section, we won't bother checking the remaining sections since we know + // already at this point that the xbe is invalid + break; + } + } + } + + if (!errorMsg.empty()) { + errorMsg += ("\nThis is dangerous, as maliciously modified Xbox applications could take control of your system." + "\nPlease do not report issues for this application.\n" + "\nAre you sure you want to continue?"); + + PopupReturn ret = PopupWarningEx(m_hwnd, PopupButtons::YesNo, PopupReturn::No, errorMsg.c_str()); + if (ret != PopupReturn::Yes) + { + delete m_Xbe; m_Xbe = nullptr; + + RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); + + UpdateCaption(); + + return; + } + } + + // save this xbe to the list of recent xbe files + if(m_XbeFilename[0] != '\0') { + bool found = false; + + // if this filename already exists, temporarily remove it + for(int c=0, r=0;cm_gui.szRecentXbeFiles[c].c_str(), m_XbeFilename) == 0) { + found = true; + r++; + } + + if(r != c) { + if(g_Settings->m_gui.szRecentXbeFiles[r].c_str() == 0 || r > m_dwRecentXbe - 1) { + g_Settings->m_gui.szRecentXbeFiles[c] = ""; + } + else { + g_Settings->m_gui.szRecentXbeFiles[c] = g_Settings->m_gui.szRecentXbeFiles[r]; + } + } + } + + if (found) { + m_dwRecentXbe--; + } + + // move all items down one, removing the last one if necessary + for (int v = RECENT_XBE_LIST_MAX - 1;v > 0; v--) { + + if(g_Settings->m_gui.szRecentXbeFiles[v-1].size() == 0) { + g_Settings->m_gui.szRecentXbeFiles[v] = ""; + } + else { + g_Settings->m_gui.szRecentXbeFiles[v] = g_Settings->m_gui.szRecentXbeFiles[v - 1]; + } + } + + // add new item as first index + g_Settings->m_gui.szRecentXbeFiles[0] = m_XbeFilename; + + if (m_dwRecentXbe < RECENT_XBE_LIST_MAX) { + m_dwRecentXbe++; + } + } + + UpdateRecentFiles(); + + XbeLoaded(); +} + +// close xbe file +void WndMain::CloseXbe() +{ + if (m_bIsStarted) + StopEmulation(); + + if(m_bXbeChanged) + { + PopupReturn ret = PopupQuestion(m_hwnd, "Changes have been made, do you wish to save?"); + + if(ret == PopupReturn::Yes) + SaveXbeAs(); + else if(ret == PopupReturn::Cancel) + return; + } + + printf("WndMain: %s unloaded.\n", m_Xbe->m_szAsciiTitle); + + m_bXbeChanged = false; + + delete m_Xbe; m_Xbe = nullptr; + + UpdateCaption(); + RefreshMenus(); + + DebuggerMonitorClose(); + + // clear logo bitmap + { + uint32_t v=0; + for(uint32_t y=0;y<17;y++) + { + for(uint32_t x=0;x<100;x++) + { + SetPixel(m_LogoDC, x, y, RGB(0, 0, 0)); + v++; + } + } + } + + // clear game logo bitmap + SelectObject(m_GameLogoDC, m_OrigGameLogo); + DeleteObject(m_GameLogoDC); + DeleteObject(m_GameLogoBMP); + + RedrawWindow(m_hwnd, nullptr, NULL, RDW_INVALIDATE); +} + +void WndMain::OpenMRU(int mru) +{ + HMENU menu = GetMenu(m_hwnd); + HMENU file_menu = GetSubMenu(menu, 0); + HMENU rxbe_menu = GetSubMenu(file_menu, 7); + + char szBuffer[270]; + + GetMenuString(rxbe_menu, ID_FILE_RXBE_0 + mru, szBuffer, 269, MF_BYCOMMAND); + + char *szFilename = (char*)((uint32_t)szBuffer + 5); // +5 skips over "&%d : " prefix (see UpdateRecentFiles) + + OpenXbe(szFilename); +} + +// Open the dashboard xbe if found +void WndMain::OpenDashboard() +{ + std::string DashboardPath = g_Settings->GetDataLocation() + std::string("\\EmuDisk\\Partition2\\xboxdash.xbe"); + OpenXbe(DashboardPath.c_str()); +} + +// save xbe file +void WndMain::SaveXbe(const char *x_filename) +{ + // ask permission to overwrite if the file already exists + if(_access(x_filename, 0) != -1) + { + if(PopupQuestionEx(m_hwnd, LOG_LEVEL::INFO, PopupButtons::YesNo, PopupReturn::No, "Overwrite existing file?") != PopupReturn::Yes) + return; + } + + // export xbe file + { + m_Xbe->Export(x_filename); + + if(m_Xbe->HasError()) + PopupError(m_hwnd, m_Xbe->GetError().c_str()); + else + { + char buffer[255]; + + sprintf(buffer, "%s was successfully saved.", m_Xbe->m_szAsciiTitle); + + PopupInfo(m_hwnd, buffer); + + m_bXbeChanged = false; + } + } +} + +// save xbe as +void WndMain::SaveXbeAs() +{ + OPENFILENAME ofn = {0}; + + char filename[MAX_PATH] = "default.xbe"; + + SuggestFilename(m_XbeFilename, filename, ".xbe"); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = m_hwnd; + ofn.lpstrFilter = "Xbox Executables (*.xbe)\0*.xbe\0"; + ofn.lpstrFile = filename; + ofn.nMaxFile = MAX_PATH; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = nullptr; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = nullptr; + ofn.lpstrDefExt = "xbe"; + ofn.Flags = OFN_PATHMUSTEXIST; + + if(GetSaveFileName(&ofn) == TRUE) + SaveXbe(ofn.lpstrFile); +} + +// Only grant access to GUI end. +namespace cli_config { +extern void SetValue(const std::string key, const std::string value); +extern void SetValue(const std::string key, const char* value); +extern void SetValue(const std::string key, const void* value); +extern void SetValue(const std::string key, int value); +} +// start emulation +void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState /*= debuggerOff*/) +{ + bool isEmulating = false; + + g_EmuShared->GetIsEmulating(&isEmulating); + + if (isEmulating) { + PopupError(m_hwnd, "A title is currently emulating, please stop emulation before attempting to start again."); + return; + } + + // Reset to default + g_EmuShared->Reset(); + m_FPS_status = 0.0f; + m_MSpF_status = 0.0f; + m_FlagsLLE_status = g_Settings->m_core.FlagsLLE; + m_LogKrnl_status = g_Settings->m_core.KrnlDebugMode != DebugMode::DM_NONE; + + // register all emulator settings to kernel process + g_Settings->SyncToEmulator(); + + // Preserve previous GUI window location. + HWND hOwner = GetParent(m_hwnd); + RECT curWindowPos; + GetWindowRect((hOwner != nullptr ? hOwner : m_hwnd), &curWindowPos); + m_prevWindowLoc.x = curWindowPos.left; + m_prevWindowLoc.y = curWindowPos.top; + ScreenToClient((hOwner != nullptr ? hOwner : m_hwnd), &m_prevWindowLoc); + m_prevWindowLoc.x = curWindowPos.left - m_prevWindowLoc.x; + m_prevWindowLoc.y = curWindowPos.top - m_prevWindowLoc.y; + + // Set the window size to emulation dimensions (The configured 'display' resolution) + // Note : Doing this here assures the emulation process will use + // the configured dimensions (because if done inside the emulation + // process, that doesn't resize correctly sometimes) + // We always resize, as we no longer tie host resolution to Xbox resolution + // 'Higher Resolution' rendering is handled as a scale factor. + ResizeWindow(m_hwnd, /*bForGUI*/false); + + // shell exe + { + + char szExeFileName[MAX_PATH]; + GetModuleFileName(GetModuleHandle(nullptr), szExeFileName, MAX_PATH); + if (g_Settings->m_core.bUseLoaderExec) { + PathRemoveFileSpec(szExeFileName); + PathAppend(szExeFileName, "\\cxbxr-ldr.exe"); + } + + bool AttachLocalDebugger = (LocalDebuggerState == debuggerOn); + g_EmuShared->SetDebuggingFlag(&AttachLocalDebugger); + + /* Main process to generate emulation command line begin. */ + // If we are adding more arguments, this is the place to do so. + cli_config::SetValue(cli_config::exec, szExeFileName); + cli_config::SetLoad(m_XbeFilename); + cli_config::SetValue(cli_config::hwnd, hwndParent); + cli_config::SetValue(cli_config::debug_mode, g_Settings->m_core.KrnlDebugMode); + if (g_Settings->m_core.KrnlDebugMode == DM_FILE) { + cli_config::SetValue(cli_config::debug_file, g_Settings->m_core.szKrnlDebug); + } + else { + cli_config::SetValue(cli_config::debug_file, ""); + } + /* Main process to generate emulation command line end. */ + + if (AttachLocalDebugger) { + + // Check then close existing debugger monitor. + DebuggerMonitorClose(); + + if (!CxbxExec(true, &m_hDebuggerProc, true)) { + PopupError(m_hwnd, "Failed to start emulation with the debugger.\n\nYou will need to build CxbxDebugger manually."); + + printf("WndMain: %s debugger shell failed.\n", m_Xbe->m_szAsciiTitle); + } + else { + m_bIsStarted = true; + printf("WndMain: %s emulation started with debugger.\n", m_Xbe->m_szAsciiTitle); + m_hDebuggerMonitorThread = std::thread(DebuggerMonitor, this); // create the debugger monitoring thread + } + } + else { + + if (!CxbxExec(false, nullptr, false)) { + PopupError(m_hwnd, "Emulation failed.\n\n If this message repeats, the Xbe is not supported."); + + printf("WndMain: %s shell failed.\n", m_Xbe->m_szAsciiTitle); + } + else { + m_bIsStarted = true; + printf("WndMain: %s emulation started.\n", m_Xbe->m_szAsciiTitle); + } + } + } +} + +// stop emulation +void WndMain::StopEmulation() +{ + m_bIsStarted = false; + if (m_hwndChild != NULL) { + if (IsWindow(m_hwndChild)) { + SendMessage(m_hwndChild, WM_CLOSE, 0, 0); + } + + m_hwndChild = NULL; + } + + UpdateCaption(); + RefreshMenus(); + + // Set the window size back to it's GUI dimensions + ResizeWindow(m_hwnd, /*bForGUI=*/true); + + g_EmuShared->SetIsEmulating(false); +} + +// wrapper function to call CrashMonitor +DWORD WndMain::CrashMonitorWrapper(LPVOID lpParam) +{ + CxbxSetThreadName("Cxbx Crash Monitor"); + + Crash_Manager_Data* pCMD = (Crash_Manager_Data*)lpParam; + static_cast(pCMD->pWndMain)->CrashMonitor(pCMD->dwChildProcID); + free(lpParam); + + return 0; +} + +// monitor for crashes +void WndMain::CrashMonitor(DWORD dwChildProcID) +{ + int iBootFlags; + DWORD dwExitCode = 0; + + // If we do receive valid process ID, let's do the next step. + if (dwChildProcID != 0) { + + HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, dwChildProcID); + + // If we do receive valid handle, let's do the next step. + if (hProcess != NULL) { + + WaitForSingleObject(hProcess, INFINITE); + + GetExitCodeProcess(hProcess, &dwExitCode); + CloseHandle(hProcess); + + g_EmuShared->GetBootFlags(&iBootFlags); + + if (!iBootFlags) { + if (dwExitCode == EXIT_SUCCESS) {// StopEmulation + return; + } + // Or else, it's a crash + } + else { + + // multi-xbe + // destroy this thread and start a new one + return; + } + } + } + + // Crash clean up. + + KillTimer(m_hwnd, TIMERID_ACTIVE_EMULATION); + KillTimer(m_hwnd, TIMERID_LED); + m_hwndChild = NULL; + m_bIsStarted = false; + g_EmuShared->SetIsEmulating(false); + UpdateCaption(); + RefreshMenus(); + DrawLedBitmap(m_hwnd, true); +} + +// monitor for Debugger to close then set as "available" (For limit to 1 debugger per Cxbx GUI.) +DWORD WndMain::DebuggerMonitor(LPVOID lpVoid) +{ + CxbxSetThreadName("Cxbx Debugger Monitor"); + WndMain* pThis = static_cast(lpVoid); + + if (pThis->m_hDebuggerProc != nullptr) { + + // Peform a wait until Debugger is closed. + WaitForSingleObject(pThis->m_hDebuggerProc, INFINITE); + + if (pThis->m_hDebuggerProc != nullptr) { + CloseHandle(pThis->m_hDebuggerProc); + pThis->m_hDebuggerProc = nullptr; + } + } + + if (pThis->m_hDebuggerMonitorThread.joinable()) { + pThis->m_hDebuggerMonitorThread.detach(); + } + + return 0; +} +void WndMain::DebuggerMonitorClose() +{ + if (m_hDebuggerProc != nullptr) { + HANDLE hDebuggerProcTemp = m_hDebuggerProc; + std::thread hDebuggerMonitorThreadTemp = std::thread(std::move(m_hDebuggerMonitorThread)); + + // Set member to null pointer before terminate, this way debugger monitor thread will remain thread-safe. + m_hDebuggerProc = nullptr; + + + TerminateProcess(hDebuggerProcTemp, EXIT_SUCCESS); + CloseHandle(hDebuggerProcTemp); + + hDebuggerMonitorThreadTemp.join(); + } +} + +// draw Xbox LED bitmap +void WndMain::DrawLedBitmap(HWND hwnd, bool bdefault) +{ + HMENU hMenu = GetMenu(hwnd); + int ActiveLEDColor; + + MENUITEMINFO mii = { 0 }; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE | MIIM_BITMAP | MIIM_STRING; + char flagString[10] = "LLE-"; + mii.dwTypeData = &flagString[0]; + mii.fType = MFT_RIGHTJUSTIFY; + mii.hbmpItem = m_LedBmp; + + // When so requested, or when not emulating, draw a black bitmap and hide LLE flags string + if (bdefault || !m_bIsStarted) { + ActiveLEDColor = XBOX_LED_COLOUR_OFF; + sprintf(flagString, " "); + } + else { // draw colored bitmap + static int LedSequenceOffset = 0; + unsigned int FlagsLLE = 0; + + // Select active color and cycle through all 4 phases in the sequence + ActiveLEDColor = m_LedSeq_status[LedSequenceOffset & 3]; + ++LedSequenceOffset; + + // Set LLE flags string based on selected LLE flags + if (m_FlagsLLE_status & LLE_APU) { + strcat(flagString, "A"); + } + if (m_FlagsLLE_status & LLE_GPU) { + strcat(flagString, "G"); + } + if (m_FlagsLLE_status & LLE_USB) { + strcat(flagString, "U"); + } + if (m_FlagsLLE_status & LLE_JIT) { + strcat(flagString, "J"); + } + if (m_FlagsLLE_status == 0) { + sprintf(flagString, "HLE"); + } + } + + SelectObject(m_LedDC, m_Brushes[ActiveLEDColor]); + SelectObject(m_LedDC, m_Pens[ActiveLEDColor]); + + m_OriLed = (HBITMAP)SelectObject(m_LedDC, m_LedBmp); + Rectangle(m_LedDC, 0, 0, m_xBmp, m_yBmp); + m_LedBmp = (HBITMAP)SelectObject(m_LedDC, m_OriLed); + + SetMenuItemInfo(hMenu, ID_LED, FALSE, &mii); + + DrawMenuBar(hwnd); + + return; +} diff --git a/src/gui/WndMain.h b/src/gui/WndMain.h index 6fa7a5a0c..420bde292 100644 --- a/src/gui/WndMain.h +++ b/src/gui/WndMain.h @@ -26,17 +26,17 @@ #define WNDMAIN_H #include "Wnd.h" -#include "common\xbe\Xbe.h" - +#include "common\xbe\Xbe.h" + #include // ****************************************************************** // * constants // ****************************************************************** -#define RECENT_XBE_LIST_MAX 10 - -typedef struct _Crash_Manager_Data { - LPVOID pWndMain; +#define RECENT_XBE_LIST_MAX 10 + +typedef struct _Crash_Manager_Data { + LPVOID pWndMain; DWORD dwChildProcID; } Crash_Manager_Data; @@ -197,8 +197,8 @@ class WndMain : public Wnd // ****************************************************************** // * cached window, process, and thread handle // ****************************************************************** - HWND m_hwndChild; - HANDLE m_hDebuggerProc; + HWND m_hwndChild; + HANDLE m_hDebuggerProc; std::thread m_hDebuggerMonitorThread; // ****************************************************************** @@ -209,24 +209,24 @@ class WndMain : public Wnd // ****************************************************************** // * is this window fully initialized? // ****************************************************************** - bool m_bCreated; - - // ****************************************************************** - // * Previous GUI window location (before start emulation) - // ****************************************************************** - POINT m_prevWindowLoc; - - // ****************************************************************** - // * Kernel process status - // ****************************************************************** - float m_FPS_status; - float m_MSpF_status; - union { - UINT m_LedSeq_status_block; - UCHAR m_LedSeq_status[4]; - }; - UINT m_FlagsLLE_status; + bool m_bCreated; + + // ****************************************************************** + // * Previous GUI window location (before start emulation) + // ****************************************************************** + POINT m_prevWindowLoc; + + // ****************************************************************** + // * Kernel process status + // ****************************************************************** + float m_FPS_status; + float m_MSpF_status; + union { + UINT m_LedSeq_status_block; + UCHAR m_LedSeq_status[4]; + }; + UINT m_FlagsLLE_status; bool m_LogKrnl_status; }; -#endif +#endif diff --git a/src/gui/resource/Cxbx.rc b/src/gui/resource/Cxbx.rc index cc62b8c01..c45e44164 100644 --- a/src/gui/resource/Cxbx.rc +++ b/src/gui/resource/Cxbx.rc @@ -1,718 +1,718 @@ -// Microsoft Visual C++ generated resource script. -// -#include "ResCxbx.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "WinResrc.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Neutral resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_VIRTUAL_SBC_FEEDBACK, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 1032 - TOPMARGIN, 7 - BOTTOMMARGIN, 207 - END - - IDD_VIDEO_CFG, DIALOG - BEGIN - BOTTOMMARGIN, 121 - END - - IDD_AUDIO_CFG, DIALOG - BEGIN - END - - IDD_NETWORK_CFG, DIALOG - BEGIN - BOTTOMMARGIN, 74 - END - - IDD_EEPROM_CFG, DIALOG - BEGIN - END - - IDD_LOGGING_CFG, DIALOG - BEGIN - VERTGUIDE, 12 - VERTGUIDE, 66 - VERTGUIDE, 121 - VERTGUIDE, 178 - VERTGUIDE, 234 - VERTGUIDE, 246 - BOTTOMMARGIN, 347 - HORZGUIDE, 54 - HORZGUIDE, 69 - HORZGUIDE, 84 - HORZGUIDE, 99 - HORZGUIDE, 114 - HORZGUIDE, 126 - HORZGUIDE, 140 - HORZGUIDE, 155 - HORZGUIDE, 168 - HORZGUIDE, 182 - HORZGUIDE, 195 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_INPUT_CFG DIALOGEX 0, 0, 243, 124 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cxbx-Reloaded : Input Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - GROUPBOX "Xbox device configuration",IDC_XID_CONFIG,13,10,217,103,WS_GROUP,WS_EX_CLIENTEDGE - COMBOBOX IDC_DEVICE_PORT1,50,25,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_DEVICE_PORT2,50,46,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_DEVICE_PORT3,50,67,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_DEVICE_PORT4,50,88,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_PORT1,166,25,50,14,BS_FLAT - PUSHBUTTON "Configure",IDC_CONFIGURE_PORT2,166,46,50,14,BS_FLAT - PUSHBUTTON "Configure",IDC_CONFIGURE_PORT3,166,67,50,14,BS_FLAT - PUSHBUTTON "Configure",IDC_CONFIGURE_PORT4,166,88,50,14,BS_FLAT - LTEXT "Port 1",IDC_STATIC,23,27,20,10 - LTEXT "Port 2",IDC_STATIC,23,48,20,10 - LTEXT "Port 3",IDC_STATIC,23,69,20,10 - LTEXT "Port 4",IDC_STATIC,23,90,20,10 -END - -IDD_XID_DUKE_CFG DIALOGEX 0, 0, 528, 280 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - GROUPBOX "Device",IDC_XID_CONFIG,12,10,175,35,WS_GROUP - COMBOBOX IDC_DEVICE_LIST,21,23,101,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Refresh",IDC_REFRESH_DEVICES,128,23,50,14,BS_FLAT - GROUPBOX "Profile",IDC_XID_PROFILE,197,10,320,35,WS_GROUP - COMBOBOX IDC_XID_PROFILE_NAME,207,24,194,10,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Save",IDC_XID_PROFILE_SAVE,405,23,50,14,BS_FLAT - PUSHBUTTON "Delete",IDC_XID_PROFILE_DELETE,459,23,50,14,BS_FLAT - GROUPBOX "Buttons",IDC_XID_BUTTONS,12,65,121,181,WS_GROUP - PUSHBUTTON "",IDC_SET_A,59,75,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_B,59,93,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_X,59,111,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_Y,59,129,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_BLACK,59,147,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_WHITE,59,165,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_BACK,59,183,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_START,59,201,57,14,BS_FLAT - LTEXT "A",IDC_STATIC,28,75,20,14,SS_CENTERIMAGE - LTEXT "B",IDC_STATIC,28,93,20,14,SS_CENTERIMAGE - LTEXT "X",IDC_STATIC,28,111,20,14,SS_CENTERIMAGE - LTEXT "Y",IDC_STATIC,28,129,20,14,SS_CENTERIMAGE - LTEXT "Black",IDC_STATIC,28,147,20,14,SS_CENTERIMAGE - LTEXT "White",IDC_STATIC,28,165,20,14,SS_CENTERIMAGE - LTEXT "Back",IDC_STATIC,28,183,20,14,SS_CENTERIMAGE - LTEXT "Start",IDC_STATIC,28,201,20,14,SS_CENTERIMAGE - GROUPBOX "L Stick",IDC_XID_LSTICK,140,65,121,181,WS_GROUP - PUSHBUTTON "",IDC_SET_LEFT_POSY,187,75,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_LEFT_NEGY,187,93,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_LEFT_NEGX,187,111,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_LEFT_POSX,187,129,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_LTHUMB,187,147,57,14,BS_FLAT - LTEXT "Up",IDC_STATIC,156,75,20,14,SS_CENTERIMAGE - LTEXT "Down",IDC_STATIC,156,93,20,14,SS_CENTERIMAGE - LTEXT "Left",IDC_STATIC,156,111,20,14,SS_CENTERIMAGE - LTEXT "Right",IDC_STATIC,156,129,20,14,SS_CENTERIMAGE - LTEXT "L Click",IDC_STATIC,156,147,27,14,SS_CENTERIMAGE - GROUPBOX "R Stick",IDC_XID_RSTICK,268,65,121,181,WS_GROUP - PUSHBUTTON "",IDC_SET_RIGHT_POSY,315,75,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_RIGHT_NEGY,315,93,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_RIGHT_NEGX,315,111,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_RIGHT_POSX,315,129,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_RTHUMB,315,147,57,14,BS_FLAT - LTEXT "Up",IDC_STATIC,284,75,20,14,SS_CENTERIMAGE - LTEXT "Down",IDC_STATIC,284,93,20,14,SS_CENTERIMAGE - LTEXT "Left",IDC_STATIC,284,111,20,14,SS_CENTERIMAGE - LTEXT "Right",IDC_STATIC,284,129,20,14,SS_CENTERIMAGE - LTEXT "R Click",IDC_STATIC,284,147,27,14,SS_CENTERIMAGE - GROUPBOX "D Pad",IDC_XID_DPAD,396,65,121,88,WS_GROUP - PUSHBUTTON "",IDC_SET_DPAD_UP,443,75,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_DPAD_DOWN,443,93,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_DPAD_LEFT,443,111,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_DPAD_RIGHT,443,129,57,14,BS_FLAT - LTEXT "Up",IDC_STATIC,412,75,20,14,SS_CENTERIMAGE - LTEXT "Down",IDC_STATIC,412,93,20,14,SS_CENTERIMAGE - LTEXT "Left",IDC_STATIC,412,111,20,14,SS_CENTERIMAGE - LTEXT "Right",IDC_STATIC,412,129,20,14,SS_CENTERIMAGE - GROUPBOX "Triggers",IDC_XID_TRIGGERS,396,157,121,52,WS_GROUP - PUSHBUTTON "",IDC_SET_LTRIGGER,443,168,57,14,BS_FLAT - PUSHBUTTON "",IDC_SET_RTRIGGER,443,187,57,14,BS_FLAT - LTEXT "Left",IDC_STATIC,412,168,20,14,SS_CENTERIMAGE - LTEXT "Right",IDC_STATIC,412,187,20,14,SS_CENTERIMAGE - GROUPBOX "Rumble",IDC_XID_RUMBLE,396,212,121,34,WS_GROUP - PUSHBUTTON "",IDC_SET_MOTOR,443,224,57,14,BS_FLAT - LTEXT "Motor",IDC_STATIC,412,224,26,14,SS_CENTERIMAGE - PUSHBUTTON "Default Bindings",IDC_XID_DEFAULT,362,256,69,14,BS_FLAT - PUSHBUTTON "Clear",IDC_XID_CLEAR,443,256,50,14,BS_FLAT -END - -IDD_RUMBLE_CFG DIALOGEX 0, 0, 155, 35 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Rumble Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - COMBOBOX IDC_RUMBLE_LIST,15,11,70,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Test",IDC_RUMBLE_TEST,95,11,45,14,BS_FLAT -END - -IDD_VIRTUAL_SBC_FEEDBACK DIALOGEX 0, 0, 1039, 214 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Virtual SteelBattalion Controller" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Left Block",IDC_LEFTBLOCK,7,7,329,200,0,WS_EX_CLIENTEDGE | WS_EX_RIGHT - GROUPBOX "Left Block",IDC_LEFTBLOCK2,7,7,329,200,0,WS_EX_RIGHT - GROUPBOX "Right Block",IDC_RIGHTBLOCK,703,7,329,200,0,WS_EX_CLIENTEDGE - GROUPBOX "Gear Lever",IDC_GEAR_LEVER,24,25,92,174,WS_GROUP,WS_EX_CLIENTEDGE - GROUPBOX "Toggle Switches",IDC_STATIC,131,102,191,95,0,WS_EX_CLIENTEDGE - CONTROL "N",IDC_GEAR_0,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,37,131,21,10 - CONTROL "1",IDC_GEAR_1,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,113,20,10 - CONTROL "2",IDC_GEAR_2,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,95,20,10 - CONTROL "3",IDC_GEAR_3,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,77,20,10 - CONTROL "4",IDC_GEAR_4,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,59,20,10 - CONTROL "5",IDC_GEAR_5,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,41,20,10 - CONTROL "R",IDC_GEAR_R,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,37,149,21,10 - CONTROL "",IDC_ROTATION_LEVER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,129,38,188,15 - CTEXT "Rotation Lever",IDC_TXT_ROTATION_LEVER,199,27,48,8 - CONTROL "VT-LOCATION MEASUREMENT",IDC_VT_LOCATION_MEASUREMENT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,208,120,113,10 - CONTROL "BUFFER MATERIAL",IDC_BUFFER_MATERIAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,208,150,77,10 - CONTROL "FUEL FLOW RATE",IDC_FUEL_FLOW_RATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,208,180,73,10 - CONTROL "OXYGEN SUPPLY SYSTEM",IDC_OXYGEN_SUPPLY_SYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,135,96,10 - CONTROL "FILT CONTROL SYSTEM",IDC_FILT_CONTROL_SYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,165,91,10 - PUSHBUTTON "Eject",IDC_BTN_EJECT,958,42,60,11 - PUSHBUTTON "COCKPIT HATCH",IDC_BTN_COCKPIT_HATCH,958,93,60,12,BS_BOTTOM - PUSHBUTTON "IGNITION",IDC_BTN_IGNITION,958,144,60,11,BS_BOTTOM - PUSHBUTTON "START",IDC_BTN_START,958,192,60,10,BS_BOTTOM - CONTROL "",IDC_PB_EJECT,"msctls_progress32",WS_BORDER,958,15,60,26 - CONTROL "",IDC_PB_COCKPIT_HATCH,"msctls_progress32",WS_BORDER,958,67,60,26 - CONTROL "",IDC_PB_IGNITION,"msctls_progress32",WS_BORDER,958,117,60,26 - CONTROL "",IDC_PB_START,"msctls_progress32",WS_BORDER,958,165,60,26 - CONTROL "",IDC_PB_GEARLEVER_5,"msctls_progress32",WS_BORDER,62,41,29,10 - CONTROL "",IDC_PB_GEARLEVER_1,"msctls_progress32",WS_BORDER,62,113,29,10 - CONTROL "",IDC_PB_GEARLEVER_2,"msctls_progress32",WS_BORDER,62,95,29,10 - CONTROL "",IDC_PB_GEARLEVER_3,"msctls_progress32",WS_BORDER,62,77,29,10 - CONTROL "",IDC_PB_GEARLEVER_4,"msctls_progress32",WS_BORDER,62,59,29,10 - CONTROL "",IDC_PB_GEARLEVER_N,"msctls_progress32",WS_BORDER,62,131,29,10 - CONTROL "",IDC_PB_GEARLEVER_R,"msctls_progress32",WS_BORDER,62,149,29,10 - CONTROL "",IDC_PB_COM5,"msctls_progress32",WS_BORDER,455,12,14,27 - CONTROL "",IDC_PB_COM1,"msctls_progress32",WS_BORDER,343,12,14,27 - CONTROL "",IDC_PB_COM2,"msctls_progress32",WS_BORDER,371,12,14,27 - CONTROL "",IDC_PB_COM3,"msctls_progress32",WS_BORDER,399,12,14,27 - CONTROL "",IDC_PB_COM4,"msctls_progress32",WS_BORDER,427,12,14,27 - CONTROL "",IDC_PB_WASHING,"msctls_progress32",WS_BORDER,359,151,86,13 - CONTROL "",IDC_PB_MAIN_WEAPON_CONTROL,"msctls_progress32",WS_BORDER,359,179,86,13 - CONTROL "",IDC_PB_EXTINGUISHER,"msctls_progress32",WS_BORDER,481,151,86,13 - CONTROL "",IDC_PB_SUB_WEAPON_CONTROL,"msctls_progress32",WS_BORDER,481,179,86,13 - CONTROL "",IDC_PB_CHAFF,"msctls_progress32",WS_BORDER,603,151,86,13 - CONTROL "",IDC_PB_MAGAZINE_CHANGE,"msctls_progress32",WS_BORDER,603,179,86,13 - CONTROL "",IDC_PB_FUNC1,"msctls_progress32",WS_BORDER,507,20,14,27 - CONTROL "",IDC_PB_FUNC2,"msctls_progress32",WS_BORDER,507,64,14,27 - CONTROL "",IDC_PB_FUNC3,"msctls_progress32",WS_BORDER,507,106,14,27 - CONTROL "",IDC_PB_TANK_DETACH,"msctls_progress32",WS_BORDER,583,20,14,27 - CONTROL "",IDC_PB_OVERRIDE,"msctls_progress32",WS_BORDER,583,64,14,27 - CONTROL "",IDC_PB_NIGHT_SCOPE,"msctls_progress32",WS_BORDER,583,106,14,27 - CONTROL "",IDC_PB_FSS,"msctls_progress32",WS_BORDER,658,20,14,27 - CONTROL "",IDC_PB_MANIPULATOR,"msctls_progress32",WS_BORDER,658,64,14,27 - CONTROL "",IDC_PB_LINE_COLOR_CHANGE,"msctls_progress32",WS_BORDER,658,106,14,27 - CONTROL "",IDC_PB_OPEN_CLOSE,"msctls_progress32",WS_BORDER,788,98,56,13 - CONTROL "",IDC_PB_MAP_ZOOM_IN_OUT,"msctls_progress32",WS_BORDER,861,98,56,13 - CONTROL "",IDC_PB_ZOOM_IN,"msctls_progress32",WS_BORDER,788,154,56,13 - CONTROL "",IDC_PB_ZOOM_OUT,"msctls_progress32",WS_BORDER,861,154,56,13 - CONTROL "",IDC_PB_MODE_SELECT,"msctls_progress32",WS_BORDER,788,135,56,13 - CONTROL "",IDC_PB_SUB_MONITOR_MODE_SELECT,"msctls_progress32",WS_BORDER,861,135,56,13 - PUSHBUTTON "ZOOM IN",IDC_BTN_ZOOM_IN,788,167,56,21 - PUSHBUTTON "ZOOM OUT",IDC_BTN_ZOOM_OUT,861,167,56,21 - PUSHBUTTON "MODE SELECT",IDC_BTN_MODE_SELECT,788,114,56,21 - PUSHBUTTON "SUB MONITOR MODE SELECT",IDC_BTN_SUB_MONITOR_MODE_SELECT,861,113,56,21,BS_MULTILINE - PUSHBUTTON "OPEN/CLOSE",IDC_BTN_OPEN_CLOSE,788,77,56,21 - PUSHBUTTON "MAP ZOOM IN/OUT",IDC_BTN_MAP_ZOON_IN_OUT,860,77,56,21,BS_MULTILINE - LTEXT "MULTI MONITOR",IDC_STATIC,716,99,55,8 - LTEXT "MAIN MONITOR ZOOM",IDC_STATIC,718,163,61,8 - GROUPBOX "",IDC_STATIC,949,59,83,148,0,WS_EX_CLIENTEDGE - PUSHBUTTON "MAIN WAPON CONTROL",IDC_BTN_MAIN_WEAPON_CONTROL,359,193,86,13 - PUSHBUTTON "SUB WAPON CTONTROL",IDC_BTN_SUB_WEAPON_CONTROL,481,192,86,13 - PUSHBUTTON "EXTINGUISHER",IDC_BTN_EXTINGUISHER,481,162,86,13 - PUSHBUTTON "MAGAZINE CHANGE",IDC_BTN_MAGAZINE_CHANGE,603,192,86,13 - PUSHBUTTON "CHAFF",IDC_BTN_CHAFF,603,162,86,13 - PUSHBUTTON "WASHING",IDC_BTN_WASHING,359,163,86,13 - PUSHBUTTON "COM1",IDC_BTN_COM1,338,42,24,13 - PUSHBUTTON "COM2",IDC_BTN_COM2,366,42,24,13 - PUSHBUTTON "COM3",IDC_BTN_COM3,394,42,24,13 - PUSHBUTTON "COM4",IDC_BTN_COM4,422,42,24,13 - PUSHBUTTON "COM5",IDC_BTN_COM5,450,42,24,13 - PUSHBUTTON "FUNC1",IDC_BTN_FUNC1,476,7,76,13 - PUSHBUTTON "FUNC2",IDC_BTN_FUNC2,476,51,76,13 - PUSHBUTTON "FUNC2",IDC_BTN_FUNC3,476,93,76,13 - PUSHBUTTON "TANK DETACH",IDC_BTN_TANK_DETACH,552,7,76,13 - PUSHBUTTON "F.S.S.",IDC_BTN_FSS,627,7,76,13 - PUSHBUTTON "OVERRIDE",IDC_BTN_OVERRIDE,552,51,76,13 - PUSHBUTTON "NIGHT SCOPE",IDC_BTN_NIGHT_SCOPE,552,93,76,13 - PUSHBUTTON "MANIPULATOR",IDC_BTN_MANIPULATOR,627,51,76,13 - PUSHBUTTON "LINE COLOR CHANGE",IDC_BTN_LINE_COLOR_CHANGE,627,93,76,13 - LTEXT "TUNER DIAL",IDC_STATIC,389,105,40,8 - GROUPBOX "Tuner Dial",IDC_STATIC,344,58,129,90,WS_GROUP,WS_EX_CLIENTEDGE - CONTROL "0",IDC_RADIO_TD0,"Button",BS_AUTORADIOBUTTON,360,100,20,10 - CONTROL "1",IDC_RADIO_TD1,"Button",BS_AUTORADIOBUTTON,369,91,20,10 - CONTROL "2",IDC_RADIO_TD2,"Button",BS_AUTORADIOBUTTON,380,82,20,10 - CONTROL "3",IDC_RADIO_TD3,"Button",BS_AUTORADIOBUTTON,390,73,20,10 - CONTROL "4",IDC_RADIO_TD4,"Button",BS_AUTORADIOBUTTON,403,64,20,10 - CONTROL "5",IDC_RADIO_TD5,"Button",BS_AUTORADIOBUTTON,419,73,20,10 - CONTROL "6",IDC_RADIO_TD6,"Button",BS_AUTORADIOBUTTON,430,82,20,10 - CONTROL "7",IDC_RADIO_TD7,"Button",BS_AUTORADIOBUTTON,439,91,20,10 - CONTROL "8",IDC_RADIO_TD8,"Button",BS_AUTORADIOBUTTON,447,100,20,10 - CONTROL "9",IDC_RADIO_TD9,"Button",BS_AUTORADIOBUTTON,439,109,20,10 - CONTROL "10",IDC_RADIO_TD10,"Button",BS_AUTORADIOBUTTON,431,118,24,10 - CONTROL "11",IDC_RADIO_TD11,"Button",BS_AUTORADIOBUTTON,417,127,24,10 - CONTROL "12",IDC_RADIO_TD12,"Button",BS_AUTORADIOBUTTON,405,136,24,10 - CONTROL "",IDC_PB_VT_LOCATION_MEASUREMENT,"msctls_progress32",WS_BORDER,188,120,14,10 - CONTROL "",IDC_PB_OXYGEN_SUPPLY_SYSTEM,"msctls_progress32",WS_BORDER,169,135,14,10 - CONTROL "",IDC_PB_BUFFER_MATERIAL,"msctls_progress32",WS_BORDER,188,150,14,10 - CONTROL "",IDC_PB_VT_LOCATION_MEASUREMENT4,"msctls_progress32",WS_BORDER,188,180,14,10 - CONTROL "",IDC_PB_FILT_CONTROL_SYSTEM,"msctls_progress32",WS_BORDER,169,165,14,10 -END - -IDD_VIDEO_CFG DIALOGEX 0, 0, 259, 150 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cxbx-Reloaded : Video Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - COMBOBOX IDC_VC_DISPLAY_ADAPTER,76,12,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_VC_D3D_DEVICE,76,31,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_VC_VIDEO_RESOLUTION,76,49,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Use Exclusive Fullscreen Mode",IDC_CV_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,76,89,112,10 - CONTROL "Force VSync",IDC_CV_VSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,193,89,58,10 - PUSHBUTTON "Cancel",IDC_VC_CANCEL,146,133,50,14,BS_FLAT - PUSHBUTTON "Accept",IDC_VC_ACCEPT,204,133,50,14,BS_FLAT - GROUPBOX "Direct3D Configuration",IDC_STATIC,4,1,250,130,BS_CENTER - LTEXT "Display Adapter:",IDC_STATIC,13,14,57,8,0,WS_EX_RIGHT - LTEXT "Direct3D Device:",IDC_STATIC,13,33,57,8,0,WS_EX_RIGHT - LTEXT "Display Resolution:",IDC_STATIC,6,52,64,8,0,WS_EX_RIGHT - LTEXT "Other Options:",IDC_STATIC,21,89,49,8,0,WS_EX_RIGHT - CONTROL "Enable Hardware YUV Overlays",IDC_CV_HARDWAREYUV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,76,102,123,11 - COMBOBOX IDC_VC_RENDER_RESOLUTION,76,68,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Render Resolution:",IDC_STATIC,7,70,63,8,0,WS_EX_RIGHT -END - -IDD_AUDIO_CFG DIALOGEX 0, 0, 259, 121 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cxbx-Reloaded : Audio Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - COMBOBOX IDC_AC_AUDIO_ADAPTER,76,12,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Enable PCM",IDC_AC_PCM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,78,34,54,10 - CONTROL "Enable XADPCM",IDC_AC_XADPCM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,133,34,69,10 - CONTROL "Enable Unknown Codec",IDC_AC_UNKNOWN_CODEC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,78,45,93,10 - PUSHBUTTON "Cancel",IDC_AC_CANCEL,146,102,50,14,BS_FLAT - PUSHBUTTON "Accept",IDC_AC_ACCEPT,206,102,50,14,BS_FLAT - GROUPBOX "DirectSound Configuration",IDC_STATIC,4,1,250,98,BS_CENTER - LTEXT "Audio Adapter:",IDC_STATIC,13,14,57,8,0,WS_EX_RIGHT - LTEXT "Codec Options:",IDC_STATIC,13,34,57,8,0,WS_EX_RIGHT - LTEXT "Other Options:",IDC_STATIC,13,60,57,8,0,WS_EX_RIGHT - CONTROL "Mute when unfocus",IDC_AC_MUTE_WHEN_UNFOCUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,78,60,78,10 -END - -IDD_NETWORK_CFG DIALOGEX 0, 0, 404, 76 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cxbx : Network Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - PUSHBUTTON "Cancel",IDC_AC_CANCEL,285,51,50,14,BS_FLAT - PUSHBUTTON "Accept",IDC_AC_ACCEPT,339,51,50,14,BS_FLAT - GROUPBOX "Network Configuration",-1,4,1,396,69,BS_CENTER - LTEXT "Network Adapter",-1,13,14,57,8,0,WS_EX_RIGHT - COMBOBOX IDC_NETWORK_ADAPTER,14,28,374,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP -END - -IDD_EEPROM_CFG DIALOGEX 0, 0, 259, 279 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cxbx-Reloaded : Eeprom Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - EDITTEXT IDC_EE_CONFOUNDER,100,10,140,12 - EDITTEXT IDC_EE_HDDKEY,100,25,140,12 - COMBOBOX IDC_EE_XBOX_REGION,100,40,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - EDITTEXT IDC_EE_SERIAL_NUMBER,100,56,140,12 - EDITTEXT IDC_EE_MAC_ADDRESS,100,72,140,12 - EDITTEXT IDC_EE_ONLINE_KEY,100,88,140,12 - COMBOBOX IDC_EE_AVREGION,100,103,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_EE_LANGUAGE,100,119,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_EE_AVSETTINGS,100,135,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_EE_AUDIOSETTINGS,100,150,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_EE_GAME_PRTL_CRTL,100,166,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - EDITTEXT IDC_EE_PRTL_PASS,100,183,140,12 - COMBOBOX IDC_EE_MOVIE_PRTL_CRTL,100,198,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_EE_DVDREGION,100,214,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "PAL 60Hz",IDC_EE_PAL60HZ,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_DISABLED | WS_TABSTOP,13,235,50,10 - CONTROL "NTSC 480p",IDC_EE_480P,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,73,235,50,10 - CONTROL "NTSC 720p",IDC_EE_720P,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,133,235,50,10 - CONTROL "NTSC 1080i",IDC_EE_1080I,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,193,235,50,10 - LTEXT "Confounder",IDC_STATIC,13,11,80,10,0,WS_EX_RIGHT - LTEXT "HDD Key",IDC_STATIC,13,26,80,10,0,WS_EX_RIGHT - LTEXT "Xbox Region",IDC_STATIC,13,42,80,10,0,WS_EX_RIGHT - LTEXT "Serial Number",IDC_STATIC,13,58,80,10,0,WS_EX_RIGHT - LTEXT "MAC Address",IDC_STATIC,13,74,80,10,0,WS_EX_RIGHT - LTEXT "Online Key",IDC_STATIC,13,89,80,10,0,WS_EX_RIGHT - LTEXT "AV Region",IDC_STATIC,13,105,80,10,0,WS_EX_RIGHT - LTEXT "Language",IDC_STATIC,13,121,80,10,0,WS_EX_RIGHT - LTEXT "Video Settings",IDC_STATIC,13,137,80,10,0,WS_EX_RIGHT - LTEXT "Audio Settings",IDC_STATIC,13,152,80,10,0,WS_EX_RIGHT - LTEXT "Game Parental Control",IDC_STATIC,13,168,80,10,0,WS_EX_RIGHT - LTEXT "Parental Password",IDC_STATIC,13,184,80,10,0,WS_EX_RIGHT - LTEXT "Movie Parental Control",IDC_STATIC,13,200,80,10,0,WS_EX_RIGHT - LTEXT "DVD Region",IDC_STATIC,13,216,80,10,0,WS_EX_RIGHT - PUSHBUTTON "Cancel",IDC_EE_CANCEL,146,251,40,14,BS_FLAT - PUSHBUTTON "Accept",IDC_EE_ACCEPT,206,251,40,14,BS_FLAT - PUSHBUTTON "Reset",IDC_EE_RESET,13,251,40,14,BS_FLAT -END - -IDD_LOGGING_CFG DIALOGEX 0, 0, 258, 355 -STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cxbx-Reloaded : Logging Configuration" -FONT 8, "Verdana", 0, 0, 0x1 -BEGIN - COMBOBOX IDC_EVENT_LV,57,9,50,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CTEXT "Event Level",IDC_STATIC,10,11,40,10,0,WS_EX_RIGHT - CONTROL "Enable Test Case Popup",IDC_LOG_POPUP_TESTCASE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,121,12,113,10 - GROUPBOX "Emulator Event",IDC_CXBXR_EVENTS,12,26,234,186,WS_GROUP,WS_EX_CLIENTEDGE - CONTROL "Enable all",IDC_LOG_ENABLE_GENERAL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,19,39,47,10 - CONTROL "Disable all",IDC_LOG_DISABLE_GENERAL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,71,39,50,10 - CONTROL "Custom",IDC_LOG_CUSTOM_GENERAL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,134,39,41,10 - CONTROL "CXBXR",IDC_LOG_CXBXR,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,27,54,39,10 - CONTROL "XBE",IDC_LOG_XBE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,92,54,29,10 - CONTROL "INIT",IDC_LOG_INIT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,148,54,30,10 - CONTROL "VMEM",IDC_LOG_VMEM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,199,54,35,10 - CONTROL "PMEM",IDC_LOG_PMEM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,32,69,34,10 - CONTROL "GUI",IDC_LOG_GUI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,92,69,29,10 - CONTROL "EEPR",IDC_LOG_EEPR,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,145,69,33,10 - CONTROL "RSA",IDC_LOG_RSA,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,204,69,30,10 - CONTROL "POOLMEM",IDC_LOG_POOLMEM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,18,84,48,10 - CONTROL "D3D8",IDC_LOG_D3D8,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,87,84,34,10 - CONTROL "D3DST",IDC_LOG_D3DST,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,139,84,39,10 - CONTROL "D3DCVT",IDC_LOG_D3DCVT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,190,84,44,10 - CONTROL "DSOUND",IDC_LOG_DSOUND,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,21,99,45,10 - CONTROL "DSBUFFER",IDC_LOG_DSBUFFER,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,71,99,50,10 - CONTROL "DSSTREAM",IDC_LOG_DSSTREAM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,125,99,53,10 - CONTROL "DS3DCALC",IDC_LOG_DS3DCALC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,181,99,53,10 - CONTROL "XMO",IDC_LOG_XMO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,35,114,31,10 - CONTROL "XAPI",IDC_LOG_XAPI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,89,114,32,10 - CONTROL "XACT",IDC_LOG_XACT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,144,114,34,10 - CONTROL "XGRP",IDC_LOG_XGRP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,200,114,34,10 - CONTROL "XONLINE",IDC_LOG_XONLINE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,21,126,45,10 - CONTROL "FS",IDC_LOG_FS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,97,126,24,10 - CONTROL "PSHB",IDC_LOG_PSHB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,144,126,34,10 - CONTROL "PXSH",IDC_LOG_PXSH,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,200,126,34,10 - CONTROL "VTXSH",IDC_LOG_VTXSH,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,28,140,38,10 - CONTROL "VTXB",IDC_LOG_VTXB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,144,140,34,10 - CONTROL "DINP",IDC_LOG_DINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,201,140,33,10 - CONTROL "XINP",IDC_LOG_XINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,34,155,32,10 - CONTROL "SDL",IDC_LOG_SDL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,88,155,33,10 - CONTROL "FILE",IDC_LOG_FILE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,148,155,30,10 - CONTROL "X86",IDC_LOG_X86,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,205,155,29,10 - CONTROL "HLE",IDC_LOG_HLE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,38,168,28,10 - CONTROL "NET",IDC_LOG_NET,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,92,168,29,10 - CONTROL "MCPX",IDC_LOG_MCPX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,143,168,35,10 - CONTROL "NV2A",IDC_LOG_NV2A,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,200,168,34,10 - CONTROL "SMC",IDC_LOG_SMC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,35,182,31,10 - CONTROL "OHCI",IDC_LOG_OHCI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,87,182,34,10 - CONTROL "USB",IDC_LOG_USB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,148,182,30,10 - CONTROL "HUB",IDC_LOG_HUB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,204,182,30,10 - CONTROL "XIDCTRL",IDC_LOG_XIDCTRL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,20,195,46,10 - CONTROL "ADM",IDC_LOG_ADM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,90,195,31,10 - CONTROL "INPSYS",IDC_LOG_INPSYS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,137,195,41,10 - GROUPBOX "Kernel Event",IDC_KERNEL_EVENTS,12,218,234,110,WS_GROUP,WS_EX_CLIENTEDGE - CONTROL "Enable all",IDC_LOG_ENABLE_KERNEL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,19,232,47,10 - CONTROL "Disable all",IDC_LOG_DISABLE_KERNEL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,71,232,50,10 - CONTROL "Custom",IDC_LOG_CUSTOM_KERNEL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,134,232,41,10 - CONTROL "KRNL",IDC_LOG_KRNL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,33,247,33,10 - CONTROL "LOG",IDC_LOG_LOG,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,91,247,30,10 - CONTROL "XBOX",IDC_LOG_XBOX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,141,247,35,10 - CONTROL "XBDM",IDC_LOG_XBDM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,199,247,35,10 - CONTROL "AV",IDC_LOG_AV,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,41,262,25,10 - CONTROL "DBG",IDC_LOG_DBG,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,90,262,31,10 - CONTROL "EX",IDC_LOG_EX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,150,262,25,10 - CONTROL "FSC",IDC_LOG_FSC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,205,262,29,10 - CONTROL "HAL",IDC_LOG_HAL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,37,277,29,10 - CONTROL "IO",IDC_LOG_IO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,97,277,24,10 - CONTROL "KD",IDC_LOG_KD,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,149,277,26,10 - CONTROL "KE",IDC_LOG_KE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,209,277,25,10 - CONTROL "KI",IDC_LOG_KI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,43,292,23,10 - CONTROL "MM",IDC_LOG_MM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,95,292,26,10 - CONTROL "NT",IDC_LOG_NT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,150,292,25,10 - CONTROL "OB",IDC_LOG_OB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,208,292,26,10 - CONTROL "PS",IDC_LOG_PS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,41,308,25,10 - CONTROL "RTL",IDC_LOG_RTL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,93,308,28,10 - CONTROL "XC",IDC_LOG_XC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,149,308,26,10 - CONTROL "XE",IDC_LOG_XE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,209,308,25,10 - PUSHBUTTON "Cancel",IDC_LOG_CANCEL,161,333,40,14,BS_FLAT - PUSHBUTTON "Accept",IDC_LOG_ACCEPT,206,333,40,14,BS_FLAT - CONTROL "VSHCACHE",IDC_LOG_VSHCACHE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,68,140,53,10 -END - -IDD_ABOUT DIALOGEX 0, 0, 310, 177 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "About Cxbx-Reloaded" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_TAB1,"SysTabControl32",0x0,7,7,296,163 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_NETWORK_CFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_CONTROLLER_HOST_MAPPING AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_VIRTUAL_SBC_FEEDBACK AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_AUDIO_CFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_EEPROM_CFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_LOGGING_CFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_VIDEO_CFG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_ABOUT AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_CXBX ICON "Cxbx-R.ico" - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "ResCxbx.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""WinResrc.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Bitmap -// - -IDB_LOGO BITMAP "Logo.bmp" - -IDR_JPEG_SPLASH BITMAP "Logo-License-CC4.bmp" - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_MAINMENU MENUEX -BEGIN - POPUP "&File", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Open Xbe...", ID_FILE_OPEN_XBE,MFT_STRING,MFS_ENABLED - MENUITEM "Open D&ashboard...\tF7", ID_FILE_OPEN_DASHBOARD,MFT_STRING,MFS_ENABLED - MENUITEM "&Close Xbe", ID_FILE_CLOSE_XBE,MFT_STRING,MFS_ENABLED - MENUITEM "", -1, MFT_SEPARATOR - MENUITEM "&Save Xbe", ID_FILE_SAVEXBEFILE,MFT_STRING,MFS_ENABLED - MENUITEM "Save Xbe &As...", ID_FILE_SAVEXBEFILEAS,MFT_STRING,MFS_ENABLED - MENUITEM "", -1, MFT_SEPARATOR - POPUP "&Recent Xbe Files", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&0 : Recent Placeholder", ID_FILE_RXBE_0,MFT_STRING,MFS_ENABLED - MENUITEM "&1 : Recent Placeholder", ID_FILE_RXBE_1,MFT_STRING,MFS_ENABLED - MENUITEM "&2 : Recent Placeholder", ID_FILE_RXBE_2,MFT_STRING,MFS_ENABLED - MENUITEM "&3 : Recent Placeholder", ID_FILE_RXBE_3,MFT_STRING,MFS_ENABLED - MENUITEM "&4 : Recent Placeholder", ID_FILE_RXBE_4,MFT_STRING,MFS_ENABLED - MENUITEM "&5 : Recent Placeholder", ID_FILE_RXBE_5,MFT_STRING,MFS_ENABLED - MENUITEM "&6 : Recent Placeholder", ID_FILE_RXBE_6,MFT_STRING,MFS_ENABLED - MENUITEM "&7 : Recent Placeholder", ID_FILE_RXBE_7,MFT_STRING,MFS_ENABLED - MENUITEM "&8 : Recent Placeholder", ID_FILE_RXBE_8,MFT_STRING,MFS_ENABLED - MENUITEM "&9 : Recent Placeholder", ID_FILE_RXBE_9,MFT_STRING,MFS_ENABLED - END - MENUITEM "", -1, MFT_SEPARATOR - MENUITEM "E&xit", ID_FILE_EXIT,MFT_STRING,MFS_ENABLED - END - POPUP "&Edit", 65535,MFT_STRING,MFS_ENABLED - BEGIN - POPUP "Logo &Bitmap", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Import...", ID_EDIT_LOGOBITMAP_IMPORT,MFT_STRING,MFS_ENABLED - MENUITEM "&Export...", ID_EDIT_LOGOBITMAP_EXPORT,MFT_STRING,MFS_ENABLED - END - POPUP "&Patch", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Allow >64 MB", ID_EDIT_PATCH_ALLOW64MB,MFT_STRING,MFS_ENABLED - MENUITEM "&Debug Mode", ID_EDIT_PATCH_DEBUGMODE,MFT_STRING,MFS_ENABLED - END - MENUITEM "", -1, MFT_SEPARATOR - POPUP "Dump &Xbe Info To...", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&File...", ID_EDIT_DUMPXBEINFOTO_FILE,MFT_STRING,MFS_ENABLED - MENUITEM "&Debug Console", ID_EDIT_DUMPXBEINFOTO_DEBUGCONSOLE,MFT_STRING,MFS_ENABLED - END - END - POPUP "&View", 65535,MFT_STRING,MFS_ENABLED - BEGIN - POPUP "&Debug Output (GUI)", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Console", ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE,MFT_STRING,MFS_ENABLED - MENUITEM "&File...", ID_EMULATION_DEBUGOUTPUTGUI_FILE,MFT_STRING,MFS_ENABLED - END - POPUP "Debug Output (&Kernel)", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Console", ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE,MFT_STRING,MFS_ENABLED - MENUITEM "&File...", ID_EMULATION_DEBUGOUTPUTKERNEL_FILE,MFT_STRING,MFS_ENABLED - END - END - POPUP "&Settings", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "Config I&nput...", ID_SETTINGS_CONFIG_INPUT,MFT_STRING,MFS_ENABLED - MENUITEM "Config &Video...", ID_SETTINGS_CONFIG_VIDEO,MFT_STRING,MFS_ENABLED - MENUITEM "Config &Audio...", ID_SETTINGS_CONFIG_AUDIO,MFT_STRING,MFS_ENABLED - MENUITEM "Config &Network...", ID_SETTINGS_CONFIG_NETWORK,MFT_STRING,MFS_ENABLED - MENUITEM "Config &Eeprom...", ID_SETTINGS_CONFIG_EEPROM,MFT_STRING,MFS_ENABLED - MENUITEM "Config &Logging...", ID_SETTINGS_CONFIG_LOGGING,MFT_STRING,MFS_ENABLED - POPUP "Config &Data Location...", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "Store in AppData", ID_SETTINGS_CONFIG_DLOCAPPDATA,MFT_STRING,MFS_ENABLED - MENUITEM "Store with Executable", ID_SETTINGS_CONFIG_DLOCEXECDIR,MFT_STRING,MFS_ENABLED - MENUITEM "Custom", ID_SETTINGS_CONFIG_DLOCCUSTOM,MFT_STRING,MFS_ENABLED - END - POPUP "&Symbol Cache", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Clear entire Symbol Cache", ID_CACHE_CLEARHLECACHE_ALL,MFT_STRING,MFS_ENABLED - MENUITEM "&Rescan title Symbol Cache", ID_CACHE_CLEARHLECACHE_CURRENT,MFT_STRING,MFS_ENABLED - END - MENUITEM "", -1, MFT_SEPARATOR - POPUP "Experimental", 65535,MFT_STRING,MFS_ENABLED - BEGIN - POPUP "&LLE", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "LLE &GPU", ID_EMULATION_LLE_GPU,MFT_STRING,MFS_GRAYED - END - END - POPUP "Hacks", 65535,MFT_STRING,MFS_ENABLED - BEGIN - POPUP "Speed Hacks", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "Run Xbox threads on all cores", ID_HACKS_RUNXBOXTHREADSONALLCORES,MFT_STRING,MFS_ENABLED - END - MENUITEM "Disable Pixel Shaders", ID_HACKS_DISABLEPIXELSHADERS,MFT_STRING,MFS_ENABLED - MENUITEM "Skip rdtsc patching", ID_HACKS_SKIPRDTSCPATCHING,MFT_STRING,MFS_ENABLED - END - MENUITEM "Use Loader Executable", ID_USELOADEREXEC,MFT_STRING,MFS_ENABLED - MENUITEM "Ignore Invalid Xbe Signature", ID_SETTINGS_IGNOREINVALIDXBESIG,MFT_STRING,MFS_ENABLED - MENUITEM "Ignore Invalid Xbe Sections", ID_SETTINGS_IGNOREINVALIDXBESEC, MFT_STRING, MFS_ENABLED - MENUITEM "Allow Admin Privilege", ID_SETTINGS_ALLOWADMINPRIVILEGE,MFT_STRING,MFS_ENABLED - MENUITEM "", -1, MFT_SEPARATOR - MENUITEM "Reset To Defaults", ID_SETTINGS_INITIALIZE,MFT_STRING,MFS_ENABLED - END - POPUP "E&mulation", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Start\tF5", ID_EMULATION_START,MFT_STRING,MFS_ENABLED - MENUITEM "Start &Debugger...\tF9", ID_EMULATION_STARTDEBUGGER,MFT_STRING,MFS_ENABLED - MENUITEM "", -1, MFT_SEPARATOR - MENUITEM "S&top\tF6", ID_EMULATION_STOP,MFT_STRING,MFS_ENABLED - END - POPUP "&Help", 65535,MFT_STRING,MFS_ENABLED - BEGIN - MENUITEM "&Go To The Official Cxbx Web Site...", ID_HELP_HOMEPAGE,MFT_STRING,MFS_ENABLED - MENUITEM "", -1, MFT_SEPARATOR - MENUITEM "&About", ID_HELP_ABOUT,MFT_STRING,MFS_ENABLED - END - MENUITEM " ", ID_FPS,MFT_STRING | MFT_RIGHTJUSTIFY,MFS_ENABLED - MENUITEM " ", ID_LED,MFT_STRING,MFS_ENABLED - MENUITEM " ", ID_LOG,MFT_STRING,MFS_ENABLED -END - - -///////////////////////////////////////////////////////////////////////////// -// -// TXT -// - -IDR_CONTRIBUTORS TXT "..\\CONTRIBUTORS" - -IDR_COPYING TXT "..\\COPYING" - -#endif // Neutral resources -///////////////////////////////////////////////////////////////////////////// - - +// Microsoft Visual C++ generated resource script. +// +#include "ResCxbx.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "WinResrc.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_VIRTUAL_SBC_FEEDBACK, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 1032 + TOPMARGIN, 7 + BOTTOMMARGIN, 207 + END + + IDD_VIDEO_CFG, DIALOG + BEGIN + BOTTOMMARGIN, 121 + END + + IDD_AUDIO_CFG, DIALOG + BEGIN + END + + IDD_NETWORK_CFG, DIALOG + BEGIN + BOTTOMMARGIN, 74 + END + + IDD_EEPROM_CFG, DIALOG + BEGIN + END + + IDD_LOGGING_CFG, DIALOG + BEGIN + VERTGUIDE, 12 + VERTGUIDE, 66 + VERTGUIDE, 121 + VERTGUIDE, 178 + VERTGUIDE, 234 + VERTGUIDE, 246 + BOTTOMMARGIN, 347 + HORZGUIDE, 54 + HORZGUIDE, 69 + HORZGUIDE, 84 + HORZGUIDE, 99 + HORZGUIDE, 114 + HORZGUIDE, 126 + HORZGUIDE, 140 + HORZGUIDE, 155 + HORZGUIDE, 168 + HORZGUIDE, 182 + HORZGUIDE, 195 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INPUT_CFG DIALOGEX 0, 0, 243, 124 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cxbx-Reloaded : Input Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + GROUPBOX "Xbox device configuration",IDC_XID_CONFIG,13,10,217,103,WS_GROUP,WS_EX_CLIENTEDGE + COMBOBOX IDC_DEVICE_PORT1,50,25,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DEVICE_PORT2,50,46,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DEVICE_PORT3,50,67,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DEVICE_PORT4,50,88,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure",IDC_CONFIGURE_PORT1,166,25,50,14,BS_FLAT + PUSHBUTTON "Configure",IDC_CONFIGURE_PORT2,166,46,50,14,BS_FLAT + PUSHBUTTON "Configure",IDC_CONFIGURE_PORT3,166,67,50,14,BS_FLAT + PUSHBUTTON "Configure",IDC_CONFIGURE_PORT4,166,88,50,14,BS_FLAT + LTEXT "Port 1",IDC_STATIC,23,27,20,10 + LTEXT "Port 2",IDC_STATIC,23,48,20,10 + LTEXT "Port 3",IDC_STATIC,23,69,20,10 + LTEXT "Port 4",IDC_STATIC,23,90,20,10 +END + +IDD_XID_DUKE_CFG DIALOGEX 0, 0, 528, 280 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + GROUPBOX "Device",IDC_XID_CONFIG,12,10,175,35,WS_GROUP + COMBOBOX IDC_DEVICE_LIST,21,23,101,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Refresh",IDC_REFRESH_DEVICES,128,23,50,14,BS_FLAT + GROUPBOX "Profile",IDC_XID_PROFILE,197,10,320,35,WS_GROUP + COMBOBOX IDC_XID_PROFILE_NAME,207,24,194,10,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Save",IDC_XID_PROFILE_SAVE,405,23,50,14,BS_FLAT + PUSHBUTTON "Delete",IDC_XID_PROFILE_DELETE,459,23,50,14,BS_FLAT + GROUPBOX "Buttons",IDC_XID_BUTTONS,12,65,121,181,WS_GROUP + PUSHBUTTON "",IDC_SET_A,59,75,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_B,59,93,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_X,59,111,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_Y,59,129,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_BLACK,59,147,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_WHITE,59,165,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_BACK,59,183,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_START,59,201,57,14,BS_FLAT + LTEXT "A",IDC_STATIC,28,75,20,14,SS_CENTERIMAGE + LTEXT "B",IDC_STATIC,28,93,20,14,SS_CENTERIMAGE + LTEXT "X",IDC_STATIC,28,111,20,14,SS_CENTERIMAGE + LTEXT "Y",IDC_STATIC,28,129,20,14,SS_CENTERIMAGE + LTEXT "Black",IDC_STATIC,28,147,20,14,SS_CENTERIMAGE + LTEXT "White",IDC_STATIC,28,165,20,14,SS_CENTERIMAGE + LTEXT "Back",IDC_STATIC,28,183,20,14,SS_CENTERIMAGE + LTEXT "Start",IDC_STATIC,28,201,20,14,SS_CENTERIMAGE + GROUPBOX "L Stick",IDC_XID_LSTICK,140,65,121,181,WS_GROUP + PUSHBUTTON "",IDC_SET_LEFT_POSY,187,75,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_LEFT_NEGY,187,93,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_LEFT_NEGX,187,111,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_LEFT_POSX,187,129,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_LTHUMB,187,147,57,14,BS_FLAT + LTEXT "Up",IDC_STATIC,156,75,20,14,SS_CENTERIMAGE + LTEXT "Down",IDC_STATIC,156,93,20,14,SS_CENTERIMAGE + LTEXT "Left",IDC_STATIC,156,111,20,14,SS_CENTERIMAGE + LTEXT "Right",IDC_STATIC,156,129,20,14,SS_CENTERIMAGE + LTEXT "L Click",IDC_STATIC,156,147,27,14,SS_CENTERIMAGE + GROUPBOX "R Stick",IDC_XID_RSTICK,268,65,121,181,WS_GROUP + PUSHBUTTON "",IDC_SET_RIGHT_POSY,315,75,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_RIGHT_NEGY,315,93,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_RIGHT_NEGX,315,111,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_RIGHT_POSX,315,129,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_RTHUMB,315,147,57,14,BS_FLAT + LTEXT "Up",IDC_STATIC,284,75,20,14,SS_CENTERIMAGE + LTEXT "Down",IDC_STATIC,284,93,20,14,SS_CENTERIMAGE + LTEXT "Left",IDC_STATIC,284,111,20,14,SS_CENTERIMAGE + LTEXT "Right",IDC_STATIC,284,129,20,14,SS_CENTERIMAGE + LTEXT "R Click",IDC_STATIC,284,147,27,14,SS_CENTERIMAGE + GROUPBOX "D Pad",IDC_XID_DPAD,396,65,121,88,WS_GROUP + PUSHBUTTON "",IDC_SET_DPAD_UP,443,75,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_DPAD_DOWN,443,93,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_DPAD_LEFT,443,111,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_DPAD_RIGHT,443,129,57,14,BS_FLAT + LTEXT "Up",IDC_STATIC,412,75,20,14,SS_CENTERIMAGE + LTEXT "Down",IDC_STATIC,412,93,20,14,SS_CENTERIMAGE + LTEXT "Left",IDC_STATIC,412,111,20,14,SS_CENTERIMAGE + LTEXT "Right",IDC_STATIC,412,129,20,14,SS_CENTERIMAGE + GROUPBOX "Triggers",IDC_XID_TRIGGERS,396,157,121,52,WS_GROUP + PUSHBUTTON "",IDC_SET_LTRIGGER,443,168,57,14,BS_FLAT + PUSHBUTTON "",IDC_SET_RTRIGGER,443,187,57,14,BS_FLAT + LTEXT "Left",IDC_STATIC,412,168,20,14,SS_CENTERIMAGE + LTEXT "Right",IDC_STATIC,412,187,20,14,SS_CENTERIMAGE + GROUPBOX "Rumble",IDC_XID_RUMBLE,396,212,121,34,WS_GROUP + PUSHBUTTON "",IDC_SET_MOTOR,443,224,57,14,BS_FLAT + LTEXT "Motor",IDC_STATIC,412,224,26,14,SS_CENTERIMAGE + PUSHBUTTON "Default Bindings",IDC_XID_DEFAULT,362,256,69,14,BS_FLAT + PUSHBUTTON "Clear",IDC_XID_CLEAR,443,256,50,14,BS_FLAT +END + +IDD_RUMBLE_CFG DIALOGEX 0, 0, 155, 35 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Rumble Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_RUMBLE_LIST,15,11,70,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Test",IDC_RUMBLE_TEST,95,11,45,14,BS_FLAT +END + +IDD_VIRTUAL_SBC_FEEDBACK DIALOGEX 0, 0, 1039, 214 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Virtual SteelBattalion Controller" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Left Block",IDC_LEFTBLOCK,7,7,329,200,0,WS_EX_CLIENTEDGE | WS_EX_RIGHT + GROUPBOX "Left Block",IDC_LEFTBLOCK2,7,7,329,200,0,WS_EX_RIGHT + GROUPBOX "Right Block",IDC_RIGHTBLOCK,703,7,329,200,0,WS_EX_CLIENTEDGE + GROUPBOX "Gear Lever",IDC_GEAR_LEVER,24,25,92,174,WS_GROUP,WS_EX_CLIENTEDGE + GROUPBOX "Toggle Switches",IDC_STATIC,131,102,191,95,0,WS_EX_CLIENTEDGE + CONTROL "N",IDC_GEAR_0,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,37,131,21,10 + CONTROL "1",IDC_GEAR_1,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,113,20,10 + CONTROL "2",IDC_GEAR_2,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,95,20,10 + CONTROL "3",IDC_GEAR_3,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,77,20,10 + CONTROL "4",IDC_GEAR_4,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,59,20,10 + CONTROL "5",IDC_GEAR_5,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,38,41,20,10 + CONTROL "R",IDC_GEAR_R,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,37,149,21,10 + CONTROL "",IDC_ROTATION_LEVER,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,129,38,188,15 + CTEXT "Rotation Lever",IDC_TXT_ROTATION_LEVER,199,27,48,8 + CONTROL "VT-LOCATION MEASUREMENT",IDC_VT_LOCATION_MEASUREMENT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,208,120,113,10 + CONTROL "BUFFER MATERIAL",IDC_BUFFER_MATERIAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,208,150,77,10 + CONTROL "FUEL FLOW RATE",IDC_FUEL_FLOW_RATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,208,180,73,10 + CONTROL "OXYGEN SUPPLY SYSTEM",IDC_OXYGEN_SUPPLY_SYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,135,96,10 + CONTROL "FILT CONTROL SYSTEM",IDC_FILT_CONTROL_SYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,188,165,91,10 + PUSHBUTTON "Eject",IDC_BTN_EJECT,958,42,60,11 + PUSHBUTTON "COCKPIT HATCH",IDC_BTN_COCKPIT_HATCH,958,93,60,12,BS_BOTTOM + PUSHBUTTON "IGNITION",IDC_BTN_IGNITION,958,144,60,11,BS_BOTTOM + PUSHBUTTON "START",IDC_BTN_START,958,192,60,10,BS_BOTTOM + CONTROL "",IDC_PB_EJECT,"msctls_progress32",WS_BORDER,958,15,60,26 + CONTROL "",IDC_PB_COCKPIT_HATCH,"msctls_progress32",WS_BORDER,958,67,60,26 + CONTROL "",IDC_PB_IGNITION,"msctls_progress32",WS_BORDER,958,117,60,26 + CONTROL "",IDC_PB_START,"msctls_progress32",WS_BORDER,958,165,60,26 + CONTROL "",IDC_PB_GEARLEVER_5,"msctls_progress32",WS_BORDER,62,41,29,10 + CONTROL "",IDC_PB_GEARLEVER_1,"msctls_progress32",WS_BORDER,62,113,29,10 + CONTROL "",IDC_PB_GEARLEVER_2,"msctls_progress32",WS_BORDER,62,95,29,10 + CONTROL "",IDC_PB_GEARLEVER_3,"msctls_progress32",WS_BORDER,62,77,29,10 + CONTROL "",IDC_PB_GEARLEVER_4,"msctls_progress32",WS_BORDER,62,59,29,10 + CONTROL "",IDC_PB_GEARLEVER_N,"msctls_progress32",WS_BORDER,62,131,29,10 + CONTROL "",IDC_PB_GEARLEVER_R,"msctls_progress32",WS_BORDER,62,149,29,10 + CONTROL "",IDC_PB_COM5,"msctls_progress32",WS_BORDER,455,12,14,27 + CONTROL "",IDC_PB_COM1,"msctls_progress32",WS_BORDER,343,12,14,27 + CONTROL "",IDC_PB_COM2,"msctls_progress32",WS_BORDER,371,12,14,27 + CONTROL "",IDC_PB_COM3,"msctls_progress32",WS_BORDER,399,12,14,27 + CONTROL "",IDC_PB_COM4,"msctls_progress32",WS_BORDER,427,12,14,27 + CONTROL "",IDC_PB_WASHING,"msctls_progress32",WS_BORDER,359,151,86,13 + CONTROL "",IDC_PB_MAIN_WEAPON_CONTROL,"msctls_progress32",WS_BORDER,359,179,86,13 + CONTROL "",IDC_PB_EXTINGUISHER,"msctls_progress32",WS_BORDER,481,151,86,13 + CONTROL "",IDC_PB_SUB_WEAPON_CONTROL,"msctls_progress32",WS_BORDER,481,179,86,13 + CONTROL "",IDC_PB_CHAFF,"msctls_progress32",WS_BORDER,603,151,86,13 + CONTROL "",IDC_PB_MAGAZINE_CHANGE,"msctls_progress32",WS_BORDER,603,179,86,13 + CONTROL "",IDC_PB_FUNC1,"msctls_progress32",WS_BORDER,507,20,14,27 + CONTROL "",IDC_PB_FUNC2,"msctls_progress32",WS_BORDER,507,64,14,27 + CONTROL "",IDC_PB_FUNC3,"msctls_progress32",WS_BORDER,507,106,14,27 + CONTROL "",IDC_PB_TANK_DETACH,"msctls_progress32",WS_BORDER,583,20,14,27 + CONTROL "",IDC_PB_OVERRIDE,"msctls_progress32",WS_BORDER,583,64,14,27 + CONTROL "",IDC_PB_NIGHT_SCOPE,"msctls_progress32",WS_BORDER,583,106,14,27 + CONTROL "",IDC_PB_FSS,"msctls_progress32",WS_BORDER,658,20,14,27 + CONTROL "",IDC_PB_MANIPULATOR,"msctls_progress32",WS_BORDER,658,64,14,27 + CONTROL "",IDC_PB_LINE_COLOR_CHANGE,"msctls_progress32",WS_BORDER,658,106,14,27 + CONTROL "",IDC_PB_OPEN_CLOSE,"msctls_progress32",WS_BORDER,788,98,56,13 + CONTROL "",IDC_PB_MAP_ZOOM_IN_OUT,"msctls_progress32",WS_BORDER,861,98,56,13 + CONTROL "",IDC_PB_ZOOM_IN,"msctls_progress32",WS_BORDER,788,154,56,13 + CONTROL "",IDC_PB_ZOOM_OUT,"msctls_progress32",WS_BORDER,861,154,56,13 + CONTROL "",IDC_PB_MODE_SELECT,"msctls_progress32",WS_BORDER,788,135,56,13 + CONTROL "",IDC_PB_SUB_MONITOR_MODE_SELECT,"msctls_progress32",WS_BORDER,861,135,56,13 + PUSHBUTTON "ZOOM IN",IDC_BTN_ZOOM_IN,788,167,56,21 + PUSHBUTTON "ZOOM OUT",IDC_BTN_ZOOM_OUT,861,167,56,21 + PUSHBUTTON "MODE SELECT",IDC_BTN_MODE_SELECT,788,114,56,21 + PUSHBUTTON "SUB MONITOR MODE SELECT",IDC_BTN_SUB_MONITOR_MODE_SELECT,861,113,56,21,BS_MULTILINE + PUSHBUTTON "OPEN/CLOSE",IDC_BTN_OPEN_CLOSE,788,77,56,21 + PUSHBUTTON "MAP ZOOM IN/OUT",IDC_BTN_MAP_ZOON_IN_OUT,860,77,56,21,BS_MULTILINE + LTEXT "MULTI MONITOR",IDC_STATIC,716,99,55,8 + LTEXT "MAIN MONITOR ZOOM",IDC_STATIC,718,163,61,8 + GROUPBOX "",IDC_STATIC,949,59,83,148,0,WS_EX_CLIENTEDGE + PUSHBUTTON "MAIN WAPON CONTROL",IDC_BTN_MAIN_WEAPON_CONTROL,359,193,86,13 + PUSHBUTTON "SUB WAPON CTONTROL",IDC_BTN_SUB_WEAPON_CONTROL,481,192,86,13 + PUSHBUTTON "EXTINGUISHER",IDC_BTN_EXTINGUISHER,481,162,86,13 + PUSHBUTTON "MAGAZINE CHANGE",IDC_BTN_MAGAZINE_CHANGE,603,192,86,13 + PUSHBUTTON "CHAFF",IDC_BTN_CHAFF,603,162,86,13 + PUSHBUTTON "WASHING",IDC_BTN_WASHING,359,163,86,13 + PUSHBUTTON "COM1",IDC_BTN_COM1,338,42,24,13 + PUSHBUTTON "COM2",IDC_BTN_COM2,366,42,24,13 + PUSHBUTTON "COM3",IDC_BTN_COM3,394,42,24,13 + PUSHBUTTON "COM4",IDC_BTN_COM4,422,42,24,13 + PUSHBUTTON "COM5",IDC_BTN_COM5,450,42,24,13 + PUSHBUTTON "FUNC1",IDC_BTN_FUNC1,476,7,76,13 + PUSHBUTTON "FUNC2",IDC_BTN_FUNC2,476,51,76,13 + PUSHBUTTON "FUNC2",IDC_BTN_FUNC3,476,93,76,13 + PUSHBUTTON "TANK DETACH",IDC_BTN_TANK_DETACH,552,7,76,13 + PUSHBUTTON "F.S.S.",IDC_BTN_FSS,627,7,76,13 + PUSHBUTTON "OVERRIDE",IDC_BTN_OVERRIDE,552,51,76,13 + PUSHBUTTON "NIGHT SCOPE",IDC_BTN_NIGHT_SCOPE,552,93,76,13 + PUSHBUTTON "MANIPULATOR",IDC_BTN_MANIPULATOR,627,51,76,13 + PUSHBUTTON "LINE COLOR CHANGE",IDC_BTN_LINE_COLOR_CHANGE,627,93,76,13 + LTEXT "TUNER DIAL",IDC_STATIC,389,105,40,8 + GROUPBOX "Tuner Dial",IDC_STATIC,344,58,129,90,WS_GROUP,WS_EX_CLIENTEDGE + CONTROL "0",IDC_RADIO_TD0,"Button",BS_AUTORADIOBUTTON,360,100,20,10 + CONTROL "1",IDC_RADIO_TD1,"Button",BS_AUTORADIOBUTTON,369,91,20,10 + CONTROL "2",IDC_RADIO_TD2,"Button",BS_AUTORADIOBUTTON,380,82,20,10 + CONTROL "3",IDC_RADIO_TD3,"Button",BS_AUTORADIOBUTTON,390,73,20,10 + CONTROL "4",IDC_RADIO_TD4,"Button",BS_AUTORADIOBUTTON,403,64,20,10 + CONTROL "5",IDC_RADIO_TD5,"Button",BS_AUTORADIOBUTTON,419,73,20,10 + CONTROL "6",IDC_RADIO_TD6,"Button",BS_AUTORADIOBUTTON,430,82,20,10 + CONTROL "7",IDC_RADIO_TD7,"Button",BS_AUTORADIOBUTTON,439,91,20,10 + CONTROL "8",IDC_RADIO_TD8,"Button",BS_AUTORADIOBUTTON,447,100,20,10 + CONTROL "9",IDC_RADIO_TD9,"Button",BS_AUTORADIOBUTTON,439,109,20,10 + CONTROL "10",IDC_RADIO_TD10,"Button",BS_AUTORADIOBUTTON,431,118,24,10 + CONTROL "11",IDC_RADIO_TD11,"Button",BS_AUTORADIOBUTTON,417,127,24,10 + CONTROL "12",IDC_RADIO_TD12,"Button",BS_AUTORADIOBUTTON,405,136,24,10 + CONTROL "",IDC_PB_VT_LOCATION_MEASUREMENT,"msctls_progress32",WS_BORDER,188,120,14,10 + CONTROL "",IDC_PB_OXYGEN_SUPPLY_SYSTEM,"msctls_progress32",WS_BORDER,169,135,14,10 + CONTROL "",IDC_PB_BUFFER_MATERIAL,"msctls_progress32",WS_BORDER,188,150,14,10 + CONTROL "",IDC_PB_VT_LOCATION_MEASUREMENT4,"msctls_progress32",WS_BORDER,188,180,14,10 + CONTROL "",IDC_PB_FILT_CONTROL_SYSTEM,"msctls_progress32",WS_BORDER,169,165,14,10 +END + +IDD_VIDEO_CFG DIALOGEX 0, 0, 259, 150 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cxbx-Reloaded : Video Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_VC_DISPLAY_ADAPTER,76,12,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_VC_D3D_DEVICE,76,31,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_VC_VIDEO_RESOLUTION,76,49,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Use Exclusive Fullscreen Mode",IDC_CV_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,76,89,112,10 + CONTROL "Force VSync",IDC_CV_VSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,193,89,58,10 + PUSHBUTTON "Cancel",IDC_VC_CANCEL,146,133,50,14,BS_FLAT + PUSHBUTTON "Accept",IDC_VC_ACCEPT,204,133,50,14,BS_FLAT + GROUPBOX "Direct3D Configuration",IDC_STATIC,4,1,250,130,BS_CENTER + LTEXT "Display Adapter:",IDC_STATIC,13,14,57,8,0,WS_EX_RIGHT + LTEXT "Direct3D Device:",IDC_STATIC,13,33,57,8,0,WS_EX_RIGHT + LTEXT "Display Resolution:",IDC_STATIC,6,52,64,8,0,WS_EX_RIGHT + LTEXT "Other Options:",IDC_STATIC,21,89,49,8,0,WS_EX_RIGHT + CONTROL "Enable Hardware YUV Overlays",IDC_CV_HARDWAREYUV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,76,102,123,11 + COMBOBOX IDC_VC_RENDER_RESOLUTION,76,68,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Render Resolution:",IDC_STATIC,7,70,63,8,0,WS_EX_RIGHT +END + +IDD_AUDIO_CFG DIALOGEX 0, 0, 259, 121 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cxbx-Reloaded : Audio Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_AC_AUDIO_ADAPTER,76,12,173,100,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Enable PCM",IDC_AC_PCM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,78,34,54,10 + CONTROL "Enable XADPCM",IDC_AC_XADPCM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,133,34,69,10 + CONTROL "Enable Unknown Codec",IDC_AC_UNKNOWN_CODEC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,78,45,93,10 + PUSHBUTTON "Cancel",IDC_AC_CANCEL,146,102,50,14,BS_FLAT + PUSHBUTTON "Accept",IDC_AC_ACCEPT,206,102,50,14,BS_FLAT + GROUPBOX "DirectSound Configuration",IDC_STATIC,4,1,250,98,BS_CENTER + LTEXT "Audio Adapter:",IDC_STATIC,13,14,57,8,0,WS_EX_RIGHT + LTEXT "Codec Options:",IDC_STATIC,13,34,57,8,0,WS_EX_RIGHT + LTEXT "Other Options:",IDC_STATIC,13,60,57,8,0,WS_EX_RIGHT + CONTROL "Mute when unfocus",IDC_AC_MUTE_WHEN_UNFOCUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,78,60,78,10 +END + +IDD_NETWORK_CFG DIALOGEX 0, 0, 404, 76 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cxbx : Network Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Cancel",IDC_AC_CANCEL,285,51,50,14,BS_FLAT + PUSHBUTTON "Accept",IDC_AC_ACCEPT,339,51,50,14,BS_FLAT + GROUPBOX "Network Configuration",-1,4,1,396,69,BS_CENTER + LTEXT "Network Adapter",-1,13,14,57,8,0,WS_EX_RIGHT + COMBOBOX IDC_NETWORK_ADAPTER,14,28,374,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP +END + +IDD_EEPROM_CFG DIALOGEX 0, 0, 259, 279 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cxbx-Reloaded : Eeprom Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_EE_CONFOUNDER,100,10,140,12 + EDITTEXT IDC_EE_HDDKEY,100,25,140,12 + COMBOBOX IDC_EE_XBOX_REGION,100,40,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_EE_SERIAL_NUMBER,100,56,140,12 + EDITTEXT IDC_EE_MAC_ADDRESS,100,72,140,12 + EDITTEXT IDC_EE_ONLINE_KEY,100,88,140,12 + COMBOBOX IDC_EE_AVREGION,100,103,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_EE_LANGUAGE,100,119,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_EE_AVSETTINGS,100,135,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_EE_AUDIOSETTINGS,100,150,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_EE_GAME_PRTL_CRTL,100,166,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_EE_PRTL_PASS,100,183,140,12 + COMBOBOX IDC_EE_MOVIE_PRTL_CRTL,100,198,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_EE_DVDREGION,100,214,140,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "PAL 60Hz",IDC_EE_PAL60HZ,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_DISABLED | WS_TABSTOP,13,235,50,10 + CONTROL "NTSC 480p",IDC_EE_480P,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,73,235,50,10 + CONTROL "NTSC 720p",IDC_EE_720P,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,133,235,50,10 + CONTROL "NTSC 1080i",IDC_EE_1080I,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,193,235,50,10 + LTEXT "Confounder",IDC_STATIC,13,11,80,10,0,WS_EX_RIGHT + LTEXT "HDD Key",IDC_STATIC,13,26,80,10,0,WS_EX_RIGHT + LTEXT "Xbox Region",IDC_STATIC,13,42,80,10,0,WS_EX_RIGHT + LTEXT "Serial Number",IDC_STATIC,13,58,80,10,0,WS_EX_RIGHT + LTEXT "MAC Address",IDC_STATIC,13,74,80,10,0,WS_EX_RIGHT + LTEXT "Online Key",IDC_STATIC,13,89,80,10,0,WS_EX_RIGHT + LTEXT "AV Region",IDC_STATIC,13,105,80,10,0,WS_EX_RIGHT + LTEXT "Language",IDC_STATIC,13,121,80,10,0,WS_EX_RIGHT + LTEXT "Video Settings",IDC_STATIC,13,137,80,10,0,WS_EX_RIGHT + LTEXT "Audio Settings",IDC_STATIC,13,152,80,10,0,WS_EX_RIGHT + LTEXT "Game Parental Control",IDC_STATIC,13,168,80,10,0,WS_EX_RIGHT + LTEXT "Parental Password",IDC_STATIC,13,184,80,10,0,WS_EX_RIGHT + LTEXT "Movie Parental Control",IDC_STATIC,13,200,80,10,0,WS_EX_RIGHT + LTEXT "DVD Region",IDC_STATIC,13,216,80,10,0,WS_EX_RIGHT + PUSHBUTTON "Cancel",IDC_EE_CANCEL,146,251,40,14,BS_FLAT + PUSHBUTTON "Accept",IDC_EE_ACCEPT,206,251,40,14,BS_FLAT + PUSHBUTTON "Reset",IDC_EE_RESET,13,251,40,14,BS_FLAT +END + +IDD_LOGGING_CFG DIALOGEX 0, 0, 258, 355 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Cxbx-Reloaded : Logging Configuration" +FONT 8, "Verdana", 0, 0, 0x1 +BEGIN + COMBOBOX IDC_EVENT_LV,57,9,50,10,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "Event Level",IDC_STATIC,10,11,40,10,0,WS_EX_RIGHT + CONTROL "Enable Test Case Popup",IDC_LOG_POPUP_TESTCASE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,121,12,113,10 + GROUPBOX "Emulator Event",IDC_CXBXR_EVENTS,12,26,234,186,WS_GROUP,WS_EX_CLIENTEDGE + CONTROL "Enable all",IDC_LOG_ENABLE_GENERAL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,19,39,47,10 + CONTROL "Disable all",IDC_LOG_DISABLE_GENERAL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,71,39,50,10 + CONTROL "Custom",IDC_LOG_CUSTOM_GENERAL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,134,39,41,10 + CONTROL "CXBXR",IDC_LOG_CXBXR,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,27,54,39,10 + CONTROL "XBE",IDC_LOG_XBE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,92,54,29,10 + CONTROL "INIT",IDC_LOG_INIT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,148,54,30,10 + CONTROL "VMEM",IDC_LOG_VMEM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,199,54,35,10 + CONTROL "PMEM",IDC_LOG_PMEM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,32,69,34,10 + CONTROL "GUI",IDC_LOG_GUI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,92,69,29,10 + CONTROL "EEPR",IDC_LOG_EEPR,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,145,69,33,10 + CONTROL "RSA",IDC_LOG_RSA,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,204,69,30,10 + CONTROL "POOLMEM",IDC_LOG_POOLMEM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,18,84,48,10 + CONTROL "D3D8",IDC_LOG_D3D8,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,87,84,34,10 + CONTROL "D3DST",IDC_LOG_D3DST,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,139,84,39,10 + CONTROL "D3DCVT",IDC_LOG_D3DCVT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,190,84,44,10 + CONTROL "DSOUND",IDC_LOG_DSOUND,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,21,99,45,10 + CONTROL "DSBUFFER",IDC_LOG_DSBUFFER,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,71,99,50,10 + CONTROL "DSSTREAM",IDC_LOG_DSSTREAM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,125,99,53,10 + CONTROL "DS3DCALC",IDC_LOG_DS3DCALC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,181,99,53,10 + CONTROL "XMO",IDC_LOG_XMO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,35,114,31,10 + CONTROL "XAPI",IDC_LOG_XAPI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,89,114,32,10 + CONTROL "XACT",IDC_LOG_XACT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,144,114,34,10 + CONTROL "XGRP",IDC_LOG_XGRP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,200,114,34,10 + CONTROL "XONLINE",IDC_LOG_XONLINE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,21,126,45,10 + CONTROL "FS",IDC_LOG_FS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,97,126,24,10 + CONTROL "PSHB",IDC_LOG_PSHB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,144,126,34,10 + CONTROL "PXSH",IDC_LOG_PXSH,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,200,126,34,10 + CONTROL "VTXSH",IDC_LOG_VTXSH,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,28,140,38,10 + CONTROL "VTXB",IDC_LOG_VTXB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,144,140,34,10 + CONTROL "DINP",IDC_LOG_DINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,201,140,33,10 + CONTROL "XINP",IDC_LOG_XINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,34,155,32,10 + CONTROL "SDL",IDC_LOG_SDL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,88,155,33,10 + CONTROL "FILE",IDC_LOG_FILE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,148,155,30,10 + CONTROL "X86",IDC_LOG_X86,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,205,155,29,10 + CONTROL "HLE",IDC_LOG_HLE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,38,168,28,10 + CONTROL "NET",IDC_LOG_NET,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,92,168,29,10 + CONTROL "MCPX",IDC_LOG_MCPX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,143,168,35,10 + CONTROL "NV2A",IDC_LOG_NV2A,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,200,168,34,10 + CONTROL "SMC",IDC_LOG_SMC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,35,182,31,10 + CONTROL "OHCI",IDC_LOG_OHCI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,87,182,34,10 + CONTROL "USB",IDC_LOG_USB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,148,182,30,10 + CONTROL "HUB",IDC_LOG_HUB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,204,182,30,10 + CONTROL "XIDCTRL",IDC_LOG_XIDCTRL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,20,195,46,10 + CONTROL "ADM",IDC_LOG_ADM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,90,195,31,10 + CONTROL "INPSYS",IDC_LOG_INPSYS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,137,195,41,10 + GROUPBOX "Kernel Event",IDC_KERNEL_EVENTS,12,218,234,110,WS_GROUP,WS_EX_CLIENTEDGE + CONTROL "Enable all",IDC_LOG_ENABLE_KERNEL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,19,232,47,10 + CONTROL "Disable all",IDC_LOG_DISABLE_KERNEL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,71,232,50,10 + CONTROL "Custom",IDC_LOG_CUSTOM_KERNEL,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,134,232,41,10 + CONTROL "KRNL",IDC_LOG_KRNL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,33,247,33,10 + CONTROL "LOG",IDC_LOG_LOG,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,91,247,30,10 + CONTROL "XBOX",IDC_LOG_XBOX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,141,247,35,10 + CONTROL "XBDM",IDC_LOG_XBDM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,199,247,35,10 + CONTROL "AV",IDC_LOG_AV,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,41,262,25,10 + CONTROL "DBG",IDC_LOG_DBG,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,90,262,31,10 + CONTROL "EX",IDC_LOG_EX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,150,262,25,10 + CONTROL "FSC",IDC_LOG_FSC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,205,262,29,10 + CONTROL "HAL",IDC_LOG_HAL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,37,277,29,10 + CONTROL "IO",IDC_LOG_IO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,97,277,24,10 + CONTROL "KD",IDC_LOG_KD,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,149,277,26,10 + CONTROL "KE",IDC_LOG_KE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,209,277,25,10 + CONTROL "KI",IDC_LOG_KI,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,43,292,23,10 + CONTROL "MM",IDC_LOG_MM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,95,292,26,10 + CONTROL "NT",IDC_LOG_NT,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,150,292,25,10 + CONTROL "OB",IDC_LOG_OB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,208,292,26,10 + CONTROL "PS",IDC_LOG_PS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,41,308,25,10 + CONTROL "RTL",IDC_LOG_RTL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,93,308,28,10 + CONTROL "XC",IDC_LOG_XC,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,149,308,26,10 + CONTROL "XE",IDC_LOG_XE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,209,308,25,10 + PUSHBUTTON "Cancel",IDC_LOG_CANCEL,161,333,40,14,BS_FLAT + PUSHBUTTON "Accept",IDC_LOG_ACCEPT,206,333,40,14,BS_FLAT + CONTROL "VSHCACHE",IDC_LOG_VSHCACHE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,68,140,53,10 +END + +IDD_ABOUT DIALOGEX 0, 0, 310, 177 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Cxbx-Reloaded" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_TAB1,"SysTabControl32",0x0,7,7,296,163 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_NETWORK_CFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_CONTROLLER_HOST_MAPPING AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_VIRTUAL_SBC_FEEDBACK AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_AUDIO_CFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_EEPROM_CFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_LOGGING_CFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_VIDEO_CFG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +IDD_ABOUT AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_CXBX ICON "Cxbx-R.ico" + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "ResCxbx.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""WinResrc.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_LOGO BITMAP "Logo.bmp" + +IDR_JPEG_SPLASH BITMAP "Logo-License-CC4.bmp" + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINMENU MENUEX +BEGIN + POPUP "&File", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Open Xbe...", ID_FILE_OPEN_XBE,MFT_STRING,MFS_ENABLED + MENUITEM "Open D&ashboard...\tF7", ID_FILE_OPEN_DASHBOARD,MFT_STRING,MFS_ENABLED + MENUITEM "&Close Xbe", ID_FILE_CLOSE_XBE,MFT_STRING,MFS_ENABLED + MENUITEM "", -1, MFT_SEPARATOR + MENUITEM "&Save Xbe", ID_FILE_SAVEXBEFILE,MFT_STRING,MFS_ENABLED + MENUITEM "Save Xbe &As...", ID_FILE_SAVEXBEFILEAS,MFT_STRING,MFS_ENABLED + MENUITEM "", -1, MFT_SEPARATOR + POPUP "&Recent Xbe Files", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&0 : Recent Placeholder", ID_FILE_RXBE_0,MFT_STRING,MFS_ENABLED + MENUITEM "&1 : Recent Placeholder", ID_FILE_RXBE_1,MFT_STRING,MFS_ENABLED + MENUITEM "&2 : Recent Placeholder", ID_FILE_RXBE_2,MFT_STRING,MFS_ENABLED + MENUITEM "&3 : Recent Placeholder", ID_FILE_RXBE_3,MFT_STRING,MFS_ENABLED + MENUITEM "&4 : Recent Placeholder", ID_FILE_RXBE_4,MFT_STRING,MFS_ENABLED + MENUITEM "&5 : Recent Placeholder", ID_FILE_RXBE_5,MFT_STRING,MFS_ENABLED + MENUITEM "&6 : Recent Placeholder", ID_FILE_RXBE_6,MFT_STRING,MFS_ENABLED + MENUITEM "&7 : Recent Placeholder", ID_FILE_RXBE_7,MFT_STRING,MFS_ENABLED + MENUITEM "&8 : Recent Placeholder", ID_FILE_RXBE_8,MFT_STRING,MFS_ENABLED + MENUITEM "&9 : Recent Placeholder", ID_FILE_RXBE_9,MFT_STRING,MFS_ENABLED + END + MENUITEM "", -1, MFT_SEPARATOR + MENUITEM "E&xit", ID_FILE_EXIT,MFT_STRING,MFS_ENABLED + END + POPUP "&Edit", 65535,MFT_STRING,MFS_ENABLED + BEGIN + POPUP "Logo &Bitmap", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Import...", ID_EDIT_LOGOBITMAP_IMPORT,MFT_STRING,MFS_ENABLED + MENUITEM "&Export...", ID_EDIT_LOGOBITMAP_EXPORT,MFT_STRING,MFS_ENABLED + END + POPUP "&Patch", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Allow >64 MB", ID_EDIT_PATCH_ALLOW64MB,MFT_STRING,MFS_ENABLED + MENUITEM "&Debug Mode", ID_EDIT_PATCH_DEBUGMODE,MFT_STRING,MFS_ENABLED + END + MENUITEM "", -1, MFT_SEPARATOR + POPUP "Dump &Xbe Info To...", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&File...", ID_EDIT_DUMPXBEINFOTO_FILE,MFT_STRING,MFS_ENABLED + MENUITEM "&Debug Console", ID_EDIT_DUMPXBEINFOTO_DEBUGCONSOLE,MFT_STRING,MFS_ENABLED + END + END + POPUP "&View", 65535,MFT_STRING,MFS_ENABLED + BEGIN + POPUP "&Debug Output (GUI)", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Console", ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE,MFT_STRING,MFS_ENABLED + MENUITEM "&File...", ID_EMULATION_DEBUGOUTPUTGUI_FILE,MFT_STRING,MFS_ENABLED + END + POPUP "Debug Output (&Kernel)", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Console", ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE,MFT_STRING,MFS_ENABLED + MENUITEM "&File...", ID_EMULATION_DEBUGOUTPUTKERNEL_FILE,MFT_STRING,MFS_ENABLED + END + END + POPUP "&Settings", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "Config I&nput...", ID_SETTINGS_CONFIG_INPUT,MFT_STRING,MFS_ENABLED + MENUITEM "Config &Video...", ID_SETTINGS_CONFIG_VIDEO,MFT_STRING,MFS_ENABLED + MENUITEM "Config &Audio...", ID_SETTINGS_CONFIG_AUDIO,MFT_STRING,MFS_ENABLED + MENUITEM "Config &Network...", ID_SETTINGS_CONFIG_NETWORK,MFT_STRING,MFS_ENABLED + MENUITEM "Config &Eeprom...", ID_SETTINGS_CONFIG_EEPROM,MFT_STRING,MFS_ENABLED + MENUITEM "Config &Logging...", ID_SETTINGS_CONFIG_LOGGING,MFT_STRING,MFS_ENABLED + POPUP "Config &Data Location...", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "Store in AppData", ID_SETTINGS_CONFIG_DLOCAPPDATA,MFT_STRING,MFS_ENABLED + MENUITEM "Store with Executable", ID_SETTINGS_CONFIG_DLOCEXECDIR,MFT_STRING,MFS_ENABLED + MENUITEM "Custom", ID_SETTINGS_CONFIG_DLOCCUSTOM,MFT_STRING,MFS_ENABLED + END + POPUP "&Symbol Cache", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Clear entire Symbol Cache", ID_CACHE_CLEARHLECACHE_ALL,MFT_STRING,MFS_ENABLED + MENUITEM "&Rescan title Symbol Cache", ID_CACHE_CLEARHLECACHE_CURRENT,MFT_STRING,MFS_ENABLED + END + MENUITEM "", -1, MFT_SEPARATOR + POPUP "Experimental", 65535,MFT_STRING,MFS_ENABLED + BEGIN + POPUP "&LLE", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "LLE &GPU", ID_EMULATION_LLE_GPU,MFT_STRING,MFS_GRAYED + END + END + POPUP "Hacks", 65535,MFT_STRING,MFS_ENABLED + BEGIN + POPUP "Speed Hacks", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "Run Xbox threads on all cores", ID_HACKS_RUNXBOXTHREADSONALLCORES,MFT_STRING,MFS_ENABLED + END + MENUITEM "Disable Pixel Shaders", ID_HACKS_DISABLEPIXELSHADERS,MFT_STRING,MFS_ENABLED + MENUITEM "Skip rdtsc patching", ID_HACKS_SKIPRDTSCPATCHING,MFT_STRING,MFS_ENABLED + END + MENUITEM "Use Loader Executable", ID_USELOADEREXEC,MFT_STRING,MFS_ENABLED + MENUITEM "Ignore Invalid Xbe Signature", ID_SETTINGS_IGNOREINVALIDXBESIG,MFT_STRING,MFS_ENABLED + MENUITEM "Ignore Invalid Xbe Sections", ID_SETTINGS_IGNOREINVALIDXBESEC, MFT_STRING, MFS_ENABLED + MENUITEM "Allow Admin Privilege", ID_SETTINGS_ALLOWADMINPRIVILEGE,MFT_STRING,MFS_ENABLED + MENUITEM "", -1, MFT_SEPARATOR + MENUITEM "Reset To Defaults", ID_SETTINGS_INITIALIZE,MFT_STRING,MFS_ENABLED + END + POPUP "E&mulation", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Start\tF5", ID_EMULATION_START,MFT_STRING,MFS_ENABLED + MENUITEM "Start &Debugger...\tF9", ID_EMULATION_STARTDEBUGGER,MFT_STRING,MFS_ENABLED + MENUITEM "", -1, MFT_SEPARATOR + MENUITEM "S&top\tF6", ID_EMULATION_STOP,MFT_STRING,MFS_ENABLED + END + POPUP "&Help", 65535,MFT_STRING,MFS_ENABLED + BEGIN + MENUITEM "&Go To The Official Cxbx Web Site...", ID_HELP_HOMEPAGE,MFT_STRING,MFS_ENABLED + MENUITEM "", -1, MFT_SEPARATOR + MENUITEM "&About", ID_HELP_ABOUT,MFT_STRING,MFS_ENABLED + END + MENUITEM " ", ID_FPS,MFT_STRING | MFT_RIGHTJUSTIFY,MFS_ENABLED + MENUITEM " ", ID_LED,MFT_STRING,MFS_ENABLED + MENUITEM " ", ID_LOG,MFT_STRING,MFS_ENABLED +END + + +///////////////////////////////////////////////////////////////////////////// +// +// TXT +// + +IDR_CONTRIBUTORS TXT "..\\CONTRIBUTORS" + +IDR_COPYING TXT "..\\COPYING" + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/gui/resource/ResCxbx.h b/src/gui/resource/ResCxbx.h index 5faad1741..3cca3c009 100644 --- a/src/gui/resource/ResCxbx.h +++ b/src/gui/resource/ResCxbx.h @@ -1,380 +1,380 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Cxbx.rc -// -#define IDI_CXBX 101 -#define IDB_SPLASH 102 -#define IDR_MAINMENU 103 -#define IDB_LOGO 106 -#define IDB_ABOUT 108 -#define IDD_INPUT_CFG 111 -#define IDD_LOGGING_CFG 112 -#define IDD_VIDEO_CFG 113 -#define IDD_AUDIO_CFG 114 -#define IDD_EEPROM_CFG 115 -#define IDR_JPEG_ABOUT 116 -#define IDR_JPEG_SPLASH 118 -#define IDD_ABOUT 119 -#define IDR_CONTRIBUTORS 121 -#define IDR_COPYING 122 -#define IDD_VIRTUAL_SBC_FEEDBACK 133 -#define IDD_XID_DUKE_CFG 134 -#define IDD_RUMBLE_CFG 135 -#define IDD_NETWORK_CFG 136 -#define IDC_LOG_CANCEL 892 -#define IDC_LOG_ACCEPT 893 -#define IDC_LOG_ENABLE_GENERAL 894 -#define IDC_LOG_DISABLE_GENERAL 895 -#define IDC_LOG_ENABLE_KERNEL 896 -#define IDC_LOG_DISABLE_KERNEL 897 -#define IDC_LOG_CUSTOM_GENERAL 898 -#define IDC_LOG_CUSTOM_KERNEL 899 -#define IDC_LOG_CXBXR 900 -#define IDC_LOG_XBE 901 -#define IDC_LOG_INIT 902 -#define IDC_LOG_VMEM 903 -#define IDC_LOG_PMEM 904 -#define IDC_LOG_GUI 905 -#define IDC_LOG_EEPR 906 -#define IDC_LOG_RSA 907 -#define IDC_LOG_POOLMEM 908 -#define IDC_LOG_D3D8 909 -#define IDC_LOG_D3DST 910 -#define IDC_LOG_D3DCVT 911 -#define IDC_LOG_DSOUND 912 -#define IDC_LOG_XAPI 913 -#define IDC_LOG_XACT 914 -#define IDC_LOG_XGRP 915 -#define IDC_LOG_XONLINE 916 -#define IDC_LOG_FS 917 -#define IDC_LOG_PSHB 918 -#define IDC_LOG_PXSH 919 -#define IDC_LOG_VTXSH 920 -#define IDC_LOG_VTXB 921 -#define IDC_LOG_DINP 922 -#define IDC_LOG_XINP 923 -#define IDC_LOG_SDL 924 -#define IDC_LOG_FILE 925 -#define IDC_LOG_X86 926 -#define IDC_LOG_HLE 927 -#define IDC_LOG_NET 928 -#define IDC_LOG_MCPX 929 -#define IDC_LOG_NV2A 930 -#define IDC_LOG_SMC 931 -#define IDC_LOG_OHCI 932 -#define IDC_LOG_USB 933 -#define IDC_LOG_HUB 934 -#define IDC_LOG_XIDCTRL 935 -#define IDC_LOG_ADM 936 -#define IDC_LOG_KRNL 937 -#define IDC_LOG_LOG 938 -#define IDC_LOG_XBOX 939 -#define IDC_LOG_XBDM 940 -#define IDC_LOG_AV 941 -#define IDC_LOG_DBG 942 -#define IDC_LOG_EX 943 -#define IDC_LOG_FSC 944 -#define IDC_LOG_HAL 945 -#define IDC_LOG_IO 946 -#define IDC_LOG_KD 947 -#define IDC_LOG_KE 948 -#define IDC_LOG_KI 949 -#define IDC_LOG_MM 950 -#define IDC_LOG_NT 951 -#define IDC_LOG_OB 952 -#define IDC_LOG_PS 953 -#define IDC_LOG_RTL 954 -#define IDC_LOG_XC 955 -#define IDC_LOG_XE 956 -#define IDC_LOG_INPSYS 957 -#define IDC_LOG_DSBUFFER 958 -#define IDC_LOG_DSSTREAM 959 -#define IDC_LOG_DS3DCALC 960 -#define IDC_LOG_XMO 961 -#define IDC_LOG_VSHCACHE 962 -#define IDC_SET_MOTOR 999 -#define IDC_SET_X 1000 -#define IDC_SET_Y 1001 -#define IDC_SET_A 1002 -#define IDC_SET_B 1003 -#define IDC_SET_WHITE 1004 -#define IDC_SET_BLACK 1005 -#define IDC_SET_LTRIGGER 1006 -#define IDC_SET_RTRIGGER 1007 -#define IDC_SET_LTHUMB 1008 -#define IDC_SET_RTHUMB 1009 -#define IDC_SET_START 1010 -#define IDC_SET_BACK 1011 -#define IDC_SET_DPAD_LEFT 1012 -#define IDC_SET_DPAD_RIGHT 1013 -#define IDC_SET_DPAD_UP 1014 -#define IDC_SET_DPAD_DOWN 1015 -#define IDC_SET_LEFT_POSY 1016 -#define IDC_SET_LEFT_NEGX 1017 -#define IDC_SET_LEFT_NEGY 1018 -#define IDC_SET_LEFT_POSX 1019 -#define IDC_INPUT_CONFIG_CANCEL 1020 -#define IDC_INPUT_CONFIG_ACCEPT 1021 -#define IDC_CONFIG_STATUS 1022 -#define IDC_SET_RIGHT_POSY 1023 -#define IDC_SET_RIGHT_NEGY 1024 -#define IDC_SET_RIGHT_NEGX 1025 -#define IDC_SET_RIGHT_POSX 1026 -#define IDC_CONFIGURE_ALL 1027 -#define IDC_BTN_EJECT 1028 -#define IDC_BTN_COCKPIT_HATCH 1029 -#define IDC_BTN_IGNITION 1030 -#define IDC_BTN_START 1031 -#define IDC_CV_FULLSCREEN 1032 -#define IDC_BTN_ZOOM_IN 1033 -#define IDC_BTN_ZOOM_OUT 1034 -#define IDC_BTN_MODE_SELECT 1035 -#define IDC_VC_DISPLAY_ADAPTER 1036 -#define IDC_BTN_SUB_MONITOR_MODE_SELECT 1037 -#define IDC_VC_D3D_DEVICE 1038 -#define IDC_BTN_OPEN_CLOSE 1039 -#define IDC_BTN_MAP_ZOON_IN_OUT 1040 -#define IDC_VC_ACCEPT 1041 -#define IDC_BTN_MAIN_WEAPON_CONTROL 1042 -#define IDC_VC_CANCEL 1043 -#define IDC_BTN_SUB_WEAPON_CONTROL 1044 -#define IDC_CV_VSYNC 1045 -#define IDC_BTN_EXTINGUISHER 1046 -#define IDC_BTN_MAGAZINE_CHANGE 1047 -#define IDC_BTN_CHAFF 1048 -#define IDC_BTN_WASHING 1049 -#define IDC_BTN_COM1 1050 -#define IDC_VC_VIDEO_RESOLUTION 1051 -#define IDC_BTN_COM2 1052 -#define IDC_VC_RENDER_RESOLUTION 1052 -#define IDC_BTN_COM3 1053 -#define IDC_BTN_COM4 1054 -#define IDC_CV_HARDWAREYUV 1055 -#define IDC_BTN_COM5 1056 -#define IDC_BTN_FUNC1 1057 -#define IDC_BTN_FUNC2 1058 -#define IDC_BTN_FUNC3 1059 -#define IDC_BTN_TANK_DETACH 1060 -#define IDC_LIST2 1061 -#define IDC_BTN_FSS 1062 -#define IDC_ABOUT 1063 -#define IDC_BTN_OVERRIDE 1064 -#define IDC_TAB1 1065 -#define IDC_BTN_NIGHT_SCOPE 1066 -#define IDC_AC_ACCEPT 1067 -#define IDC_BTN_MANIPULATOR 1068 -#define IDC_AC_CANCEL 1069 -#define IDC_BTN_LINE_COLOR_CHANGE 1070 -#define IDC_AC_AUDIO_ADAPTER 1071 -#define IDC_AC_PCM 1072 -#define IDC_AC_XADPCM 1073 -#define IDC_AC_UNKNOWN_CODEC 1074 -#define IDC_EE_CONFOUNDER 1075 -#define IDC_EE_HDDKEY 1076 -#define IDC_EE_XBOX_REGION 1077 -#define IDC_EE_SERIAL_NUMBER 1078 -#define IDC_EE_MAC_ADDRESS 1079 -#define IDC_EE_ONLINE_KEY 1080 -#define IDC_EE_AVREGION 1081 -#define IDC_EE_LANGUAGE 1082 -#define IDC_EE_AVSETTINGS 1083 -#define IDC_EE_AUDIOSETTINGS 1084 -#define IDC_EE_GAME_PRTL_CRTL 1085 -#define IDC_EE_PRTL_PASS 1086 -#define IDC_EE_MOVIE_PRTL_CRTL 1087 -#define IDC_EE_DVDREGION 1088 -#define IDC_EE_ACCEPT 1089 -#define IDC_EE_CANCEL 1090 -#define IDC_EE_RESET 1091 -#define IDC_EE_PAL60HZ 1092 -#define IDC_EE_480P 1093 -#define IDC_EE_720P 1094 -#define IDC_EE_1080I 1095 -#define ID_GUI_STATUS_KRNL_IS_READY 1096 -#define ID_GUI_STATUS_LLE_FLAGS 1097 -#define ID_GUI_STATUS_XBOX_LED_COLOUR 1098 -#define ID_GUI_STATUS_LOG_ENABLED 1099 -#define IDC_AC_MUTE_WHEN_UNFOCUS 1100 -#define IDC_XBOX_PORT_0 1158 -#define IDC_XBOX_PORT_1 1166 -#define IDC_XBOX_PORT_2 1174 -#define IDC_XBOX_PORT_3 1182 -#define IDC_LEFTBLOCK 1200 -#define IDC_LEFTBLOCK2 1201 -#define IDC_RIGHTBLOCK 1202 -#define IDC_GEAR_LEVER 1203 -#define IDC_GEAR_0 1204 -#define IDC_GEAR_1 1205 -#define IDC_GEAR_2 1206 -#define IDC_GEAR_3 1207 -#define IDC_GEAR_4 1208 -#define IDC_GEAR_5 1209 -#define IDC_GEAR_R 1210 -#define IDC_ROTATION_LEVER 1211 -#define IDC_TXT_ROTATION_LEVER 1212 -#define IDC_VT_LOCATION_MEASUREMENT 1213 -#define IDC_BUFFER_MATERIAL 1214 -#define IDC_FUEL_FLOW_RATE 1215 -#define IDC_OXYGEN_SUPPLY_SYSTEM 1216 -#define IDC_FILT_CONTROL_SYSTEM 1217 -#define IDC_PB_EJECT 1218 -#define IDC_PB_COCKPIT_HATCH 1219 -#define IDC_PB_IGNITION 1220 -#define IDC_PB_START 1221 -#define IDC_PB_GEARLEVER_5 1222 -#define IDC_PB_GEARLEVER_1 1223 -#define IDC_PB_GEARLEVER_2 1224 -#define IDC_PB_GEARLEVER_3 1225 -#define IDC_PB_GEARLEVER_4 1226 -#define IDC_PB_GEARLEVER_N 1227 -#define IDC_PB_GEARLEVER_R 1228 -#define IDC_PB_COM5 1229 -#define IDC_PB_COM1 1230 -#define IDC_PB_COM2 1231 -#define IDC_PB_COM3 1232 -#define IDC_PB_COM4 1233 -#define IDC_PB_WASHING 1234 -#define IDC_PB_MAIN_WEAPON_CONTROL 1235 -#define IDC_PB_EXTINGUISHER 1236 -#define IDC_PB_SUB 1237 -#define IDC_PB_SUB_WEAPON_CONTROL 1237 -#define IDC_PB_CHAFF 1238 -#define IDC_PB_MAGAZINE_CHANGE 1239 -#define IDC_PB_FUNC1 1240 -#define IDC_PB_FUNC2 1241 -#define IDC_PB_FUNC3 1242 -#define IDC_PB_TANK_DETACH 1243 -#define IDC_PB_OVERRIDE 1244 -#define IDC_PB_NIGHT_SCOPE 1245 -#define IDC_PB_FSS 1246 -#define IDC_PB_MANIPULATOR 1247 -#define IDC_PB_LINE_COLOR_CHANGE 1248 -#define IDC_PB_OPEN_CLOSE 1249 -#define IDC_PB_MAP_ZOOM_IN_OUT 1250 -#define IDC_PB_ZOOM_IN 1251 -#define IDC_PB_ZOOM_OUT 1252 -#define IDC_PB_MODE_SELECT 1253 -#define IDC_PB_SUB_MONITOR_MODE_SELECT 1254 -#define IDC_RADIO_TD0 1255 -#define IDC_RADIO_TD1 1256 -#define IDC_RADIO_TD2 1257 -#define IDC_RADIO_TD3 1258 -#define IDC_RADIO_TD4 1259 -#define IDC_RADIO_TD5 1260 -#define IDC_RADIO_TD6 1261 -#define IDC_RADIO_TD7 1262 -#define IDC_RADIO_TD8 1263 -#define IDC_RADIO_TD9 1264 -#define IDC_RADIO_TD10 1265 -#define IDC_RADIO_TD11 1266 -#define IDC_RADIO_TD12 1267 -#define IDC_PB_VT_LOCATION_MEASUREMENT 1268 -#define IDC_PB_OXYGEN_SUPPLY_SYSTEM 1269 -#define IDC_PB_BUFFER_MATERIAL 1270 -#define IDC_PB_VT_LOCATION_MEASUREMENT4 1271 -#define IDC_PB_FILT_CONTROL_SYSTEM 1272 -#define IDC_EVENT_LV 1273 -#define IDC_CXBXR_EVENTS 1274 -#define IDC_KERNEL_EVENTS 1275 -#define IDC_XID_CONFIG 1276 -#define IDC_DEVICE_PORT1 1277 -#define IDC_DEVICE_PORT2 1278 -#define IDC_DEVICE_PORT3 1279 -#define IDC_DEVICE_PORT4 1280 -#define IDC_CONFIGURE_PORT1 1281 -#define IDC_CONFIGURE_PORT2 1282 -#define IDC_CONFIGURE_PORT3 1283 -#define IDC_CONFIGURE_PORT4 1284 -#define IDC_DEVICE_LIST 1285 -#define IDC_REFRESH_DEVICES 1286 -#define IDC_XID_DEFAULT 1287 -#define IDC_XID_PROFILE 1288 -#define IDC_XID_PROFILE_NAME 1289 -#define IDC_XID_PROFILE_SAVE 1291 -#define IDC_XID_PROFILE_DELETE 1292 -#define IDC_XID_BUTTONS 1293 -#define IDC_XID_LSTICK 1294 -#define IDC_XID_RSTICK 1295 -#define IDC_XID_DPAD 1296 -#define IDC_XID_TRIGGERS 1297 -#define IDC_XID_RUMBLE 1298 -#define IDC_XID_OPTIONS 1299 -#define IDC_XID_CLEAR 1300 -#define IDC_RUMBLE_LIST 1301 -#define IDC_RUMBLE_TEST 1302 -#define IDC_NETWORK_ADAPTER 1303 -#define IDC_LOG_POPUP_TESTCASE 1304 -#define ID_FILE_EXIT 40005 -#define ID_HELP_ABOUT 40008 -#define ID_EMULATION_START 40009 -#define ID_FILE_OPEN_XBE 40013 -#define ID_FILE_CLOSE_XBE 40014 -#define ID_FILE_OPEN_DASHBOARD 40015 -#define ID_HELP_HOMEPAGE 40019 -#define ID_FILE_SAVEXBEFILE 40021 -#define ID_FILE_SAVEXBEFILEAS 40022 -#define ID_EDIT_LOGOBITMAP_EXPORT 40025 -#define ID_EDIT_LOGOBITMAP_IMPORT 40026 -#define ID_EDIT_PATCH_ALLOW64MB 40027 -#define ID_EDIT_PATCH_DEBUGMODE 40031 -#define ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE 40035 -#define ID_EMULATION_DEBUGOUTPUTGUI_FILE 40036 -#define ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE 40037 -#define ID_EMULATION_DEBUGOUTPUTKERNEL_FILE 40038 -#define ID_EMULATION_LLE_APU 40039 -#define ID_EMULATION_LLE_GPU 40040 -#define ID_EMULATION_LLE_JIT 40041 -#define ID_SETTINGS_CONFIG_INPUT 40046 -#define ID_SETTINGS_CONFIG_VIDEO 40047 -#define ID_SETTINGS_CONFIG_AUDIO 40048 -#define ID_SETTINGS_CONFIG_EEPROM 40049 -#define ID_FILE_RXBE_0 40050 -#define ID_FILE_RXBE_1 40051 -#define ID_FILE_RXBE_2 40052 -#define ID_FILE_RXBE_3 40053 -#define ID_FILE_RXBE_4 40054 -#define ID_FILE_RXBE_5 40055 -#define ID_FILE_RXBE_6 40056 -#define ID_FILE_RXBE_7 40057 -#define ID_FILE_RXBE_8 40058 -#define ID_FILE_RXBE_9 40059 -#define ID_EDIT_DUMPXBEINFOTO_FILE 40071 -#define ID_EDIT_DUMPXBEINFOTO_DEBUGCONSOLE 40072 -#define ID_EMULATION_STOP 40082 -#define ID_SETTINGS_CACHE 40083 -#define ID_CACHE_CLEARHLECACHE_ALL 40084 -#define ID_CACHE_CLEARHLECACHE_CURRENT 40085 -#define ID_SETTINGS_HACKS 40088 -#define ID_HACKS_DISABLEPIXELSHADERS 40089 -#define ID_LED 40090 -#define ID_SETTINGS_INITIALIZE 40091 -#define ID_EMULATION_STARTDEBUGGER 40092 -#define ID_FPS 40096 -#define ID_HACKS_RUNXBOXTHREADSONALLCORES 40098 -#define ID_HACKS_SKIPRDTSCPATCHING 40099 -#define ID_HACKS_SPEEDHACKS 40103 -#define ID_SETTINGS_CONFIG_DLOCCUSTOM 40104 -#define ID_SETTINGS_CONFIG_DLOCAPPDATA 40105 -#define ID_SETTINGS_CONFIG_DLOCEXECDIR 40106 -#define ID_SETTINGS_ALLOWADMINPRIVILEGE 40107 -#define ID_SETTINGS_CONFIG_LOGGING 40108 -#define ID_SYNC_CONFIG_LOGGING 40109 -#define ID_LOG 40110 -#define ID_SETTINGS_CONFIG_NETWORK 40111 -#define ID_SYNC_CONFIG_INPUT 40112 -#define ID_SETTINGS_EXPERIMENTAL 40113 -#define ID_USELOADEREXEC 40114 -#define ID_SETTINGS_IGNOREINVALIDXBESIG 40115 -#define ID_SETTINGS_IGNOREINVALIDXBESEC 40116 -#define IDC_STATIC -1 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 136 -#define _APS_NEXT_COMMAND_VALUE 40117 -#define _APS_NEXT_CONTROL_VALUE 1305 -#define _APS_NEXT_SYMED_VALUE 109 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Cxbx.rc +// +#define IDI_CXBX 101 +#define IDB_SPLASH 102 +#define IDR_MAINMENU 103 +#define IDB_LOGO 106 +#define IDB_ABOUT 108 +#define IDD_INPUT_CFG 111 +#define IDD_LOGGING_CFG 112 +#define IDD_VIDEO_CFG 113 +#define IDD_AUDIO_CFG 114 +#define IDD_EEPROM_CFG 115 +#define IDR_JPEG_ABOUT 116 +#define IDR_JPEG_SPLASH 118 +#define IDD_ABOUT 119 +#define IDR_CONTRIBUTORS 121 +#define IDR_COPYING 122 +#define IDD_VIRTUAL_SBC_FEEDBACK 133 +#define IDD_XID_DUKE_CFG 134 +#define IDD_RUMBLE_CFG 135 +#define IDD_NETWORK_CFG 136 +#define IDC_LOG_CANCEL 892 +#define IDC_LOG_ACCEPT 893 +#define IDC_LOG_ENABLE_GENERAL 894 +#define IDC_LOG_DISABLE_GENERAL 895 +#define IDC_LOG_ENABLE_KERNEL 896 +#define IDC_LOG_DISABLE_KERNEL 897 +#define IDC_LOG_CUSTOM_GENERAL 898 +#define IDC_LOG_CUSTOM_KERNEL 899 +#define IDC_LOG_CXBXR 900 +#define IDC_LOG_XBE 901 +#define IDC_LOG_INIT 902 +#define IDC_LOG_VMEM 903 +#define IDC_LOG_PMEM 904 +#define IDC_LOG_GUI 905 +#define IDC_LOG_EEPR 906 +#define IDC_LOG_RSA 907 +#define IDC_LOG_POOLMEM 908 +#define IDC_LOG_D3D8 909 +#define IDC_LOG_D3DST 910 +#define IDC_LOG_D3DCVT 911 +#define IDC_LOG_DSOUND 912 +#define IDC_LOG_XAPI 913 +#define IDC_LOG_XACT 914 +#define IDC_LOG_XGRP 915 +#define IDC_LOG_XONLINE 916 +#define IDC_LOG_FS 917 +#define IDC_LOG_PSHB 918 +#define IDC_LOG_PXSH 919 +#define IDC_LOG_VTXSH 920 +#define IDC_LOG_VTXB 921 +#define IDC_LOG_DINP 922 +#define IDC_LOG_XINP 923 +#define IDC_LOG_SDL 924 +#define IDC_LOG_FILE 925 +#define IDC_LOG_X86 926 +#define IDC_LOG_HLE 927 +#define IDC_LOG_NET 928 +#define IDC_LOG_MCPX 929 +#define IDC_LOG_NV2A 930 +#define IDC_LOG_SMC 931 +#define IDC_LOG_OHCI 932 +#define IDC_LOG_USB 933 +#define IDC_LOG_HUB 934 +#define IDC_LOG_XIDCTRL 935 +#define IDC_LOG_ADM 936 +#define IDC_LOG_KRNL 937 +#define IDC_LOG_LOG 938 +#define IDC_LOG_XBOX 939 +#define IDC_LOG_XBDM 940 +#define IDC_LOG_AV 941 +#define IDC_LOG_DBG 942 +#define IDC_LOG_EX 943 +#define IDC_LOG_FSC 944 +#define IDC_LOG_HAL 945 +#define IDC_LOG_IO 946 +#define IDC_LOG_KD 947 +#define IDC_LOG_KE 948 +#define IDC_LOG_KI 949 +#define IDC_LOG_MM 950 +#define IDC_LOG_NT 951 +#define IDC_LOG_OB 952 +#define IDC_LOG_PS 953 +#define IDC_LOG_RTL 954 +#define IDC_LOG_XC 955 +#define IDC_LOG_XE 956 +#define IDC_LOG_INPSYS 957 +#define IDC_LOG_DSBUFFER 958 +#define IDC_LOG_DSSTREAM 959 +#define IDC_LOG_DS3DCALC 960 +#define IDC_LOG_XMO 961 +#define IDC_LOG_VSHCACHE 962 +#define IDC_SET_MOTOR 999 +#define IDC_SET_X 1000 +#define IDC_SET_Y 1001 +#define IDC_SET_A 1002 +#define IDC_SET_B 1003 +#define IDC_SET_WHITE 1004 +#define IDC_SET_BLACK 1005 +#define IDC_SET_LTRIGGER 1006 +#define IDC_SET_RTRIGGER 1007 +#define IDC_SET_LTHUMB 1008 +#define IDC_SET_RTHUMB 1009 +#define IDC_SET_START 1010 +#define IDC_SET_BACK 1011 +#define IDC_SET_DPAD_LEFT 1012 +#define IDC_SET_DPAD_RIGHT 1013 +#define IDC_SET_DPAD_UP 1014 +#define IDC_SET_DPAD_DOWN 1015 +#define IDC_SET_LEFT_POSY 1016 +#define IDC_SET_LEFT_NEGX 1017 +#define IDC_SET_LEFT_NEGY 1018 +#define IDC_SET_LEFT_POSX 1019 +#define IDC_INPUT_CONFIG_CANCEL 1020 +#define IDC_INPUT_CONFIG_ACCEPT 1021 +#define IDC_CONFIG_STATUS 1022 +#define IDC_SET_RIGHT_POSY 1023 +#define IDC_SET_RIGHT_NEGY 1024 +#define IDC_SET_RIGHT_NEGX 1025 +#define IDC_SET_RIGHT_POSX 1026 +#define IDC_CONFIGURE_ALL 1027 +#define IDC_BTN_EJECT 1028 +#define IDC_BTN_COCKPIT_HATCH 1029 +#define IDC_BTN_IGNITION 1030 +#define IDC_BTN_START 1031 +#define IDC_CV_FULLSCREEN 1032 +#define IDC_BTN_ZOOM_IN 1033 +#define IDC_BTN_ZOOM_OUT 1034 +#define IDC_BTN_MODE_SELECT 1035 +#define IDC_VC_DISPLAY_ADAPTER 1036 +#define IDC_BTN_SUB_MONITOR_MODE_SELECT 1037 +#define IDC_VC_D3D_DEVICE 1038 +#define IDC_BTN_OPEN_CLOSE 1039 +#define IDC_BTN_MAP_ZOON_IN_OUT 1040 +#define IDC_VC_ACCEPT 1041 +#define IDC_BTN_MAIN_WEAPON_CONTROL 1042 +#define IDC_VC_CANCEL 1043 +#define IDC_BTN_SUB_WEAPON_CONTROL 1044 +#define IDC_CV_VSYNC 1045 +#define IDC_BTN_EXTINGUISHER 1046 +#define IDC_BTN_MAGAZINE_CHANGE 1047 +#define IDC_BTN_CHAFF 1048 +#define IDC_BTN_WASHING 1049 +#define IDC_BTN_COM1 1050 +#define IDC_VC_VIDEO_RESOLUTION 1051 +#define IDC_BTN_COM2 1052 +#define IDC_VC_RENDER_RESOLUTION 1052 +#define IDC_BTN_COM3 1053 +#define IDC_BTN_COM4 1054 +#define IDC_CV_HARDWAREYUV 1055 +#define IDC_BTN_COM5 1056 +#define IDC_BTN_FUNC1 1057 +#define IDC_BTN_FUNC2 1058 +#define IDC_BTN_FUNC3 1059 +#define IDC_BTN_TANK_DETACH 1060 +#define IDC_LIST2 1061 +#define IDC_BTN_FSS 1062 +#define IDC_ABOUT 1063 +#define IDC_BTN_OVERRIDE 1064 +#define IDC_TAB1 1065 +#define IDC_BTN_NIGHT_SCOPE 1066 +#define IDC_AC_ACCEPT 1067 +#define IDC_BTN_MANIPULATOR 1068 +#define IDC_AC_CANCEL 1069 +#define IDC_BTN_LINE_COLOR_CHANGE 1070 +#define IDC_AC_AUDIO_ADAPTER 1071 +#define IDC_AC_PCM 1072 +#define IDC_AC_XADPCM 1073 +#define IDC_AC_UNKNOWN_CODEC 1074 +#define IDC_EE_CONFOUNDER 1075 +#define IDC_EE_HDDKEY 1076 +#define IDC_EE_XBOX_REGION 1077 +#define IDC_EE_SERIAL_NUMBER 1078 +#define IDC_EE_MAC_ADDRESS 1079 +#define IDC_EE_ONLINE_KEY 1080 +#define IDC_EE_AVREGION 1081 +#define IDC_EE_LANGUAGE 1082 +#define IDC_EE_AVSETTINGS 1083 +#define IDC_EE_AUDIOSETTINGS 1084 +#define IDC_EE_GAME_PRTL_CRTL 1085 +#define IDC_EE_PRTL_PASS 1086 +#define IDC_EE_MOVIE_PRTL_CRTL 1087 +#define IDC_EE_DVDREGION 1088 +#define IDC_EE_ACCEPT 1089 +#define IDC_EE_CANCEL 1090 +#define IDC_EE_RESET 1091 +#define IDC_EE_PAL60HZ 1092 +#define IDC_EE_480P 1093 +#define IDC_EE_720P 1094 +#define IDC_EE_1080I 1095 +#define ID_GUI_STATUS_KRNL_IS_READY 1096 +#define ID_GUI_STATUS_LLE_FLAGS 1097 +#define ID_GUI_STATUS_XBOX_LED_COLOUR 1098 +#define ID_GUI_STATUS_LOG_ENABLED 1099 +#define IDC_AC_MUTE_WHEN_UNFOCUS 1100 +#define IDC_XBOX_PORT_0 1158 +#define IDC_XBOX_PORT_1 1166 +#define IDC_XBOX_PORT_2 1174 +#define IDC_XBOX_PORT_3 1182 +#define IDC_LEFTBLOCK 1200 +#define IDC_LEFTBLOCK2 1201 +#define IDC_RIGHTBLOCK 1202 +#define IDC_GEAR_LEVER 1203 +#define IDC_GEAR_0 1204 +#define IDC_GEAR_1 1205 +#define IDC_GEAR_2 1206 +#define IDC_GEAR_3 1207 +#define IDC_GEAR_4 1208 +#define IDC_GEAR_5 1209 +#define IDC_GEAR_R 1210 +#define IDC_ROTATION_LEVER 1211 +#define IDC_TXT_ROTATION_LEVER 1212 +#define IDC_VT_LOCATION_MEASUREMENT 1213 +#define IDC_BUFFER_MATERIAL 1214 +#define IDC_FUEL_FLOW_RATE 1215 +#define IDC_OXYGEN_SUPPLY_SYSTEM 1216 +#define IDC_FILT_CONTROL_SYSTEM 1217 +#define IDC_PB_EJECT 1218 +#define IDC_PB_COCKPIT_HATCH 1219 +#define IDC_PB_IGNITION 1220 +#define IDC_PB_START 1221 +#define IDC_PB_GEARLEVER_5 1222 +#define IDC_PB_GEARLEVER_1 1223 +#define IDC_PB_GEARLEVER_2 1224 +#define IDC_PB_GEARLEVER_3 1225 +#define IDC_PB_GEARLEVER_4 1226 +#define IDC_PB_GEARLEVER_N 1227 +#define IDC_PB_GEARLEVER_R 1228 +#define IDC_PB_COM5 1229 +#define IDC_PB_COM1 1230 +#define IDC_PB_COM2 1231 +#define IDC_PB_COM3 1232 +#define IDC_PB_COM4 1233 +#define IDC_PB_WASHING 1234 +#define IDC_PB_MAIN_WEAPON_CONTROL 1235 +#define IDC_PB_EXTINGUISHER 1236 +#define IDC_PB_SUB 1237 +#define IDC_PB_SUB_WEAPON_CONTROL 1237 +#define IDC_PB_CHAFF 1238 +#define IDC_PB_MAGAZINE_CHANGE 1239 +#define IDC_PB_FUNC1 1240 +#define IDC_PB_FUNC2 1241 +#define IDC_PB_FUNC3 1242 +#define IDC_PB_TANK_DETACH 1243 +#define IDC_PB_OVERRIDE 1244 +#define IDC_PB_NIGHT_SCOPE 1245 +#define IDC_PB_FSS 1246 +#define IDC_PB_MANIPULATOR 1247 +#define IDC_PB_LINE_COLOR_CHANGE 1248 +#define IDC_PB_OPEN_CLOSE 1249 +#define IDC_PB_MAP_ZOOM_IN_OUT 1250 +#define IDC_PB_ZOOM_IN 1251 +#define IDC_PB_ZOOM_OUT 1252 +#define IDC_PB_MODE_SELECT 1253 +#define IDC_PB_SUB_MONITOR_MODE_SELECT 1254 +#define IDC_RADIO_TD0 1255 +#define IDC_RADIO_TD1 1256 +#define IDC_RADIO_TD2 1257 +#define IDC_RADIO_TD3 1258 +#define IDC_RADIO_TD4 1259 +#define IDC_RADIO_TD5 1260 +#define IDC_RADIO_TD6 1261 +#define IDC_RADIO_TD7 1262 +#define IDC_RADIO_TD8 1263 +#define IDC_RADIO_TD9 1264 +#define IDC_RADIO_TD10 1265 +#define IDC_RADIO_TD11 1266 +#define IDC_RADIO_TD12 1267 +#define IDC_PB_VT_LOCATION_MEASUREMENT 1268 +#define IDC_PB_OXYGEN_SUPPLY_SYSTEM 1269 +#define IDC_PB_BUFFER_MATERIAL 1270 +#define IDC_PB_VT_LOCATION_MEASUREMENT4 1271 +#define IDC_PB_FILT_CONTROL_SYSTEM 1272 +#define IDC_EVENT_LV 1273 +#define IDC_CXBXR_EVENTS 1274 +#define IDC_KERNEL_EVENTS 1275 +#define IDC_XID_CONFIG 1276 +#define IDC_DEVICE_PORT1 1277 +#define IDC_DEVICE_PORT2 1278 +#define IDC_DEVICE_PORT3 1279 +#define IDC_DEVICE_PORT4 1280 +#define IDC_CONFIGURE_PORT1 1281 +#define IDC_CONFIGURE_PORT2 1282 +#define IDC_CONFIGURE_PORT3 1283 +#define IDC_CONFIGURE_PORT4 1284 +#define IDC_DEVICE_LIST 1285 +#define IDC_REFRESH_DEVICES 1286 +#define IDC_XID_DEFAULT 1287 +#define IDC_XID_PROFILE 1288 +#define IDC_XID_PROFILE_NAME 1289 +#define IDC_XID_PROFILE_SAVE 1291 +#define IDC_XID_PROFILE_DELETE 1292 +#define IDC_XID_BUTTONS 1293 +#define IDC_XID_LSTICK 1294 +#define IDC_XID_RSTICK 1295 +#define IDC_XID_DPAD 1296 +#define IDC_XID_TRIGGERS 1297 +#define IDC_XID_RUMBLE 1298 +#define IDC_XID_OPTIONS 1299 +#define IDC_XID_CLEAR 1300 +#define IDC_RUMBLE_LIST 1301 +#define IDC_RUMBLE_TEST 1302 +#define IDC_NETWORK_ADAPTER 1303 +#define IDC_LOG_POPUP_TESTCASE 1304 +#define ID_FILE_EXIT 40005 +#define ID_HELP_ABOUT 40008 +#define ID_EMULATION_START 40009 +#define ID_FILE_OPEN_XBE 40013 +#define ID_FILE_CLOSE_XBE 40014 +#define ID_FILE_OPEN_DASHBOARD 40015 +#define ID_HELP_HOMEPAGE 40019 +#define ID_FILE_SAVEXBEFILE 40021 +#define ID_FILE_SAVEXBEFILEAS 40022 +#define ID_EDIT_LOGOBITMAP_EXPORT 40025 +#define ID_EDIT_LOGOBITMAP_IMPORT 40026 +#define ID_EDIT_PATCH_ALLOW64MB 40027 +#define ID_EDIT_PATCH_DEBUGMODE 40031 +#define ID_EMULATION_DEBUGOUTPUTGUI_CONSOLE 40035 +#define ID_EMULATION_DEBUGOUTPUTGUI_FILE 40036 +#define ID_EMULATION_DEBUGOUTPUTKERNEL_CONSOLE 40037 +#define ID_EMULATION_DEBUGOUTPUTKERNEL_FILE 40038 +#define ID_EMULATION_LLE_APU 40039 +#define ID_EMULATION_LLE_GPU 40040 +#define ID_EMULATION_LLE_JIT 40041 +#define ID_SETTINGS_CONFIG_INPUT 40046 +#define ID_SETTINGS_CONFIG_VIDEO 40047 +#define ID_SETTINGS_CONFIG_AUDIO 40048 +#define ID_SETTINGS_CONFIG_EEPROM 40049 +#define ID_FILE_RXBE_0 40050 +#define ID_FILE_RXBE_1 40051 +#define ID_FILE_RXBE_2 40052 +#define ID_FILE_RXBE_3 40053 +#define ID_FILE_RXBE_4 40054 +#define ID_FILE_RXBE_5 40055 +#define ID_FILE_RXBE_6 40056 +#define ID_FILE_RXBE_7 40057 +#define ID_FILE_RXBE_8 40058 +#define ID_FILE_RXBE_9 40059 +#define ID_EDIT_DUMPXBEINFOTO_FILE 40071 +#define ID_EDIT_DUMPXBEINFOTO_DEBUGCONSOLE 40072 +#define ID_EMULATION_STOP 40082 +#define ID_SETTINGS_CACHE 40083 +#define ID_CACHE_CLEARHLECACHE_ALL 40084 +#define ID_CACHE_CLEARHLECACHE_CURRENT 40085 +#define ID_SETTINGS_HACKS 40088 +#define ID_HACKS_DISABLEPIXELSHADERS 40089 +#define ID_LED 40090 +#define ID_SETTINGS_INITIALIZE 40091 +#define ID_EMULATION_STARTDEBUGGER 40092 +#define ID_FPS 40096 +#define ID_HACKS_RUNXBOXTHREADSONALLCORES 40098 +#define ID_HACKS_SKIPRDTSCPATCHING 40099 +#define ID_HACKS_SPEEDHACKS 40103 +#define ID_SETTINGS_CONFIG_DLOCCUSTOM 40104 +#define ID_SETTINGS_CONFIG_DLOCAPPDATA 40105 +#define ID_SETTINGS_CONFIG_DLOCEXECDIR 40106 +#define ID_SETTINGS_ALLOWADMINPRIVILEGE 40107 +#define ID_SETTINGS_CONFIG_LOGGING 40108 +#define ID_SYNC_CONFIG_LOGGING 40109 +#define ID_LOG 40110 +#define ID_SETTINGS_CONFIG_NETWORK 40111 +#define ID_SYNC_CONFIG_INPUT 40112 +#define ID_SETTINGS_EXPERIMENTAL 40113 +#define ID_USELOADEREXEC 40114 +#define ID_SETTINGS_IGNOREINVALIDXBESIG 40115 +#define ID_SETTINGS_IGNOREINVALIDXBESEC 40116 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 136 +#define _APS_NEXT_COMMAND_VALUE 40117 +#define _APS_NEXT_CONTROL_VALUE 1305 +#define _APS_NEXT_SYMED_VALUE 109 +#endif +#endif diff --git a/src/gui/targetver.h b/src/gui/targetver.h index 90e767bfc..87c0086de 100644 --- a/src/gui/targetver.h +++ b/src/gui/targetver.h @@ -1,8 +1,8 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -#include +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/src/version.h.in b/src/version.h.in index ea81b51d4..851d31b79 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,3 +1,3 @@ #pragma once -#cmakedefine _GIT_VERSION "@_GIT_VERSION@" +#cmakedefine _GIT_VERSION "@_GIT_VERSION@" diff --git a/src/vsbc/CxbxVSBC.h b/src/vsbc/CxbxVSBC.h index 962869fa6..7def7099d 100644 --- a/src/vsbc/CxbxVSBC.h +++ b/src/vsbc/CxbxVSBC.h @@ -1,11 +1,11 @@ -#pragma once - +#pragma once + #ifdef CXBXVSBC_EXPORTS #define CXBXVSBC_API __declspec(dllexport) #else #define CXBXVSBC_API __declspec(dllimport) -#endif - +#endif + #ifndef X_XONTROLLER_HOST_BRIDGE_HOSTTYPE_VIRTUAL_SBC #define X_XONTROLLER_HOST_BRIDGE_HOSTTYPE_VIRTUAL_SBC 0x80 #endif