Dxbx/Source/Delphi/src/uXbe.pas

1322 lines
45 KiB
ObjectPascal

(*
This file is part of Dxbx - a XBox emulator written in Delphi (ported over from cxbx)
Copyright (C) 2007 Shadow_tj and other members of the development team.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*)
unit uXbe;
{$INCLUDE Dxbx.inc}
interface
uses
// Delphi
Windows, // for DWord
SysUtils, // for Format
StrUtils, // for IfThen
IniFiles, // for TIniFile
Classes,
Controls,
Dialogs, // for MessageDlg
Graphics, // for TBitmap
UITypes, // prevents H2443 Inline function 'MessageDlg' has not been expanded
// Dxbx
XbeHeaders,
uConsts,
uTypes,
uTime,
uDxbxUtils,
uLog,
uEmuD3D8Types, // X_D3DBaseTexture
uFileSystem; // Drives
type
TXbeType = (xtRetail, xtDebug, xtChihiro);
const
// Entry point address XOR keys per Xbe type (Retail, Debug or Chihiro) :
XOR_EP_KEY: array [TXbeType] of DWORD = (XOR_EP_RETAIL, XOR_EP_DEBUG, XOR_ENTRY_POINT_CHIHIRO);
// Kernel thunk address XOR keys per Xbe type (Retail, Debug or Chihiro) :
XOR_KT_KEY: array [TXbeType] of DWORD = (XOR_KT_RETAIL, XOR_KT_DEBUG, XOR_KERNEL_THUNK_CHIHIRO);
PE_HEADER_ALIGNMENT = $1000; // - Actually, there's no such thing; JclDebug calls this 'ModuleCodeOffset'
_MagicNumber = 'XBEH'; // Xbe File Format
// TODO -oDxbx : Remove most dependancies on this TXbe type in OpenXbe and it's callers.
// Instead, start accessesing Xbe's (and other resources) via Drives.D.FileSystem,
// which would create a better layer of seperation. Do note, that some kernel
// I/O functions (like asynchronuous file access) don't map too well to this
// setup... but even if this happens, we could still use the FileSystem abstraction
// but bypass it for the special cases (which means we can only do this on MappedFolders)
type
XBE_LIBRARYVERSIONs = array of XBE_LIBRARYVERSION;
TXbe = class(TObject)
private
MyFile: TMemoryStream;
FRawData: MathPtr;
procedure ConstructorInit;
function GetFileSize: Int64;
public
FIsValid: boolean;
XbePath: string;
m_bzSection: array of TRawSection;
m_Certificate: XBE_CERTIFICATE;
m_Header: XBEIMAGE_HEADER;
m_HeaderEx: array of Byte;
m_KernelLibraryVersion: XBE_LIBRARYVERSION;
m_LibraryVersion: XBE_LIBRARYVERSIONs;
m_ExtraLibraryVersion: XBE_LIBRARYVERSIONs;
m_SectionHeader: array of XBE_SECTIONHEADER;
m_szSectionName: array of array of AnsiChar; // TODO -oDXBX: Use XBE_SECTIONNAME_MAXLENGTH
m_TLS: PXBE_TLS;
m_XAPILibraryVersion: XBE_LIBRARYVERSION;
property RawData: MathPtr read FRawData;
property FileSize: Int64 read GetFileSize;
property isValid: boolean read FisValid;
class function FileExists(aFileName: string): Boolean;
constructor Create(const aFileName: string);
destructor Destroy; override;
function GetTLSData: DWord;
function CanRunXbe(out NoRunReason: string): Boolean;
function DetermineDumpFileName: string;
function DumpInformation(FileName: string = ''): Boolean;
function GetAddr(x_dwVirtualAddress: DWord): Integer;
function GetAddrStr(x_dwVirtualAddress: DWord; const aMaxLen: Integer = MaxInt): string;
function GetAddrWStr(x_dwVirtualAddress: DWord; const aMaxLen: Integer = MaxInt): WideString;
function FindSection(const aSectionName: string; out Size: Integer): TRawSection;
function ExportLogoBitmap(aBitmap: TBitmap): Boolean;
function ExportIconBitmap(aBitmap: TBitmap): Boolean;
function ExportXPRToBitmap(XprImage: PXPR_IMAGE; aBitmap: TBitmap): Boolean;
end;
var
g_Xbe_XbePath: string; // The path of the running Xbe, as seen from Windows
// OpenXDK logo bitmap (used by cxbe by default)
OpenXDK: array of uint08;
dwSizeOfOpenXDK: uint32;
function GetDWordVal(aBuffer: MathPtr; i: Integer): DWord;
function BetterTime(x_timeDate: uint32): string;
function TitleIDToString(TitleID: DWord): string;
function OpenXbe(aFileName: string; var aXbe: TXbe{; var aExeFileName, aXbeFileName: string}): Boolean;
procedure XbeLoaded;
function GetReadableTitle(const pCertificate: PXBE_CERTIFICATE): string;
function AllowedMediaToString(const aAllowedMedia: Cardinal): string;
function GameRegionToString(const aGameRegion: Cardinal): string;
function GameRatingToString(const aGameRating: Cardinal): string;
function GameDisplayFrequency(const aGameRegion: Cardinal): int;
function XbeHeaderInitFlagsToString(const Flag: DWORD): string;
function GetXbeType(const aXbeHeader: PXbeHeader): TXbeType;
function XbeTypeToString(const aXbeType: TXbeType): string;
procedure AddSymbolToList(const aStringList: TStringList; const Address: Pointer; MangledName: string);
procedure LoadSymbolsFromCache(const aStringList: TStringList; const aCacheFile: string);
var
DumpToolString: string;
m_szAsciiTitle: string;
implementation
procedure AddSymbolToList(const aStringList: TStringList; const Address: Pointer; MangledName: string);
begin
aStringList.AddObject(MangledName, TObject(Address));
// Sort the list again (TODO : Maybe we should insert at the right spot later, but this was faster to implement) :
aStringList.CustomSort(@SortObjects);
end;
procedure LoadSymbolsFromCache(const aStringList: TStringList; const aCacheFile: string);
var
i, j: Integer;
FuncStr, AddrStr: string;
Addr: Pointer;
begin
with TIniFile.Create(aCacheFile) do
try
ReadSectionValues('Symbols', aStringList);
finally
Free;
end;
with aStringList do
begin
// Split up each line into name and address, putting the name back as a string
// and the address in the Object column :
for i := 0 to Count - 1 do
begin
FuncStr := Strings[i];
// In the [Symbols] section, each line looks like this :
// ?Pause@CDirectSoundStream@DirectSound@@QAGJK@Z=$000CCDB1;DirectSound.CDirectSoundStream.Pause
// ^-------mangled function name----------------^ ^address^ ^----unmangled function name-------^
// Find the split-location :
j := Pos('=$', FuncStr);
if j > 0 then
begin
// Get the 8-digit memory address from this position (the additional unmangled name is ignored),
// Convert that address to a pointer, and put that in the Object-part of the output StringList :
AddrStr := Copy(FuncStr, j + 2, 8);
Addr := Pointer(HexToIntDef(AddrStr, 0));
Objects[i] := TObject(Addr);
// Extract the mangled function name, and put that in the String-part of the output StringList :
System.Delete(FuncStr, j, MaxInt);
Strings[i] := FuncStr;
end;
end;
// Sort the list on address :
CustomSort(@SortObjects);
end;
end;
procedure XbeLoaded;
begin
WriteLog(DxbxFormat('DXBX: %s loaded.', [m_szAsciiTitle]));
end;
function GetReadableTitle(const pCertificate: PXBE_CERTIFICATE): string;
begin
// Use Title, or when that's empty, the parent folder name :
Result := Trim(PWideCharMaxLenToString(pCertificate.wszTitleName, XBE_TITLENAME_MAXLENGTH));
if Result <> '' then
Exit;
Result := ExtractFileName(g_Xbe_XbePath);
if SameText(Result, 'default.xbe') then
Result := ExtractFileName(ExtractFileDir(g_Xbe_XbePath))
else
Result := ChangeFileExt(Result, '');
end;
function OpenXbe(aFileName: string; var aXbe: TXbe{; var aExeFileName, aXbeFileName: string}): Boolean;
begin
Result := False;
if Assigned(aXbe) or not (TXbe.FileExists(aFileName)) then
Exit;
{var}aXbe := TXbe.Create({aXbe}aFileName);
try
Result := aXbe.isValid;
if Result then
XbeLoaded();
except
FreeAndNil(aXbe);
raise;
end;
end;
function GetDWordVal(aBuffer: MathPtr; i: Integer): DWord;
begin
Result := (Ord(aBuffer[i + 0]) shl 0)
+ (Ord(aBuffer[i + 1]) shl 8)
+ (Ord(aBuffer[i + 2]) shl 16)
+ (Ord(aBuffer[i + 3]) shl 24);
end; // GetDwordVal
function GameDisplayFrequency(const aGameRegion: Cardinal): int;
begin
Result := 60;
if (aGameRegion and XBEIMAGE_GAME_REGION_ALL) = XBEIMAGE_GAME_REGION_RESTOFWORLD then
Result := 50;
end;
function AllowedMediaToString(const aAllowedMedia: Cardinal): string;
begin
Result := '';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_MEDIA_MASK) > 0 then
begin
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_HARD_DISK) > 0 then
Result := Result + ' HARD_DISK';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DVD_X2) > 0 then
Result := Result + ' DVD_X2';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DVD_CD) > 0 then
Result := Result + ' DVD_CD';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_CD) > 0 then
Result := Result + ' CD';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DVD_5_RO) > 0 then
Result := Result + ' DVD_5_RO';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DVD_9_RO) > 0 then
Result := Result + ' DVD_9_RO';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DVD_5_RW) > 0 then
Result := Result + ' DVD_5_RW';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DVD_9_RW) > 0 then
Result := Result + ' DVD_9_RW';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_DONGLE) > 0 then
Result := Result + ' DONGLE';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD) > 0 then
Result := Result + ' BOARD';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_MEDIA_MASK) >= (XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD * 2) then
Result := Result + ' UNKNOWN '; // IntToStr(aAllowedMedia)
Result := 'MEDIA_TYPE:' + StringReplace(Trim(Result), ' ', '+', [rfReplaceAll]);
end;
if (aAllowedMedia and (not XBEIMAGE_MEDIA_TYPE_MEDIA_MASK)) > 0 then
begin
Result := Result + ' NONSECURE:';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_NONSECURE_HARD_DISK) > 0 then
Result := Result + 'HARD_DISK';
if (aAllowedMedia and XBEIMAGE_MEDIA_TYPE_NONSECURE_MODE) > 0 then
Result := Result + ' MODE';
end;
end;
function GameRegionToString(const aGameRegion: Cardinal): string;
begin
if (aGameRegion and XBEIMAGE_GAME_REGION_ALL) = XBEIMAGE_GAME_REGION_ALL then
Result := 'ALL'
else
begin
Result := '';
if (aGameRegion and XBEIMAGE_GAME_REGION_JAPAN) > 0 then
Result := Result + ' JAP';
if (aGameRegion and XBEIMAGE_GAME_REGION_US_CANADA) > 0 then
Result := Result + ' NTSC';
if (aGameRegion and XBEIMAGE_GAME_REGION_RESTOFWORLD) > 0 then
Result := Result + ' PAL';
end;
if (aGameRegion and XBEIMAGE_GAME_REGION_MANUFACTURING) > 0 then
Result := Result + ' DEBUG';
Result := StringReplace(Trim(Result), ' ', '+', [rfReplaceAll]);
if Result = '' then
begin
if aGameRegion = 0 then
Result := 'UNKNOWN'
else
Result := 'REGION ' + IntToStr(aGameRegion);
end;
end;
function GameRatingToString(const aGameRating: Cardinal): string;
const
GameRatingStrings: array [0..6] of string = (
// Info from: http://xboxdevwiki.net/EEPROM
'(RP) Rating Pending',
'(AO) Adults Only',
'(M) Mature',
'(T) Teen',
'(E) Everyone',
'(K-A) Kids to Adults',
'(EC) Early Childhood'
);
begin
if aGameRating < Length(GameRatingStrings) then
Result := GameRatingStrings[aGameRating]
else
Result := 'ERROR: Unknown rating';
end;
function XbeHeaderInitFlagsToString(const Flag: DWORD): string;
begin
Result := '';
if (Flag and XBE_INIT_FLAG_MountUtilityDrive) > 0 then
Result := Result + '[Mount Utility Drive] ';
if (Flag and XBE_INIT_FLAG_FormatUtilityDrive) > 0 then
Result := Result + '[Format Utility Drive] ';
if (Flag and XBE_INIT_FLAG_Limit64MB) > 0 then
Result := Result + '[Limit Devkit Run Time Memory to 64MB] ';
if (Flag and XBE_INIT_FLAG_DontSetupHarddisk) > 0 then
Result := Result + '[Setup Harddisk] ';
end;
function GetXbeType(const aXbeHeader: PXbeHeader): TXbeType;
begin
// 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 (aXbeHeader.dwEntryAddr and $f0000000) = $40000000 then
Result := xtChihiro
else
// 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 (aXbeHeader.dwKernelImageThunkAddr and $80000000) > 0 then
Result := xtDebug
else
// Otherwise, the XBE is a Retail build :
Result := xtRetail;
end;
function XbeTypeToString(const aXbeType: TXbeType): string;
const
XbeTypeString: array [TXbeType] of string = ('Retail', 'Debug', 'Chihiro');
begin
Result := XbeTypeString[aXbeType];
end;
{ TXbe }
procedure TXbe.ConstructorInit;
begin
FreeAndNil(MyFile);
FRawData := nil;
SetLength(m_HeaderEx, 0);
SetLength(m_SectionHeader, 0);
SetLength(m_szSectionName, 0);
SetLength(m_LibraryVersion, 0);
SetLength(m_ExtraLibraryVersion, 0);
FreeMem({var}m_TLS);
SetLength(m_bzSection, 0);
end; // TXbe.ConstructorInit
class function TXbe.FileExists(aFileName: string): Boolean;
var
DummyStr: string;
begin
Result := Drives.D.OpenImage(aFileName, {out}DummyStr)
// and SameText(ExtractFileExt(Drives.D.FileSystem.SelectedFile), '.xbe');
end;
constructor TXbe.Create(const aFileName: string);
var
FileName: string;
FileHandle: TFileHandle;
sFileType: string;
ExSize: LongInt;
lIndex, lIndex2: DWord;
RawSize, RawAddr: DWord;
I: DWord;
Drive: PLogicalVolume;
begin
ConstructorInit();
Drive := Drives.D;
FisValid := Drive.OpenImage(aFileName, {out}FileName);
if not FisValid then
begin
MessageDlg(DxbxFormat('Could not open path : %s', [aFileName]), mtError, [mbOk], 0);
Exit;
end;
MyFile := TMemoryStream.Create;
g_Xbe_XbePath := aFileName;
FileHandle := Drive.FileSystem.Open(FileName);
try
MyFile.Size := Drive.FileSystem.Seek(FileHandle, 0, soFromEnd);
Drive.FileSystem.Seek(FileHandle, 0, soFromBeginning);
Drive.FileSystem.Read(FileHandle, MyFile.Memory^, MyFile.Size);
finally
Drive.FileSystem.Close(FileHandle);
end;
FRawData := MyFile.Memory;
// verify xbe file was opened
sFileType := ExtractFileExt(aFileName);
FisValid := (MyFile.Size <> 0);
if not FisValid then
begin
MessageDlg(DxbxFormat('Could not open %s file', [sFileType]), mtError, [mbOk], 0);
Exit;
end;
WriteLog(DxbxFormat('DXBX: Opening %s file...OK', [sFileType]));
// remember xbe path
XbePath := aFileName;
WriteLog(DxbxFormat('DXBX: Storing %s Path...Ok', [sFileType]));
FisValid := not (MyFile.Size < SizeOf(m_Header));
if not FisValid then
begin
MessageDlg(DxbxFormat('Unexpected end of file while reading %s Image Header', [sFileType]), mtError, [mbOk], 0);
Exit;
end;
i := 0;
CopyMemory(@m_Header, RawData, SizeOf(m_Header));
Inc(i, SizeOf(m_Header));
// check xbe image header
FisValid := m_Header.dwMagic = _MagicNumber;
if not FisValid then
begin
MessageDlg(DxbxFormat('Invalid magic number in %s file', [sFileType]), mtError, [mbOk], 0);
Exit;
end;
WriteLog('DXBX: Reading Image Header...Ok');
ExSize := RoundUp(m_Header.dwSizeofHeaders, PE_HEADER_ALIGNMENT) - SizeOf(m_Header);
// Read Xbe Image Header Extra Bytes
if (m_Header.dwSizeofHeaders > SizeOf(m_Header)) then
begin
WriteLog('DXBX: Reading Image Header Extra Bytes...');
SetLength(m_HeaderEx, ExSize);
CopyMemory(m_HeaderEx, @(RawData[i]), ExSize);
// Inc(i, ExSize);
end;
// read xbe certificate
i := m_Header.dwCertificateAddr - m_Header.dwBaseAddr;
CopyMemory(@m_Certificate, @(RawData[i]), SizeOf(m_Certificate));
WriteLog('DXBX: Reading Certificate...OK');
m_szAsciiTitle := GetReadableTitle(@m_Certificate);
WriteLog('DXBX: Title = ' + m_szAsciiTitle);
// read xbe section headers
begin
{$IFDEF DXBX_DEBUG}
DbgPrintf('DXBX: Reading Section Headers...');
{$ENDIF}
i := m_Header.dwSectionHeadersAddr - m_Header.dwBaseAddr;
SetLength(m_SectionHeader, m_Header.dwSections);
for lIndex := 0 to m_Header.dwSections - 1 do
begin
CopyMemory(@(m_SectionHeader[lIndex]), @(RawData[i]), SizeOf(m_SectionHeader[lIndex]));
Inc(i, SizeOf(m_SectionHeader[lIndex]));
{$IFDEF DXBX_DEBUG}
DbgPrintf('DXBX: Reading Section Header 0x%.4x... OK', [lIndex]);
{$ENDIF}
end; // for LIndex
end;
// Read xbe section names
begin
{$IFDEF DXBX_DEBUG}
DbgPrintf('DXBX: Reading Section Names...');
{$ENDIF}
SetLength(m_szSectionName, m_Header.dwSections, XBE_SECTIONNAME_MAXLENGTH);
for lIndex := 0 to m_Header.dwSections - 1 do
begin
RawAddr := GetAddr(m_SectionHeader[lIndex].dwSectionNameAddr);
if RawAddr <> 0 then
begin
for lIndex2 := 0 to XBE_SECTIONNAME_MAXLENGTH -1 do
begin
m_szSectionName[lIndex][lIndex2] := AnsiChar(RawData[RawAddr + lIndex2]);
if m_szSectionName[lIndex][lIndex2] = #0 then
break;
end; // for lIndex2
end; // if
{$IFDEF DXBX_DEBUG}
DbgPrintf('DXBX: Reading Section Name 0x%.04X... OK (%s)', [lIndex, PAnsiChar(m_szSectionName[lIndex])]);
{$ENDIF}
end; // for lIndex
end;
// Read xbe library versions
if m_Header.dwLibraryVersionsAddr <> 0 then
begin
WriteLog('DXBX: Reading Library Versions...');
i := m_Header.dwLibraryVersionsAddr - m_Header.dwBaseAddr;
SetLength(m_LibraryVersion, m_Header.dwLibraryVersions);
for lIndex := 0 to m_Header.dwLibraryVersions - 1 do
begin
CopyMemory(@(m_LibraryVersion[lIndex]), @(RawData[i]), SizeOf(m_LibraryVersion[lIndex]));
Inc(i, SizeOf(m_LibraryVersion[lIndex]));
WriteLog(DxbxFormat('DXBX: Reading Library Version 0x%.4x... OK', [lIndex]));
end; // for LIndex
end;
// read xbe kernel library version
WriteLog('DXBX: Reading Kernel Library Version...');
if m_Header.dwKernelLibraryVersionAddr = 0 then
MessageDlg('Could not locate kernel library version', mtError, [mbOk], 0)
else
begin
i := m_Header.dwKernelLibraryVersionAddr - m_Header.dwBaseAddr;
CopyMemory({Dest=}@m_KernelLibraryVersion, {Source=}@(RawData[i]), SizeOf(XBE_LIBRARYVERSION));
WriteLog(DxbxFormat('DXBX: Kernel Library Version = %d... OK', [m_KernelLibraryVersion.wBuildVersion]));
end;
// read xbe xapi library version
WriteLog('DXBX: Reading Xapi Library Version...');
if m_Header.dwXAPILibraryVersionAddr = 0 then
MessageDlg('Could not locate Xapi Library Version', mtError, [mbOk], 0)
else
begin
i := m_Header.dwXAPILibraryVersionAddr - m_Header.dwBaseAddr;
CopyMemory({Dest=}@m_XAPILibraryVersion, {Source=}@(RawData[i]), SizeOf(XBE_LIBRARYVERSION));
WriteLog(DxbxFormat('DXBX: XAPI Library Version = %d... OK', [m_XAPILibraryVersion.wBuildVersion]));
end;
// Does the header contain extra library versions?
if m_Header.dwSizeofImageHeader > FIELD_OFFSET(PXbeHeader(nil).dwExtraLibraryVersions) then
// Read xbe extra library versions
if m_Header.dwExtraLibraryVersionsAddr <> 0 then
begin
WriteLog('DXBX: Reading Extra Library Versions...');
i := m_Header.dwExtraLibraryVersionsAddr - m_Header.dwBaseAddr;
SetLength(m_ExtraLibraryVersion, m_Header.dwExtraLibraryVersions);
for lIndex := 0 to m_Header.dwExtraLibraryVersions - 1 do
begin
CopyMemory(@(m_ExtraLibraryVersion[lIndex]), @(RawData[i]), SizeOf(m_ExtraLibraryVersion[lIndex]));
Inc(i, SizeOf(m_ExtraLibraryVersion[lIndex]));
WriteLog(DxbxFormat('DXBX: Reading Extra Library Version 0x%.4x... OK', [lIndex]));
end; // for LIndex
end;
// read Xbe sections
begin
WriteLog('DXBX: Reading Sections...');
SetLength(m_bzSection, m_Header.dwSections);
for lIndex := 0 to m_Header.dwSections - 1 do
begin
//Debug info of turok from cxbx
//v=0 RawSize: 1578256 RawAddr: 4096
//v=2 RawSize: 1585152 RawAddr: 63024
//v=2 RawSize: 1650688 RawAddr: 1344
//v=3 RawSize: 1654784 RawAddr: 33336
//v=4 RawSize: 1691648 RawAddr: 120692
//v=5 RawSize: 1814528 RawAddr: 160420
//v=6 RawSize: 1978368 RawAddr: 28996
//v=7 RawSize: 2011136 RawAddr: 246588
//v=8 RawSize: 2260992 RawAddr: 99384
RawSize := m_SectionHeader[lIndex].dwSizeofRaw;
if RawSize > 0 then // Dxbx addition, to prevent againt empty sections
begin
RawAddr := m_SectionHeader[lIndex].dwRawAddr;
SetLength(m_bzSection[lIndex], RawSize);
for lIndex2 := 0 to RawSize - 1 do
m_bzSection[lIndex][lIndex2] := Byte(RawData[RawAddr + lIndex2]);
end;
WriteLog(DxbxFormat('DXBX: Reading Section 0x%.4x... OK', [lIndex]));
end;
// if lIndex2 < RawSize then
// WriteLog(DxbxFormat('Unexpected end of file while reading %s Section', [sFileType] ));
end;
if m_Header.dwTLSAddr <> 0 then
begin
WriteLog('DXBX: Reading Thread Local Storage...');
if GetAddr(m_Header.dwTLSAddr) <> 0 then
begin
m_TLS := AllocMem(SizeOf(XBE_TLS));
i := GetAddr(m_Header.dwTLSAddr);
m_TLS.dwDataStartAddr := GetDwordVal(RawData, i);
i := i + 4;
m_TLS.dwDataEndAddr := GetDwordVal(RawData, i);
i := i + 4;
m_TLS.dwTLSIndexAddr := GetDwordVal(RawData, i);
i := i + 4;
m_TLS.dwTLSCallbackAddr := GetDwordVal(RawData, i);
i := i + 4;
m_TLS.dwSizeofZeroFill := GetDwordVal(RawData, i);
i := i + 4;
m_TLS.dwCharacteristics := GetDwordVal(RawData, i);
end;
end;
end; // TXbe.Create
destructor TXbe.Destroy;
begin
m_szAsciiTitle := '';
ConstructorInit();
inherited Destroy;
end;
function TXbe.CanRunXbe(out NoRunReason: string): Boolean;
var
i: Integer;
begin
NoRunReason := '';
if Length(m_LibraryVersion) = 0 then
NoRunReason := 'No linked libraries found!'
else
for i := 0 to Length(m_LibraryVersion) - 1 do
begin
if Pos({D3D}'8LTCG', UpperCase(string(AnsiString(m_LibraryVersion[i].szName)))) > 0 then
begin
NoRunReason := 'Cannot patch link-time optimized libraries!';
Break;
end;
if Pos({D3D}'8D', UpperCase(string(AnsiString(m_LibraryVersion[i].szName)))) > 0 then
begin
NoRunReason := 'Cannot patch debug libraries!';
Break;
end;
end;
Result := (NoRunReason = '');
end;
function TXbe.GetTLSData: DWord;
begin
if m_TLS.dwDataStartAddr = 0 then
Result := 0
else
Result := GetAddr(m_TLS.dwDataStartAddr);
end; // TXbe.GetTLSData
function TXbe.DetermineDumpFileName: string;
begin
Result := GetReadableTitle(@m_Certificate);
Result := TitleToNiceFilename(Result);
// Include game region (and possibly version) :
if (m_Certificate.dwVersion > 0)
or (m_Certificate.dwGameRegion > 0) then
begin
Result := Result + '-' + GameRegionToString(m_Certificate.dwGameRegion);
if (m_Certificate.dwVersion > 1) and (m_Certificate.dwVersion < 20) then
Result := Result + ' V' + IntToStr(m_Certificate.dwVersion);
end;
Result := Result + '.txt';
end;
function BetterTime(x_timeDate: uint32): string;
begin
Result := '';
DateTimeToString(Result, 'ddd mmm dd hh:mm:ss yyyy', CTimeToDateTime(x_timeDate));
end;
function TitleIDToString(TitleID: DWord): string;
var
Char1: AnsiChar;
Char2: AnsiChar;
begin
Result := '';
Char1 := AnsiChar(TitleID shr 24);
Char2 := AnsiChar(TitleID shr 16);
// 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)
if (Char1 in ['A'..'Z']) and (Char2 in ['A'..'Z']) then
Result := Format('%s%s-%.3d', [Char1, Char2, TitleID and $FFFF]);
end;
function TXbe.DumpInformation(FileName: string): Boolean;
var
FileEx: TextFile;
lIndex, lIndex2: Integer;
TmpStr: string;
StrAsciiFileName: string;
Flag: Word;
// BIndex: Byte;
QVersion: Word;
DumpToFile: Boolean;
procedure _LogEx(Text: string);
begin
if DumpToFile then
Writeln(FileEx, Text)
else
WriteLog(Text);
end;
begin
Result := True;
DumpToFile := (FileName <> '');
if DumpToFile then
begin
AssignFile({var}FileEx, FileName);
Rewrite({var}FileEx);
end;
_LogEx(DxbxFormat('XBE information generated by %s', [DumpToolString]));
_LogEx('');
_LogEx(DxbxFormat('Title identified as "%s"', [m_szAsciiTitle]));
_LogEx('');
_LogEx('Dumping XBE file header...');
_LogEx('');
_LogEx('Magic Number : XBEH');
_LogEx('Digitial Signature : <Hex Dump>');
TmpStr := '';
lIndex2 := 0;
for lIndex := 0 to 255 do
begin
TmpStr := TmpStr + IntToHex(m_Header.pbDigitalSignature[lIndex], 2);
if lIndex2 = 15 then
begin
_LogEx(' ' + TmpStr);
TmpStr := '';
lIndex2 := -1;
end;
Inc(lIndex2);
end;
_LogEx(' </Hex Dump>');
_LogEx(DxbxFormat('Base Address : 0x%.8x', [m_Header.dwBaseAddr]));
_LogEx(DxbxFormat('Size of Headers : 0x%.8x', [m_Header.dwSizeofHeaders]));
_LogEx(DxbxFormat('Size of Image : 0x%.8x', [m_Header.dwSizeofImage]));
_LogEx(DxbxFormat('Size of Image Header : 0x%.8x', [m_Header.dwSizeofImageHeader]));
_LogEx(DxbxFormat('TimeDate Stamp : 0x%.8x (%s)', [m_Header.dwTimeDate, BetterTime(m_Header.dwTimeDate)]));
_LogEx(DxbxFormat('Certificate Address : 0x%.8x', [m_Header.dwCertificateAddr]));
_LogEx(DxbxFormat('Number of Sections : 0x%.8x', [m_Header.dwSections]));
_LogEx(DxbxFormat('Section Headers Address : 0x%.8x', [m_header.dwSectionHeadersAddr]));
_LogEx(DxbxFormat('Init Flags : 0x%.8x ', [m_Header.dwInitFlags]) + XbeHeaderInitFlagsToString(m_Header.dwInitFlags));
lIndex := GetAddr(m_Header.dwDebugUnicodeFileNameAddr);
lIndex2 := 0;
TmpStr := '';
while lIndex2 < 40 do
begin
TmpStr := TmpStr + Char(RawData[lIndex]);
Inc(lIndex2);
lIndex := lIndex + 2;
if Char(RawData[lIndex]) = #0 then
break;
end;
//TmpStr := WideStringToString(AsciiFileName, 437);
StrAsciiFileName := TmpStr;
TmpStr := '';
_LogEx(DxbxFormat('Entry Point : 0x%.8x (Retail: 0x%.8x, Debug: 0x%.8x)', [m_Header.dwEntryAddr, m_Header.dwEntryAddr xor XOR_EP_Retail, m_Header.dwEntryAddr xor XOR_EP_DEBUG]));
_LogEx(DxbxFormat('TLS Address : 0x%.8x', [m_Header.dwTLSAddr]));
_LogEx(DxbxFormat('(PE) Stack Commit : 0x%.8x', [m_Header.dwPeStackCommit]));
_LogEx(DxbxFormat('(PE) Heap Reserve : 0x%.8x', [m_Header.dwPeHeapReserve]));
_LogEx(DxbxFormat('(PE) Heap Commit : 0x%.8x', [m_Header.dwPeHeapCommit]));
_LogEx(DxbxFormat('(PE) Base Address : 0x%.8x', [m_Header.dwPeBaseAddr]));
_LogEx(DxbxFormat('(PE) Size of Image : 0x%.8x', [m_Header.dwPeSizeofImage]));
_LogEx(DxbxFormat('(PE) Checksum : 0x%.8x', [m_Header.dwPeChecksum]));
_LogEx(DxbxFormat('(PE) TimeDate Stamp : 0x%.8x (%s)', [m_Header.dwPeTimeDate, BetterTime(m_Header.dwPeTimeDate)]));
_LogEx(DxbxFormat('Debug PathName Address : 0x%.8x ("%s")', [m_Header.dwDebugPathNameAddr, GetAddrStr(m_Header.dwDebugPathNameAddr)]));
_LogEx(DxbxFormat('Debug FileName Address : 0x%.8x ("%s")', [m_Header.dwDebugFileNameAddr, GetAddrStr(m_Header.dwDebugFileNameAddr)]));
_LogEx(DxbxFormat('Debug Unicode FileName Address : 0x%.8x (L"%s")', [m_Header.dwDebugUnicodeFileNameAddr, StrAsciiFileName]));
_LogEx(DxbxFormat('Kernel Image Thunk Address : 0x%.8x (Retail: 0x%.8x, Debug: 0x%.8x)', [m_Header.dwKernelImageThunkAddr, m_Header.dwKernelImageThunkAddr xor XOR_KT_RETAIL, m_Header.dwKernelImageThunkAddr xor XOR_KT_DEBUG]));
_LogEx(DxbxFormat('NonKernel Import Dir Address : 0x%.8x', [m_Header.dwNonKernelImportDirAddr]));
_LogEx(DxbxFormat('Library Versions : 0x%.8x', [m_Header.dwLibraryVersions]));
_LogEx(DxbxFormat('Library Versions Address : 0x%.8x', [m_Header.dwLibraryVersionsAddr]));
_LogEx(DxbxFormat('Kernel Library Version Address : 0x%.8x', [m_Header.dwKernelLibraryVersionAddr]));
_LogEx(DxbxFormat('XAPI Library Version Address : 0x%.8x', [m_Header.dwXAPILibraryVersionAddr]));
_LogEx(DxbxFormat('Logo Bitmap Address : 0x%.8x', [m_Header.dwLogoBitmapAddr]));
_LogEx(DxbxFormat('Logo Bitmap Size : 0x%.8x', [m_Header.dwSizeofLogoBitmap]));
_LogEx('');
_LogEx('Dumping XBE Certificate...');
_LogEx('');
_LogEx(DxbxFormat('Size of Certificate : 0x%.8x', [m_Certificate.dwSize]));
_LogEx(DxbxFormat('TimeDate Stamp : 0x%.8x (%s)', [m_Certificate.dwTimeDate, BetterTime(m_Certificate.dwTimeDate)]));
_LogEx(DxbxFormat('Title ID : 0x%.8x (%s)', [m_Certificate.dwTitleId, TitleIDToString(m_Certificate.dwTitleId)]));
_LogEx(DxbxFormat('Title : "%s"', [m_szAsciiTitle]));
// print alternate titles
_LogEx(DxbxFormat('Alternate Titles IDs : 0x%.8x (%s)', [m_Certificate.dwAlternateTitleId[0], TitleIDToString(m_Certificate.dwAlternateTitleId[0])]));
for lIndex := 1 to 15 do
_LogEx(DxbxFormat(' 0x%.8x (%s)', [m_Certificate.dwAlternateTitleId[lIndex], TitleIDToString(m_Certificate.dwAlternateTitleId[lIndex])]));
_LogEx(DxbxFormat('Allowed Media : 0x%.8x (%s)', [m_Certificate.dwAllowedMedia, AllowedMediaToString(m_Certificate.dwAllowedMedia)]));
_LogEx(DxbxFormat('Game Region : 0x%.8x (%s)', [m_Certificate.dwGameRegion, GameRegionToString(m_Certificate.dwGameRegion)]));
_LogEx(DxbxFormat('Game Ratings : 0x%.8x (%s)', [m_Certificate.dwGameRatings, GameRatingToString(m_Certificate.dwGameRatings)]));
_LogEx(DxbxFormat('Disk Number : 0x%.8x', [m_Certificate.dwDiskNumber]));
_LogEx(DxbxFormat('Version : 0x%.8x', [m_Certificate.dwVersion]));
// Print Lan Key
TmpStr := '';
for lIndex := 0 to 15 do
TmpStr := TmpStr + IntToHex(Ord(m_Certificate.bzLanKey[lIndex]), 2);
_LogEx('LAN Key : ' + TmpStr);
// print signature key
TmpStr := '';
for lIndex := 0 to 15 do
TmpStr := TmpStr + IntToHex(Ord(m_Certificate.bzSignatureKey[lIndex]), 2);
_LogEx('Signature Key : ' + TmpStr);
// print alternative signature keys
_LogEx('Title Alternative Signature Keys : <Hex Dump>');
for lIndex := 0 to 15 do
begin
TmpStr := '';
for lIndex2 := 0 to 15 do
TmpStr := TmpStr + IntToHex(Ord(m_Certificate.bzTitleAlternateSignatureKey[lIndex][lIndex2]), 2);
_LogEx(' ' + TmpStr);
end;
_LogEx(' </Hex Dump>');
// print section headers
_LogEx('');
_LogEx('Dumping XBE Section Headers...');
_LogEx('');
for lIndex := 0 to m_Header.dwSections - 1 do
begin
TmpStr := '';
for lIndex2 := 0 to XBE_SECTIONNAME_MAXLENGTH-1 do
begin
if m_szSectionName[lIndex][lIndex2] <> #0 then
TmpStr := TmpStr + Char(m_szSectionName[lIndex][lIndex2])
else
break;
end;
_LogEx(DxbxFormat('Section Name : 0x%.8x ("%s")', [m_SectionHeader[lIndex].dwSectionNameAddr, TmpStr]));
TmpStr := '';
TmpStr := DxbxFormat('Flags : 0x%.2x%.2x%.2x%.2x', [m_SectionHeader[lIndex].dwFlags[3], m_SectionHeader[lIndex].dwFlags[2], m_SectionHeader[lIndex].dwFlags[1], m_SectionHeader[lIndex].dwFlags[0]]);
Flag := m_SectionHeader[lIndex].dwFlags[0];
TmpStr := TmpStr + ' '; // Insert open space
if (Flag and XBE_SECTIONHEADER_FLAG_Writable) > 0 then
TmpStr := TmpStr + '(Writable) ';
if (Flag and XBE_SECTIONHEADER_FLAG_Preload) > 0 then
TmpStr := TmpStr + '(Preload) ';
if (Flag and XBE_SECTIONHEADER_FLAG_Executable) > 0 then
TmpStr := TmpStr + '(Executable) ';
if (Flag and XBE_SECTIONHEADER_FLAG_InsertedFile) > 0 then
TmpStr := TmpStr + '(Inserted File) ';
if (Flag and XBE_SECTIONHEADER_FLAG_HeadPageRO) > 0 then
TmpStr := TmpStr + '(Head Page RO) ';
if (Flag and XBE_SECTIONHEADER_FLAG_TailPageRO) > 0 then
TmpStr := TmpStr + '(Tail Page RO) ';
_LogEx(TmpStr);
_LogEx(DxbxFormat('Virtual Address : 0x%.8x', [m_SectionHeader[lIndex].dwVirtualAddr]));
_LogEx(DxbxFormat('Virtual Size : 0x%.8x', [m_SectionHeader[lIndex].dwVirtualSize]));
_LogEx(DxbxFormat('Raw Address : 0x%.8x', [m_SectionHeader[lIndex].dwRawAddr]));
_LogEx(DxbxFormat('Size of Raw : 0x%.8x', [m_SectionHeader[lIndex].dwSizeofRaw]));
_LogEx(DxbxFormat('Section Name Address : 0x%.8x', [m_SectionHeader[lIndex].dwSectionNameAddr]));
_LogEx(DxbxFormat('Section Reference Count : 0x%.8x', [m_SectionHeader[lIndex].dwSectionRefCount]));
_LogEx(DxbxFormat('Head Shared Reference Count Addr : 0x%.8x', [m_SectionHeader[lIndex].dwHeadSharedRefCountAddr]));
_LogEx(DxbxFormat('Tail Shared Reference Count Addr : 0x%.8x', [m_SectionHeader[lIndex].dwTailSharedRefCountAddr]));
TmpStr := '';
for lIndex2 := 0 to 19 do
TmpStr := TmpStr + IntToHex(Ord(m_SectionHeader[lIndex].bzSectionDigest[lIndex2]), 2);
_LogEx('Section Digest : ' + TmpStr);
_LogEx('');
end;
// print library versions
_LogEx('Dumping XBE Library Versions...');
_LogEx('');
if (SizeOf(m_LibraryVersion) = 0) or (m_Header.dwLibraryVersions = 0) then
begin
_LogEx('(This XBE contains no Library Versions)');
_LogEx('');
end
else
begin
for lIndex := 0 to m_Header.dwLibraryVersions - 1 do
begin
TmpStr := '';
for lIndex2 := 0 to 7 do
begin
if m_LibraryVersion[lIndex].szName[lIndex2] <> #0 then
TmpStr := TmpStr + Char(m_LibraryVersion[lIndex].szName[lIndex2]);
end;
_LogEx('Library Name : ' + TmpStr);
_LogEx(DxbxFormat('Version : %d.%d.%d', [m_LibraryVersion[lIndex].wMajorVersion, m_LibraryVersion[lIndex].wMinorVersion, m_LibraryVersion[lIndex].wBuildVersion]));
//Some bit maths the QVersion Flag is only 13 bits long so i convert the 13 bits to a number
QVersion := m_LibraryVersion[lIndex].wFlags and ((1 shl 13) - 1);
Flag := m_LibraryVersion[lIndex].wFlags;
TmpStr := DxbxFormat('Flags : QFEVersion : 0x%.4x, ', [QVersion]);
if (Flag and XBE_LIBRARYVERSION_FLAG_DebugBuild) > 0 then
TmpStr := TmpStr + 'Debug, '
else
TmpStr := TmpStr + 'Retail, ';
if (Flag and XBE_LIBRARYVERSION_FLAG_ApprovedYes) > 0 then
TmpStr := TmpStr + 'Approved'
else
if (Flag and XBE_LIBRARYVERSION_FLAG_ApprovedPossibly) > 0 then
TmpStr := TmpStr + 'Possibly Approved'
else
TmpStr := TmpStr + 'Unapproved';
_LogEx(TmpStr);
_LogEx('');
end; // for lIndex
end;
_LogEx('Dumping XBE TLS...');
_LogEx('');
// print thread local storage
if Assigned(m_TLS) then
begin
_LogEx(DxbxFormat('Data Start Address : 0x%.8x', [m_TLS.dwDataStartAddr]));
_LogEx(DxbxFormat('Data End Address : 0x%.8x', [m_TLS.dwDataEndAddr]));
_LogEx(DxbxFormat('TLS Index Address : 0x%.8x', [m_TLS.dwTLSIndexAddr]));
_LogEx(DxbxFormat('TLS Callback Address : 0x%.8x', [m_TLS.dwTLSCallbackAddr]));
_LogEx(DxbxFormat('Size of Zero Fill : 0x%.8x', [m_TLS.dwSizeofZeroFill]));
_LogEx(DxbxFormat('Characteristics : 0x%.8x', [m_TLS.dwCharacteristics]));
end
else
_LogEx('(This XBE contains no TLS)');
if DumpToFile then
CloseFile(FileEx);
end; // TXbe.DumpInformation
// TODO -oDXBX: Return real adresses like Cxbx
function TXbe.GetAddr(x_dwVirtualAddress: DWord): Integer;
var
lIndex, VirtAddr, VirtSize, dwoffs: DWord;
begin
dwoffs := x_dwVirtualAddress - m_Header.dwBaseAddr;
Result := 0;
// offset in image header
if dwoffs < SizeOf(m_Header) then
Result := dwOffs
else
begin
// offset in image header extra bytes
if dwoffs < m_Header.dwSizeofHeaders then
Result := dwOffs// - SizeOf(m_Header) // TODO -oDXBX: Return adresses in m_HeaderEx
else
begin
// offset into some random section
for lIndex := 0 to m_Header.dwSections - 1 do
begin
VirtAddr := m_SectionHeader[lIndex].dwVirtualAddr;
VirtSize := m_SectionHeader[lIndex].dwVirtualSize;
if (x_dwVirtualAddress >= VirtAddr) and (x_dwVirtualAddress < (VirtAddr + VirtSize)) then
begin
Result := m_SectionHeader[lIndex].dwRawAddr + (x_dwVirtualAddress - VirtAddr);
Exit;
end;
end;
end;
end;
end;
function TXbe.GetAddrStr(x_dwVirtualAddress: DWord; const aMaxLen: Integer = MaxInt): string;
var
lIndex: Integer;
begin
lIndex := GetAddr(x_dwVirtualAddress);
Result := '';
try
Result := string(PCharToString(PAnsiChar(@RawData[lIndex]), aMaxLen));
except
// ignore - probably out of bounds read
end;
end;
function TXbe.GetAddrWStr(x_dwVirtualAddress: DWord; const aMaxLen: Integer = MaxInt): WideString;
var
lIndex: Integer;
begin
lIndex := GetAddr(x_dwVirtualAddress);
Result := '';
try
Result := PWideCharToString(@RawData[lIndex], aMaxLen);
except
// ignore - probably out of bounds read
end;
end;
function TXbe.GetFileSize: Int64;
begin
Result := MyFile.Size;
end;
// TXbe.GetAddr
function TXbe.FindSection(const aSectionName: string; out Size: Integer): TRawSection;
var
i: Integer;
begin
for i := 0 to m_Header.dwSections - 1 do
begin
if SameText(aSectionName, string(StrLPas(PAnsiChar(m_szSectionName[i]), XBE_SECTIONNAME_MAXLENGTH))) then
begin
Result := m_bzSection[i];
{out}Size := m_SectionHeader[i].dwSizeofRaw;
Exit;
end;
end;
Result := nil;
end;
(*
// import logo bitmap from raw monochrome data (100x17 8bit grayscale)
procedure TXbe.ImportLogoBitmap(const uint08 x_Gray[100*17])
var
LogoBuffer: PAnsiChar;
LogoSize: uint32;
begin
char *LogoBuffer = new char[4*1024];
LogoSize := 0;
// encode logo bitmap
begin
for(uint32 v=1;v<100*17;LogoSize++)
begin
char color = x_Gray[v] shr 4;
uint32 len = 1;
while(++v<100*17-1) and (len < 1024) and (color = (x_Gray[v] shr 4)) do
Inc(len);
LogoRLE *cur = (LogoRLE * )@LogoBuffer[LogoSize];
if (len <= 7) then
begin
cur.m_Eight.bType1 := 1;
cur.m_Eight.Len := len;
cur.m_Eight.Data := color;
end;
else
begin
cur.m_Sixteen.bType1 := 0;
cur.m_Sixteen.bType2 := 1;
cur.m_Sixteen.Len := len;
cur.m_Sixteen.Data := color;
Inc(LogoSize);
end;
end;
end;
// check if there is room to save this, if not then throw an error
begin
uint08 *RLE = GetLogoBitmap(LogoSize);
if (RLE = 0) then
begin
if (GetError() = 0) then
SetError('Logo bitmap could not be imported (not enough space in file?)', False);
Exit;
end;
memcpy(RLE, LogoBuffer, LogoSize);
end;
end;
*)
// export raw monochrome data (100x17 8bit grayscale) to logo bitmap
function TXbe.ExportLogoBitmap(aBitmap: TBitmap): Boolean;
const
XBE_LOGO_WIDTH = 100;
XBE_LOGO_HEIGHT = 17;
var
x_Gray: Byte;
dwLength, x, y, lIndex, lIndex2, Len, Data: DWord;
RLE: DWord;
pos0, pos1: Byte;
begin
Result := False;
dwLength := m_Header.dwSizeofLogoBitmap;
if (dwLength = 0) or (m_Header.dwLogoBitmapAddr = 0) then
Exit;
RLE := GetAddr(m_Header.dwLogoBitmapAddr);
if RLE = 0 then
Exit;
try
aBitmap.PixelFormat := pf32bit;
aBitmap.SetSize(XBE_LOGO_WIDTH, XBE_LOGO_HEIGHT);
Len := 0;
Data := 0;
x := 0;
y := 0;
lIndex := 0;
while lIndex < dwLength do
begin
// Read 2 bytes.
Pos0 := Ord(RawData[RLE + lIndex]);
Pos1 := Ord(RawData[RLE + 1 + lIndex]);
if (Pos0 and 1) > 0 then // Check if the bit 0 is set.
begin
Len := Pos0 shr 1 and 7; // Select the bits from 1 to 3
Data := Pos0 shr 4 and 15; // Select the bits from 4 to 7
end
else
begin
if (Pos0 and 2) > 0 then // Check if the bit 1 is set.
begin
Len := (Pos0 shr 2 and 63) + (Pos1 and 15) * 256; // Select the bits from 2 to 7 from the first byte (Pos0) and the bits from 0 to 3 from the second byte (Pos1) and form a number.
Data := Pos1 shr 4 and 15; // Select the bits from 4 to 7 from the second byte (Pos1)
Inc(lIndex); // The index is incremented because 2 bytes were read.
end;
end;
lIndex2 := 0; while lIndex2 < Len do
begin
x_Gray := Byte(Data shl 4);
aBitmap.Canvas.Pixels[x, y] := RGB(x_Gray, x_Gray, x_Gray);
Inc(x);
if x = XBE_LOGO_WIDTH then
begin
x := 0;
Inc(y);
if y = XBE_LOGO_HEIGHT then
begin
lIndex := dwLength;
break;
end;
end;
Inc(lIndex2);
end;
Inc(lIndex); // Index increment
end;
Result := True;
except
Result := False;
end;
end; // TXbe.ExportLogoBitmap
// Reads the $$XTIMAGE Section, decodes the data and returns the bitmap;
function TXbe.ExportIconBitmap(aBitmap: TBitmap): Boolean;
var
Section: TRawSection;
SectionSize: Integer;
begin
Result := False;
// Find icon section :
Section := FindSection(XBE_SECTIONNAME_GAMEICON, {out}SectionSize);
if Section = nil then
begin
Section := FindSection(XBE_SECTIONNAME_SAVEICON, {out}SectionSize);
if Section = nil then
Exit;
end;
Result := ExportXPRToBitmap(PXPR_IMAGE(Section), aBitmap);
end;
function TXbe.ExportXPRToBitmap(XprImage: PXPR_IMAGE; aBitmap: TBitmap): Boolean;
var
Width, Height: Cardinal;
Scanlines16: RGB16Scanlines;
Scanlines32: RGB32Scanlines;
begin
Result := False;
// Check if it's an XPR (Xbox Packed Resources) :
if (XprImage.hdr.Header.dwMagic = XPR0_MAGIC_VALUE)
or (XprImage.hdr.Header.dwMagic = XPR1_MAGIC_VALUE) then
begin
// Determine image dimensions :
Width := 1 shl ((XprImage.hdr.Texture.Format and X_D3DFORMAT_USIZE_MASK) shr X_D3DFORMAT_USIZE_SHIFT);
Height := 1 shl ((XprImage.hdr.Texture.Format and X_D3DFORMAT_VSIZE_MASK) shr X_D3DFORMAT_VSIZE_SHIFT);
// Prepare easy access to the bitmap data :
aBitmap.SetSize(Width, Height);
if (XprImage.hdr.Header.dwTotalSize - XprImage.hdr.Header.dwHeaderSize)/Width/Height = 2 then
begin // 16 bit per pixel textures
aBitmap.PixelFormat := pf16bit;
Scanlines16.Initialize(aBitmap);
// Read the texture into the 16bit bitmap :
Result := ReadD3D16bitTextureFormatIntoBitmap(
{Format=}(XprImage.hdr.Texture.Format and X_D3DFORMAT_FORMAT_MASK) shr X_D3DFORMAT_FORMAT_SHIFT,
{Data=}PBytes(@(XprImage.pBits[0])),
{DataSize=}XprImage.hdr.Header.dwTotalSize - XprImage.hdr.Header.dwHeaderSize,
{Output=}@Scanlines16);
end
else
begin // 32 bit per pixel textures
aBitmap.PixelFormat := pf32bit;
Scanlines32.Initialize(aBitmap);
// Read the texture into the 32bit bitmap :
Result := ReadD3DTextureFormatIntoBitmap(
{Format=}(XprImage.hdr.Texture.Format and X_D3DFORMAT_FORMAT_MASK) shr X_D3DFORMAT_FORMAT_SHIFT,
{Data=}PBytes(@(XprImage.pBits[0])),
{DataSize=}XprImage.hdr.Header.dwTotalSize - XprImage.hdr.Header.dwHeaderSize,
{Output=}@Scanlines32);
end;
Exit;
end;
// TODO -oDXBX: Check for 'DDS ' format, and read that too, perhaps using these resources :
// http://www.imageconverterplus.com/help-center/about-icp/supported-formats/dds/
// http://archive.netbsd.se/view_attachment.php?id=2463254.32112
// if StrLPas(PAnsiChar(@XprImage.hdr.Header.dwMagic), 3) = 'DDS' then
// offset := 124;
end;
end.