diff --git a/360/file_browser.c b/360/file_browser.c
index 3254db7e05..716a9c4c47 100644
--- a/360/file_browser.c
+++ b/360/file_browser.c
@@ -32,7 +32,7 @@ static const char * filebrowser_get_extension(const char * filename)
void filebrowser_parse_directory(filebrowser_t * filebrowser, const char * path, const char *extensions)
{
- BOOL error = FALSE;
+ int error = FALSE;
filebrowser->file_count = 0;
WIN32_FIND_DATA ffd;
diff --git a/360/main.c b/360/main.c
index c5c9544a94..d0e393bd62 100644
--- a/360/main.c
+++ b/360/main.c
@@ -235,7 +235,7 @@ static void get_environment_settings (void)
Mounted[DEVICE_MEMORY_ONBOARD] = Mount(DEVICE_MEMORY_ONBOARD,"OnBoardMU:");
Mounted[DEVICE_CDROM0] = Mount(DEVICE_CDROM0,"Dvd:");
- BOOL result_filecache = XSetFileCacheSize(0x100000);
+ int result_filecache = XSetFileCacheSize(0x100000);
if(result_filecache != TRUE)
{
diff --git a/360/media/Arial_12.abc b/360/media/Arial_12.abc
new file mode 100644
index 0000000000..f24f2f135a
Binary files /dev/null and b/360/media/Arial_12.abc differ
diff --git a/360/media/Arial_12.rdf b/360/media/Arial_12.rdf
new file mode 100644
index 0000000000..8321cbd73a
--- /dev/null
+++ b/360/media/Arial_12.rdf
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/360/media/Arial_12.tga b/360/media/Arial_12.tga
new file mode 100644
index 0000000000..e9061f54b0
Binary files /dev/null and b/360/media/Arial_12.tga differ
diff --git a/360/menu.cpp b/360/menu.cpp
index 73322d9268..8df3905298 100644
--- a/360/menu.cpp
+++ b/360/menu.cpp
@@ -55,7 +55,7 @@ static void filebrowser_fetch_directory_entries(const char *path, CXuiList * rom
{
filebrowser_parse_directory(&browser, path, ssnes_console_get_rom_ext());
- DWORD dwNum_rompath = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
+ unsigned long dwNum_rompath = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
wchar_t * rompath_name = new wchar_t[dwNum_rompath];
MultiByteToWideChar(CP_ACP, 0, path, -1, rompath_name, dwNum_rompath);
rompath_title->SetText(rompath_name);
@@ -64,7 +64,7 @@ static void filebrowser_fetch_directory_entries(const char *path, CXuiList * rom
romlist->InsertItems(0, browser.file_count);
for(unsigned i = 0; i < browser.file_count; i++)
{
- DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, browser.cur[i].d_name, -1, NULL, 0);
+ unsigned long dwNum = MultiByteToWideChar(CP_ACP, 0, browser.cur[i].d_name, -1, NULL, 0);
wchar_t * entry_name = new wchar_t[dwNum];
MultiByteToWideChar(CP_ACP, 0, browser.cur[i].d_name, -1, entry_name, dwNum);
romlist->SetText(i, entry_name);
@@ -124,8 +124,8 @@ HRESULT CSSNESMain::OnInit(XUIMessageInit * pInitData, BOOL& bHandled)
const char * core_text = snes_library_id();
char package_version[32];
sprintf(package_version, "SSNES %s", PACKAGE_VERSION);
- DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, core_text, -1, NULL, 0);
- DWORD dwNum_package = MultiByteToWideChar(CP_ACP, 0, package_version, -1, NULL, 0);
+ unsigned long dwNum = MultiByteToWideChar(CP_ACP, 0, core_text, -1, NULL, 0);
+ unsigned long dwNum_package = MultiByteToWideChar(CP_ACP, 0, package_version, -1, NULL, 0);
wchar_t * core_text_utf = new wchar_t[dwNum];
wchar_t * package_version_utf = new wchar_t[dwNum_package];
MultiByteToWideChar(CP_ACP, 0, core_text, -1, core_text_utf, dwNum);
@@ -232,9 +232,7 @@ HRESULT CSSNESMain::OnNotifyPress( HXUIOBJ hObjPressed, BOOL& bHandled )
hr = XuiSceneCreate(L"file://game:/media/", L"ssnes_settings.xur", NULL, &app.hSSNESSettings);
if (FAILED(hr))
- {
SSNES_ERR("Failed to load scene.\n");
- }
NavigateForward(app.hSSNESSettings);
}
diff --git a/360/xdk360_input.cpp b/360/xdk360_input.cpp
index 3698fce409..1a9d9b3e9f 100644
--- a/360/xdk360_input.cpp
+++ b/360/xdk360_input.cpp
@@ -27,7 +27,7 @@
static XINPUT_STATE state[4];
-const int DEADZONE = 16000;
+#define DEADZONE (16000)
static void xdk360_input_poll(void *data)
{
diff --git a/360/xdk360_video.cpp b/360/xdk360_video.cpp
index 7c38704e28..ecac2019db 100644
--- a/360/xdk360_video.cpp
+++ b/360/xdk360_video.cpp
@@ -68,8 +68,10 @@ typedef struct DrawVerticeFormats
} DrawVerticeFormats;
static bool g_quitting;
+static bool g_first_msg;
unsigned g_frame_count;
void *g_d3d;
+Console g_screen_console;
static void xdk360_gfx_free(void * data)
{
@@ -273,6 +275,18 @@ static bool xdk360_gfx_frame(void *data, const void *frame,
vid->xdk360_render_device->SetStreamSource(0, vid->vertex_buf, 0, sizeof(DrawVerticeFormats));
vid->xdk360_render_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+ if (msg)
+ {
+ if(IS_TIMER_EXPIRED() || g_first_msg)
+ {
+ g_screen_console.Format(true, msg);
+ g_first_msg = 0;
+ SET_TIMER_EXPIRATION(60);
+ }
+
+ g_screen_console.Render();
+ }
+
vid->xdk360_render_device->Present(NULL, NULL, NULL, NULL);
return true;
@@ -311,6 +325,15 @@ void xdk360_video_init(void)
video_info.input_scale = 2;
g_d3d = xdk360_gfx_init(&video_info, NULL, NULL);
+
+ g_first_msg = true;
+
+ HRESULT hr = g_screen_console.Create("game:\\media\\Arial_12.xpr",
+ 0xff000000, 0xffffffff );
+ if(FAILED(hr))
+ {
+ SSNES_ERR("Couldn't create debug console.\n");
+ }
}
void xdk360_video_deinit(void)
diff --git a/360/xdk360_video.h b/360/xdk360_video.h
index 76d010ca18..c1ca81304a 100644
--- a/360/xdk360_video.h
+++ b/360/xdk360_video.h
@@ -19,25 +19,42 @@
#ifndef _XDK360_VIDEO_H
#define _XDK360_VIDEO_H
+#include "xdk360_video_console.h"
+
+typedef struct {
+ float x;
+ float y;
+ float z;
+ float rhw;
+ float u;
+ float v;
+} primitive_t;
+
+#define DFONT_MAX 4096
+#define PRIM_FVF (D3DFVF_XYZRHW | D3DFVF_TEX1)
+
typedef struct xdk360_video
{
+ unsigned last_width, last_height;
IDirect3D9* xdk360_device;
IDirect3DDevice9* xdk360_render_device;
IDirect3DVertexShader9 *pVertexShader;
IDirect3DPixelShader9* pPixelShader;
IDirect3DVertexDeclaration9* pVertexDecl;
IDirect3DVertexBuffer9* vertex_buf;
+ IDirect3DTexture9* font_texture;
IDirect3DTexture9* lpTexture;
- unsigned last_width, last_height;
D3DPRESENT_PARAMETERS d3dpp;
} xdk360_video_t;
-#define IS_TIMER_EXPIRED() (!(g_frame_count < g_console.timer_expiration_frame_count))
+#define IS_TIMER_NOT_EXPIRED() (g_frame_count < g_console.timer_expiration_frame_count)
+#define IS_TIMER_EXPIRED() (!(IS_TIMER_NOT_EXPIRED()))
#define SET_TIMER_EXPIRATION(value) g_console.timer_expiration_frame_count = g_frame_count + value;
void xdk360_video_init(void);
void xdk360_video_deinit(void);
+extern Console g_screen_console;
extern unsigned g_frame_count;
extern void *g_d3d;
diff --git a/360/xdk360_video_console.cpp b/360/xdk360_video_console.cpp
new file mode 100644
index 0000000000..a872f693c5
--- /dev/null
+++ b/360/xdk360_video_console.cpp
@@ -0,0 +1,273 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2012 - Daniel De Matteis
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES 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 Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 SSNES.
+ * If not, see .
+ */
+
+#include
+#include
+#include "xdk360_video.h"
+#include "xdk360_video_console.h"
+#include "xdk360_video_debugfonts.h"
+#include "../general.h"
+
+Console::Console()
+{
+ first_message = true;
+ m_Buffer = NULL;
+ m_Lines = NULL;
+ m_nScrollOffset = 0;
+}
+
+Console::~Console()
+{
+ Destroy();
+}
+
+HRESULT Console::Create( LPCSTR strFontFileName, D3DCOLOR colBackColor,
+ D3DCOLOR colTextColor, unsigned int nLines )
+{
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *m_pd3dDevice = vid->xdk360_render_device;
+
+ // Calculate the safe area
+ unsigned int uiSafeAreaPct = SAFE_AREA_PCT_HDTV;
+
+ m_cxSafeArea = ( vid->d3dpp.BackBufferWidth * uiSafeAreaPct ) / 100;
+ m_cySafeArea = ( vid->d3dpp.BackBufferHeight * uiSafeAreaPct ) / 100;
+
+ m_cxSafeAreaOffset = ( vid->d3dpp.BackBufferWidth - m_cxSafeArea ) / 2;
+ m_cySafeAreaOffset = ( vid->d3dpp.BackBufferHeight - m_cySafeArea ) / 2;
+
+ // Create the font
+ HRESULT hr = m_Font.Create( strFontFileName );
+ if( FAILED( hr ) )
+ {
+ SSNES_ERR( "Could not create font.\n" );
+ return -1;
+ }
+
+ // Save the colors
+ m_colBackColor = colBackColor;
+ m_colTextColor = colTextColor;
+
+ // Calculate the number of lines on the screen
+ float fCharWidth, fCharHeight;
+ m_Font.GetTextExtent( L"i", &fCharWidth, &fCharHeight, FALSE );
+
+ m_cScreenHeight = (unsigned int)( m_cySafeArea / fCharHeight );
+ m_cScreenWidth = (unsigned int)( m_cxSafeArea / fCharWidth );
+
+ m_cScreenHeightVirtual = max( m_cScreenHeight, nLines );
+
+ m_fLineHeight = fCharHeight;
+
+ // Allocate memory to hold the lines
+ m_Buffer = new wchar_t[ m_cScreenHeightVirtual * ( m_cScreenWidth + 1 ) ];
+ m_Lines = new wchar_t *[ m_cScreenHeightVirtual ];
+
+ // Set the line pointers as indexes into the buffer
+ for( unsigned int i = 0; i < m_cScreenHeightVirtual; i++ )
+ m_Lines[ i ] = m_Buffer + ( m_cScreenWidth + 1 ) * i;
+
+ CLEAR_SCREEN();
+
+ return hr;
+}
+
+//--------------------------------------------------------------------------------------
+// Name: Destroy()
+// Desc: Tear everything down
+//--------------------------------------------------------------------------------------
+void Console::Destroy()
+{
+ // Delete the memory we've allocated
+ if( m_Lines )
+ {
+ delete[] m_Lines;
+ m_Lines = NULL;
+ }
+
+ if( m_Buffer )
+ {
+ delete[] m_Buffer;
+ m_Buffer = NULL;
+ }
+
+ // Destroy the font
+ m_Font.Destroy();
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: Render()
+// Desc: Render the console to the screen
+//--------------------------------------------------------------------------------------
+void Console::Render()
+{
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *m_pd3dDevice = vid->xdk360_render_device;
+
+ // The top line
+ unsigned int nTextLine = ( m_nCurLine - m_cScreenHeight + m_cScreenHeightVirtual - m_nScrollOffset + 1 )
+ % m_cScreenHeightVirtual;
+
+ m_Font.Begin();
+
+ for( unsigned int nScreenLine = 0; nScreenLine < m_cScreenHeight; nScreenLine++ )
+ {
+ m_Font.DrawText( (float)( m_cxSafeAreaOffset ),
+ (float)( m_cySafeAreaOffset + m_fLineHeight * nScreenLine ),
+ m_colTextColor, m_Lines[nTextLine] );
+
+ nTextLine = ( nTextLine + 1 ) % m_cScreenHeightVirtual;
+ }
+
+ m_Font.End();
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: Add( CHAR )
+// Desc: Convert ANSI to WCHAR and add to the current line
+//--------------------------------------------------------------------------------------
+void Console::Add( char ch )
+{
+ wchar_t wch;
+
+ int ret = MultiByteToWideChar( CP_ACP, // ANSI code page
+ 0, // No flags
+ &ch, // Character to convert
+ 1, // Convert one byte
+ &wch, // Target wide character buffer
+ 1 ); // One wide character
+
+ Add( wch );
+}
+
+
+
+//--------------------------------------------------------------------------------------
+// Name: Add( WCHAR )
+// Desc: Add a wide character to the current line
+//--------------------------------------------------------------------------------------
+void Console::Add( wchar_t wch )
+{
+ // If this is a newline, just increment lines and move on
+ if( wch == L'\n' )
+ {
+ INCREMENT_LINE();
+ return;
+ }
+
+ int bIncrementLine = FALSE; // Whether to wrap to the next line
+
+ if( m_cCurLineLength == m_cScreenWidth )
+ bIncrementLine = TRUE;
+ else
+ {
+ // Try to append the character to the line
+ m_Lines[ m_nCurLine ][ m_cCurLineLength ] = wch;
+
+ if( m_Font.GetTextWidth( m_Lines[ m_nCurLine ] ) > m_cxSafeArea )
+ {
+ // The line is too long, we need to wrap the character to the next line
+ m_Lines[ m_nCurLine][ m_cCurLineLength ] = L'\0';
+ bIncrementLine = TRUE;
+ }
+ }
+
+ // If we need to skip to the next line, do so
+ if( bIncrementLine )
+ {
+ INCREMENT_LINE();
+ m_Lines[ m_nCurLine ][0] = wch;
+ }
+
+ if(IS_TIMER_EXPIRED())
+ m_cCurLineLength++;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: Format()
+// Desc: Output a variable argument list using a format string
+//--------------------------------------------------------------------------------------
+void Console::Format(int clear_screen, _In_z_ _Printf_format_string_ LPCSTR strFormat, ... )
+{
+ if(clear_screen)
+ {
+ CLEAR_SCREEN();
+ }
+
+ va_list pArgList;
+ va_start( pArgList, strFormat );
+ FormatV( strFormat, pArgList );
+ va_end( pArgList );
+
+ // Render the output
+ Render();
+}
+
+void Console::Format(int clear_screen, _In_z_ _Printf_format_string_ LPCWSTR wstrFormat, ... )
+{
+ if(clear_screen)
+ {
+ CLEAR_SCREEN();
+ }
+
+ va_list pArgList;
+ va_start( pArgList, wstrFormat );
+ FormatV( wstrFormat, pArgList );
+ va_end( pArgList );
+
+ // Render the output
+ Render();
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: FormatV()
+// Desc: Output a va_list using a format string
+//--------------------------------------------------------------------------------------
+void Console::FormatV( _In_z_ _Printf_format_string_ LPCSTR strFormat, va_list pArgList )
+{
+ // Count the required length of the string
+ unsigned long dwStrLen = _vscprintf( strFormat, pArgList ) + 1; // +1 = null terminator
+ char * strMessage = ( char * )_malloca( dwStrLen );
+ vsprintf_s( strMessage, dwStrLen, strFormat, pArgList );
+
+ // Output the string to the console
+ unsigned long uStringLength = strlen( strMessage );
+ for( unsigned long i = 0; i < uStringLength; i++ )
+ Add( strMessage[i] );
+
+ _freea( strMessage );
+}
+
+void Console::FormatV( _In_z_ _Printf_format_string_ LPCWSTR wstrFormat, va_list pArgList )
+{
+ // Count the required length of the string
+ unsigned long dwStrLen = _vscwprintf( wstrFormat, pArgList ) + 1; // +1 = null terminator
+ wchar_t * strMessage = ( wchar_t * )_malloca( dwStrLen * sizeof( wchar_t ) );
+ vswprintf_s( strMessage, dwStrLen, wstrFormat, pArgList );
+
+ // Output the string to the console
+ unsigned long uStringLength = wcslen( strMessage );
+ for( unsigned long i = 0; i < uStringLength; i++ )
+ Add( strMessage[i] );
+
+ _freea( strMessage );
+}
diff --git a/360/xdk360_video_console.h b/360/xdk360_video_console.h
new file mode 100644
index 0000000000..e0a6c0bb08
--- /dev/null
+++ b/360/xdk360_video_console.h
@@ -0,0 +1,105 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2012 - Daniel De Matteis
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES 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 Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 SSNES.
+ * If not, see .
+ */
+
+#pragma once
+#ifndef SSNES360_CONSOLE_H
+#define SSNES360_CONSOLE_H
+
+#include
+#include "xdk360_video_debugfonts.h"
+
+#define PAGE_UP (255)
+#define PAGE_DOWN (-255)
+
+#define SCREEN_SIZE_X_DEFAULT 640
+#define SCREEN_SIZE_Y_DEFAULT 480
+
+#define SAFE_AREA_PCT_4x3 85
+#define SAFE_AREA_PCT_HDTV 90
+
+#define INCREMENT_LINE() \
+ m_nCurLine = ( m_nCurLine + 1 ) % m_cScreenHeightVirtual; \
+ m_cCurLineLength = 0; \
+ memset( m_Lines[m_nCurLine], 0, ( m_cScreenWidth + 1 ) * sizeof( wchar_t ) );
+
+#define CLEAR_SCREEN() \
+ m_nCurLine = 0; \
+ m_cCurLineLength = 0; \
+ memset( m_Buffer, 0, m_cScreenHeightVirtual * ( m_cScreenWidth + 1 ) * sizeof( wchar_t ) ); \
+ Render();
+
+//--------------------------------------------------------------------------------------
+// Name: class Console
+// Desc: Class to implement the console.
+//--------------------------------------------------------------------------------------
+class Console
+{
+public:
+ Console();
+ ~Console();
+
+ // Initialization
+ HRESULT Create( LPCSTR strFontFileName,
+ D3DCOLOR colBackColor,
+ D3DCOLOR colTextColor,
+ unsigned int nLines = 0 );
+
+ void Destroy();
+
+ // Console output
+ void Format(int clear_screen, _In_z_ _Printf_format_string_ LPCSTR strFormat, ... );
+ void Format(int clear_screen, _In_z_ _Printf_format_string_ LPCWSTR wstrFormat, ... );
+ void FormatV( _In_z_ _Printf_format_string_ LPCSTR strFormat, va_list pArgList );
+ void FormatV( _In_z_ _Printf_format_string_ LPCWSTR wstrFormat, va_list pArgList );
+
+ // method for rendering the console
+ void Render();
+private:
+ int first_message;
+ // Safe area dimensions
+ unsigned int m_cxSafeArea;
+ unsigned int m_cySafeArea;
+
+ unsigned int m_cxSafeAreaOffset;
+ unsigned int m_cySafeAreaOffset;
+
+ // Font for rendering text
+ XdkFont m_Font;
+
+ // Colors
+ D3DCOLOR m_colBackColor;
+ D3DCOLOR m_colTextColor;
+
+ // Text Buffers
+ unsigned int m_cScreenHeight; // height in lines of screen area
+ unsigned int m_cScreenHeightVirtual; // height in lines of text storage buffer
+ unsigned int m_cScreenWidth; // width in characters
+ float m_fLineHeight; // height of a single line in pixels
+
+ wchar_t * m_Buffer; // buffer big enough to hold a full screen
+ wchar_t ** m_Lines; // pointers to individual lines
+ unsigned int m_nCurLine; // index of current line being written to
+ unsigned int m_cCurLineLength; // length of the current line
+ int m_nScrollOffset; // offset to display text (in lines)
+
+ // Add a character to the current line
+ void Add( char ch );
+ void Add( wchar_t wch );
+};
+
+#endif
diff --git a/360/xdk360_video_debugfonts.cpp b/360/xdk360_video_debugfonts.cpp
new file mode 100644
index 0000000000..0e370437d8
--- /dev/null
+++ b/360/xdk360_video_debugfonts.cpp
@@ -0,0 +1,964 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2012 - Daniel De Matteis
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES 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 Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 SSNES.
+ * If not, see .
+ */
+
+#include
+#include
+#include "xdk360_video.h"
+#include "xdk360_video_debugfonts.h"
+#include "../general.h"
+
+#define CALCFONTFILEHEADERSIZE(x) ( sizeof(unsigned long) + (sizeof(float)* 4) + sizeof(unsigned short) + (sizeof(wchar_t)*(x)) )
+#define FONTFILEVERSION 5
+
+typedef struct FontFileHeaderImage_t {
+ unsigned long m_dwFileVersion; // Version of the font file (Must match FONTFILEVERSION)
+ float m_fFontHeight; // Height of the font strike in pixels
+ float m_fFontTopPadding; // Padding above the strike zone
+ float m_fFontBottomPadding; // Padding below the strike zone
+ float m_fFontYAdvance; // Number of pixels to move the cursor for a line feed
+ unsigned short m_cMaxGlyph; // Number of font characters (Should be an odd number to maintain DWORD Alignment)
+ wchar_t m_TranslatorTable[1]; // ASCII to Glyph lookup table, NOTE: It's m_cMaxGlyph+1 in size.
+ // Entry 0 maps to the "Unknown" glyph.
+} FontFileHeaderImage_t;
+
+// Font strike array. Immediately follows the FontFileHeaderImage_t
+// structure image
+
+typedef struct FontFileStrikesImage_t {
+ unsigned long m_dwNumGlyphs; // Size of font strike array (First entry is the unknown glyph)
+ GLYPH_ATTR m_Glyphs[1]; // Array of font strike uv's etc... NOTE: It's m_dwNumGlyphs in size
+} FontFileStrikesImage_t;
+
+//--------------------------------------------------------------------------------------
+// Vertex and pixel shaders for font rendering
+// Please note the removal of comment or dead lines...
+// They are commented out because the shader compiler has no use for them.
+//--------------------------------------------------------------------------------------
+static const char g_strFontShader[] =
+ "struct VS_IN\n"
+ "{\n"
+ "float2 Pos : POSITION;\n"
+ "float2 Tex : TEXCOORD0;\n"
+ "float4 ChannelSelector : TEXCOORD1;\n"
+ "};\n"
+// "\n"
+ "struct VS_OUT\n"
+ "{\n"
+ "float4 Position : POSITION;\n"
+ "float4 Diffuse : COLOR0_center;\n"
+ "float2 TexCoord0 : TEXCOORD0;\n"
+ "float4 ChannelSelector : TEXCOORD1;\n"
+ "};\n"
+// "\n"
+ "uniform float4 Color : register(c1);\n"
+ "uniform float2 TexScale : register(c2);\n"
+// "\n"
+ "sampler FontTexture : register(s0);\n"
+// "\n"
+ "VS_OUT FontVertexShader( VS_IN In )\n"
+ "{\n"
+ "VS_OUT Out;\n"
+ "Out.Position.x = (In.Pos.x-0.5);\n"
+ "Out.Position.y = (In.Pos.y-0.5);\n"
+ "Out.Position.z = ( 0.0 );\n"
+ "Out.Position.w = ( 1.0 );\n"
+ "Out.Diffuse = Color;\n"
+ "Out.TexCoord0.x = In.Tex.x * TexScale.x;\n"
+ "Out.TexCoord0.y = In.Tex.y * TexScale.y;\n"
+ "Out.ChannelSelector = In.ChannelSelector;\n"
+ "return Out;\n"
+ "}\n"
+ // "\n"
+ "float4 FontPixelShader( VS_OUT In ) : COLOR0\n"
+ "{\n"
+// "// Fetch a texel from the font texture\n"
+ "float4 FontTexel = tex2D( FontTexture, In.TexCoord0 );\n"
+// "\n"
+ "if( dot( In.ChannelSelector, float4(1,1,1,1) ) )\n"
+ "{\n"
+// "// Select the color from the channel\n"
+ "float value = dot( FontTexel, In.ChannelSelector );\n"
+// "\n"
+// "// For white pixels, the high bit is 1 and the low\n"
+// "// bits are luminance, so r0.a will be > 0.5. For the\n"
+// "// RGB channel, we want to lop off the msb and shift\n"
+// "// the lower bits up one bit. This is simple to do\n"
+// "// with the _bx2 modifier. Since these pixels are\n"
+// "// opaque, we emit a 1 for the alpha channel (which\n"
+// "// is 0.5 x2 ).\n"
+// "\n"
+// "// For black pixels, the high bit is 0 and the low\n"
+// "// bits are alpha, so r0.a will be < 0.5. For the RGB\n"
+// "// channel, we emit zero. For the alpha channel, we\n"
+// "// just use the x2 modifier to scale up the low bits\n"
+// "// of the alpha.\n"
+ "float4 Color;\n"
+ "Color.rgb = ( value > 0.5f ? 2*value-1 : 0.0f );\n"
+ "Color.a = 2 * ( value > 0.5f ? 1.0f : value );\n"
+// "\n"
+// "// Return the texture color modulated with the vertex\n"
+// "// color\n"
+ "return Color * In.Diffuse;\n"
+ "}\n"
+ "else\n"
+ "{\n"
+ "return FontTexel * In.Diffuse;\n"
+ "}\n"
+ "}\n";
+
+typedef struct AtgFont_Locals_t {
+ D3DVertexDeclaration* m_pFontVertexDecl; // Shared vertex buffer
+ D3DVertexShader* m_pFontVertexShader; // Created vertex shader
+ D3DPixelShader* m_pFontPixelShader; // Created pixel shader
+} AtgFont_Locals_t;
+
+// All elements are defaulted to NULL
+static AtgFont_Locals_t s_AtgFontLocals; // Global static instance
+
+//--------------------------------------------------------------------------------------
+// Name: CreateFontShaders()
+// Desc: Creates the global font shaders
+//--------------------------------------------------------------------------------------
+
+HRESULT XdkFont::CreateFontShaders()
+{
+ //
+ // There are only two states the globals could be in,
+ // Initialized, in which the ref count is increased,
+ // Uninialized, in which the vertex/pixel shaders need to be
+ // started up and a vertex array created.
+ ///
+
+ HRESULT hr;
+
+ if (!s_AtgFontLocals.m_pFontVertexDecl)
+ {
+ // Use the do {} while(0); trick for a fake goto
+ // It simplies tear down on error conditions.
+ do
+ {
+
+ // Step #1, create my vertex array with 16 bytes per entry
+ // Floats for the position,
+ // shorts for the uvs
+ // 32 bit packed ARGB 8:8:8:8 for color
+
+ static const D3DVERTEXELEMENT9 decl[] =
+ {
+ { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, 8, D3DDECLTYPE_USHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ D3DDECL_END()
+ };
+
+ // Cache this global into a register
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *pd3dDevice = vid->xdk360_render_device;
+
+ hr = pd3dDevice->CreateVertexDeclaration( decl, &s_AtgFontLocals.m_pFontVertexDecl );
+ if (SUCCEEDED(hr))
+ {
+
+ // Step #2, create my vertex shader
+ ID3DXBuffer* pShaderCode;
+ hr = D3DXCompileShader( g_strFontShader, sizeof(g_strFontShader)-1 ,
+ NULL, NULL, "FontVertexShader", "vs.2.0", 0,&pShaderCode, NULL, NULL );
+ if (SUCCEEDED(hr))
+ {
+ hr = pd3dDevice->CreateVertexShader( ( unsigned long * )pShaderCode->GetBufferPointer(),
+ &s_AtgFontLocals.m_pFontVertexShader );
+ // Release the compiled shader
+ pShaderCode->Release();
+
+ if(SUCCEEDED(hr))
+ {
+ // Step #3, create my pixel shader
+ hr = D3DXCompileShader( g_strFontShader, sizeof(g_strFontShader)-1 ,
+ NULL, NULL, "FontPixelShader", "ps.2.0", 0,&pShaderCode, NULL, NULL );
+ if ( SUCCEEDED(hr))
+ {
+ hr = pd3dDevice->CreatePixelShader( ( DWORD* )pShaderCode->GetBufferPointer(),
+ &s_AtgFontLocals.m_pFontPixelShader );
+ // Release the compiled shader
+ pShaderCode->Release();
+
+ if (SUCCEEDED(hr))
+ {
+ hr = S_OK;
+ break; // Skip the teardown code
+ }
+ }
+ // If the code got to here, a fatal error has occured
+ // and a clean shutdown needs to be performed.
+
+ s_AtgFontLocals.m_pFontVertexShader->Release();
+ }
+ // Ensure the pointer is NULL
+ s_AtgFontLocals.m_pFontVertexShader = NULL;
+ }
+ s_AtgFontLocals.m_pFontVertexDecl->Release();
+ }
+ // Ensure this pointer is NULL
+ s_AtgFontLocals.m_pFontVertexDecl = NULL;
+ }while(0); // Exit point for the break command.
+ return hr;
+ }
+ else
+ {
+ //
+ // Already initialized, so just add to the ref counts
+ //
+
+ s_AtgFontLocals.m_pFontVertexDecl->AddRef();
+ s_AtgFontLocals.m_pFontVertexShader->AddRef();
+ s_AtgFontLocals.m_pFontPixelShader->AddRef();
+ hr = S_OK;
+ }
+ return hr; // Return the error code if any
+}
+
+//--------------------------------------------------------------------------------------
+// Name: ReleaseFontShaders()
+// Desc: Releases the font shaders by reference
+//--------------------------------------------------------------------------------------
+
+void XdkFont::ReleaseFontShaders()
+{
+ // Safely release shaders
+ // NOTE: They are released in reverse order of creation
+ // to make sure any interdependencies are dealt with
+
+ if( ( s_AtgFontLocals.m_pFontPixelShader != NULL ) && ( s_AtgFontLocals.m_pFontPixelShader->Release() == 0 ) )
+ s_AtgFontLocals.m_pFontPixelShader = NULL;
+ if( ( s_AtgFontLocals.m_pFontVertexShader != NULL ) && ( s_AtgFontLocals.m_pFontVertexShader->Release() == 0 ) )
+ s_AtgFontLocals.m_pFontVertexShader = NULL;
+ if( ( s_AtgFontLocals.m_pFontVertexDecl != NULL ) && ( s_AtgFontLocals.m_pFontVertexDecl->Release() == 0 ) )
+ s_AtgFontLocals.m_pFontVertexDecl = NULL;
+}
+
+//--------------------------------------------------------------------------------------
+// Name: Font()
+// Desc: Constructor
+//--------------------------------------------------------------------------------------
+XdkFont::XdkFont()
+{
+ m_pFontTexture = NULL;
+
+ m_dwNumGlyphs = 0L;
+ m_Glyphs = NULL;
+
+ m_fCursorX = 0.0f;
+ m_fCursorY = 0.0f;
+
+ m_fXScaleFactor = 2.0f;
+ m_fYScaleFactor = 2.0f;
+ m_fSlantFactor = 0.0f;
+ m_bRotate = FALSE;
+ m_dRotCos = cos( 0.0 );
+ m_dRotSin = sin( 0.0 );
+
+ m_cMaxGlyph = 0;
+ m_TranslatorTable = NULL;
+
+ m_dwNestedBeginCount = 0L;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: ~Font()
+// Desc: Destructor
+//--------------------------------------------------------------------------------------
+XdkFont::~XdkFont()
+{
+ Destroy();
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: Create()
+// Desc: Create the font's internal objects (texture and array of glyph info)
+// using the XPR packed resource file
+//--------------------------------------------------------------------------------------
+HRESULT XdkFont::Create( const char * strFontFileName )
+{
+ // Create the font
+ if( FAILED( m_xprResource.Create( strFontFileName ) ) )
+ return E_FAIL;
+
+ D3DTexture * pFontTexture = m_xprResource.GetTexture( "FontTexture" );
+ const void * pFontData = m_xprResource.GetData( "FontData");
+
+ // Save a copy of the texture
+ m_pFontTexture = pFontTexture;
+
+ // Check version of file (to make sure it matches up with the FontMaker tool)
+ const unsigned char * pData = static_cast(pFontData);
+ unsigned long dwFileVersion = reinterpret_cast(pData)->m_dwFileVersion;
+
+ if( dwFileVersion == FONTFILEVERSION )
+ {
+ m_fFontHeight = reinterpret_cast(pData)->m_fFontHeight;
+ m_fFontTopPadding = reinterpret_cast(pData)->m_fFontTopPadding;
+ m_fFontBottomPadding = reinterpret_cast(pData)->m_fFontBottomPadding;
+ m_fFontYAdvance = reinterpret_cast(pData)->m_fFontYAdvance;
+
+ // Point to the translator string which immediately follows the 4 floats
+ m_cMaxGlyph = reinterpret_cast(pData)->m_cMaxGlyph;
+
+ m_TranslatorTable = const_cast(reinterpret_cast(pData))->m_TranslatorTable;
+
+ pData += CALCFONTFILEHEADERSIZE( m_cMaxGlyph + 1 );
+
+ // Read the glyph attributes from the file
+ m_dwNumGlyphs = reinterpret_cast(pData)->m_dwNumGlyphs;
+ m_Glyphs = reinterpret_cast(pData)->m_Glyphs; // Pointer
+ }
+ else
+ {
+ SSNES_ERR( "Incorrect version number on font file.\n" );
+ return E_FAIL;
+ }
+
+ // Create the vertex and pixel shaders for rendering the font
+ if( FAILED( CreateFontShaders() ) )
+ {
+ SSNES_ERR( "Could not create font shaders.\n" );
+ return E_FAIL;
+ }
+
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *pd3dDevice = vid->xdk360_render_device;
+
+ // Initialize the window
+ D3DDISPLAYMODE DisplayMode;
+ pd3dDevice->GetDisplayMode( 0, &DisplayMode );
+ m_rcWindow.x1 = 0;
+ m_rcWindow.y1 = 0;
+ m_rcWindow.x2 = DisplayMode.Width;
+ m_rcWindow.y2 = DisplayMode.Height;
+
+ // Determine whether we should save/restore state
+ m_bSaveState = TRUE;
+
+ return S_OK;
+}
+
+//--------------------------------------------------------------------------------------
+// Name: Destroy()
+// Desc: Destroy the font object
+//--------------------------------------------------------------------------------------
+void XdkFont::Destroy()
+{
+ m_pFontTexture = NULL;
+ m_dwNumGlyphs = 0L;
+ m_Glyphs = NULL;
+ m_cMaxGlyph = 0;
+ m_TranslatorTable = NULL;
+ m_dwNestedBeginCount = 0L;
+
+ // Safely release shaders
+ ReleaseFontShaders();
+
+ if( m_xprResource.m_bInitialized)
+ m_xprResource.Destroy();
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: SetWindow()
+// Desc: Sets the and the bounds rect for drawing text and resets the cursor position
+//--------------------------------------------------------------------------------------
+void XdkFont::SetWindow(const D3DRECT &rcWindow )
+{
+ m_rcWindow.x1 = rcWindow.x1;
+ m_rcWindow.y1 = rcWindow.y1;
+ m_rcWindow.x2 = rcWindow.x2;
+ m_rcWindow.y2 = rcWindow.y2;
+
+ m_fCursorX = 0.0f;
+ m_fCursorY = 0.0f;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: SetWindow()
+// Desc: Sets the and the bounds rect for drawing text and resets the cursor position
+//--------------------------------------------------------------------------------------
+void XdkFont::SetWindow( long x1, long y1, long x2, long y2 )
+{
+ m_rcWindow.x1 = x1;
+ m_rcWindow.y1 = y1;
+ m_rcWindow.x2 = x2;
+ m_rcWindow.y2 = y2;
+
+ m_fCursorX = 0.0f;
+ m_fCursorY = 0.0f;
+}
+
+//--------------------------------------------------------------------------------------
+// Name: GetWindow()
+// Desc: Gets the current bounds rect for drawing
+//--------------------------------------------------------------------------------------
+void XdkFont::GetWindow( D3DRECT &rcWindow ) const
+{
+ rcWindow = m_rcWindow; // NOTE: This is a structure copy
+}
+
+//--------------------------------------------------------------------------------------
+// Name: SetCursorPosition()
+// Desc: Sets the cursor position for drawing text
+//--------------------------------------------------------------------------------------
+void XdkFont::SetCursorPosition( float fCursorX, float fCursorY )
+{
+ m_fCursorX = floorf( fCursorX );
+ m_fCursorY = floorf( fCursorY );
+}
+
+//--------------------------------------------------------------------------------------
+// Name: GetTextExtent()
+// Desc: Get the dimensions of a text string
+//--------------------------------------------------------------------------------------
+
+void XdkFont::GetTextExtent( const wchar_t * strText, float * pWidth,
+ float * pHeight, int bFirstLineOnly ) const
+{
+ // Set default text extent in output parameters
+ int iWidth = 0;
+ float fHeight = 0.0f;
+
+ if( strText )
+ {
+ // Initialize counters that keep track of text extent
+ int ix = 0;
+ float fy = m_fFontHeight; // One character high to start
+ if( fy > fHeight )
+ fHeight = fy;
+
+ // Loop through each character and update text extent
+ unsigned long letter;
+ while( (letter = *strText) != 0 )
+ {
+ ++strText;
+
+ // Handle newline character
+ if( letter == L'\n' )
+ {
+ if( bFirstLineOnly )
+ break;
+ ix = 0;
+ fy += m_fFontYAdvance;
+ // since the height has changed, test against the height extent
+ if( fy > fHeight )
+ fHeight = fy;
+ }
+
+ // Handle carriage return characters by ignoring them. This helps when
+ // displaying text from a file.
+ if( letter == L'\r' )
+ continue;
+
+ // Translate unprintable characters
+ const GLYPH_ATTR* pGlyph;
+
+ if( letter > m_cMaxGlyph )
+ letter = 0; // Out of bounds?
+ else
+ letter = m_TranslatorTable[letter]; // Remap ASCII to glyph
+
+ pGlyph = &m_Glyphs[letter]; // Get the requested glyph
+
+ // Get text extent for this character's glyph
+ ix += pGlyph->wOffset;
+ ix += pGlyph->wAdvance;
+
+ // Since the x widened, test against the x extent
+
+ if( ix > iWidth )
+ iWidth = ix;
+ }
+ }
+
+ // Convert the width to a float here, load/hit/store. :(
+ float fWidth = static_cast(iWidth); // Delay the use if fWidth to reduce LHS pain
+ // Apply the scale factor to the result
+ fHeight *= m_fYScaleFactor;
+ // Store the final results
+ *pHeight = fHeight;
+
+ fWidth *= m_fXScaleFactor;
+ *pWidth = fWidth;
+}
+
+//--------------------------------------------------------------------------------------
+// Name: GetTextWidth()
+// Desc: Returns the width in pixels of a text string
+//--------------------------------------------------------------------------------------
+float XdkFont::GetTextWidth( const wchar_t * strText ) const
+{
+ float fTextWidth, fTextHeight;
+ GetTextExtent( strText, &fTextWidth, &fTextHeight );
+ return fTextWidth;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: Begin()
+// Desc: Prepares the font vertex buffers for rendering.
+//--------------------------------------------------------------------------------------
+VOID XdkFont::Begin()
+{
+ PIXBeginNamedEvent( 0, "Text Rendering" );
+
+ // Set state on the first call
+ if( m_dwNestedBeginCount == 0 )
+ {
+ // Cache the global pointer into a register
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *pD3dDevice = vid->xdk360_render_device;
+
+ // Save state
+ if( m_bSaveState )
+ {
+ // Note, we are not saving the texture, vertex, or pixel shader,
+ // since it's not worth the performance. We're more interested
+ // in saving state that would cause hard to find problems.
+ pD3dDevice->GetRenderState( D3DRS_ALPHABLENDENABLE,
+ &m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHABLENDENABLE ] );
+ pD3dDevice->GetRenderState( D3DRS_SRCBLEND, &m_dwSavedState[ SAVEDSTATE_D3DRS_SRCBLEND ] );
+ pD3dDevice->GetRenderState( D3DRS_DESTBLEND, &m_dwSavedState[ SAVEDSTATE_D3DRS_DESTBLEND ] );
+ pD3dDevice->GetRenderState( D3DRS_BLENDOP, &m_dwSavedState[ SAVEDSTATE_D3DRS_BLENDOP ] );
+ pD3dDevice->GetRenderState( D3DRS_ALPHATESTENABLE, &m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHATESTENABLE ] );
+ pD3dDevice->GetRenderState( D3DRS_ALPHAREF, &m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAREF ] );
+ pD3dDevice->GetRenderState( D3DRS_ALPHAFUNC, &m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAFUNC ] );
+ pD3dDevice->GetRenderState( D3DRS_FILLMODE, &m_dwSavedState[ SAVEDSTATE_D3DRS_FILLMODE ] );
+ pD3dDevice->GetRenderState( D3DRS_CULLMODE, &m_dwSavedState[ SAVEDSTATE_D3DRS_CULLMODE ] );
+ pD3dDevice->GetRenderState( D3DRS_ZENABLE, &m_dwSavedState[ SAVEDSTATE_D3DRS_ZENABLE ] );
+ pD3dDevice->GetRenderState( D3DRS_STENCILENABLE, &m_dwSavedState[ SAVEDSTATE_D3DRS_STENCILENABLE ] );
+ pD3dDevice->GetRenderState( D3DRS_VIEWPORTENABLE, &m_dwSavedState[ SAVEDSTATE_D3DRS_VIEWPORTENABLE ] );
+ pD3dDevice->GetSamplerState( 0, D3DSAMP_MINFILTER, &m_dwSavedState[ SAVEDSTATE_D3DSAMP_MINFILTER ] );
+ pD3dDevice->GetSamplerState( 0, D3DSAMP_MAGFILTER, &m_dwSavedState[ SAVEDSTATE_D3DSAMP_MAGFILTER ] );
+ pD3dDevice->GetSamplerState( 0, D3DSAMP_ADDRESSU, &m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSU ] );
+ pD3dDevice->GetSamplerState( 0, D3DSAMP_ADDRESSV, &m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSV ] );
+ }
+
+ // Set the texture scaling factor as a vertex shader constant
+ D3DSURFACE_DESC TextureDesc;
+ m_pFontTexture->GetLevelDesc( 0, &TextureDesc ); // Get the description
+
+ // Set render state
+ pD3dDevice->SetTexture( 0, m_pFontTexture );
+
+ // Read the TextureDesc here to ensure no load/hit/store from GetLevelDesc()
+ float vTexScale[4];
+ vTexScale[0] = 1.0f / TextureDesc.Width; // LHS due to int->float conversion
+ vTexScale[1] = 1.0f / TextureDesc.Height;
+ vTexScale[2] = 0.0f;
+ vTexScale[3] = 0.0f;
+
+ pD3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
+ pD3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
+ pD3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
+ pD3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
+ pD3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
+ pD3dDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 );
+ pD3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
+ pD3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
+ pD3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
+ pD3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
+ pD3dDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
+ pD3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, FALSE );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
+
+ pD3dDevice->SetVertexDeclaration( s_AtgFontLocals.m_pFontVertexDecl );
+ pD3dDevice->SetVertexShader( s_AtgFontLocals.m_pFontVertexShader );
+ pD3dDevice->SetPixelShader( s_AtgFontLocals.m_pFontPixelShader );
+
+ // Set the texture scaling factor as a vertex shader constant
+ // Call here to avoid load hit store from writing to vTexScale above
+ pD3dDevice->SetVertexShaderConstantF( 2, vTexScale, 1 );
+ }
+
+ // Keep track of the nested begin/end calls.
+ m_dwNestedBeginCount++;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: DrawText()
+// Desc: Draws text as textured polygons
+//--------------------------------------------------------------------------------------
+VOID XdkFont::DrawText( unsigned long dwColor, const wchar_t * strText,
+ unsigned long dwFlags, float fMaxPixelWidth )
+{
+ DrawText( m_fCursorX, m_fCursorY, dwColor, strText, dwFlags, fMaxPixelWidth );
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: DrawText()
+// Desc: Draws text as textured polygons
+// TODO: This function should use the Begin/SetVertexData/End() API when it
+// becomes available.
+//--------------------------------------------------------------------------------------
+VOID XdkFont::DrawText( float fOriginX, float fOriginY, unsigned long dwColor,
+ const wchar_t * strText, unsigned long dwFlags, float fMaxPixelWidth )
+{
+ if( strText == NULL )
+ return;
+ if( L'\0' == strText[0] )
+ return;
+
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *pd3dDevice = vid->xdk360_render_device;
+
+ // Create a PIX user-defined event that encapsulates all of the text draw calls.
+ // This makes DrawText calls easier to recognize in PIX captures, and it makes
+ // them take up fewer entries in the event list.
+ PIXBeginNamedEvent( dwColor, "DrawText: %S", strText );
+
+ // Set the color as a vertex shader constant
+ float vColor[4];
+ vColor[0] = ( ( dwColor & 0x00ff0000 ) >> 16L ) / 255.0F;
+ vColor[1] = ( ( dwColor & 0x0000ff00 ) >> 8L ) / 255.0F;
+ vColor[2] = ( ( dwColor & 0x000000ff ) >> 0L ) / 255.0F;
+ vColor[3] = ( ( dwColor & 0xff000000 ) >> 24L ) / 255.0F;
+
+ // Set up stuff to prepare for drawing text
+ Begin();
+
+ // Perform the actual storing of the color constant here to prevent
+ // a load-hit-store by inserting work between the store and the use of
+ // the vColor array.
+ pd3dDevice->SetVertexShaderConstantF( 1, vColor, 1 );
+
+ // Set the starting screen position
+ if( ( fOriginX < 0.0f ) || ( ( dwFlags & FONT_RIGHT ) && ( fOriginX <= 0.0f ) ) )
+ fOriginX += ( m_rcWindow.x2 - m_rcWindow.x1 );
+ if( fOriginY < 0.0f )
+ fOriginY += ( m_rcWindow.y2 - m_rcWindow.y1 );
+
+ m_fCursorX = floorf( fOriginX );
+ m_fCursorY = floorf( fOriginY );
+
+ // Adjust for padding
+ fOriginY -= m_fFontTopPadding;
+
+ float fEllipsesPixelWidth = m_fXScaleFactor * 3.0f * ( m_Glyphs[m_TranslatorTable[L'.']].wOffset +
+ m_Glyphs[m_TranslatorTable[L'.']].wAdvance );
+
+ if( dwFlags & FONT_TRUNCATED )
+ {
+ // Check if we will really need to truncate the string
+ if( fMaxPixelWidth <= 0.0f )
+ dwFlags &= ( ~FONT_TRUNCATED );
+ else
+ {
+ float w, h;
+ GetTextExtent( strText, &w, &h, TRUE );
+
+ // If not, then clear the flag
+ if( w <= fMaxPixelWidth )
+ dwFlags &= ( ~FONT_TRUNCATED );
+ }
+ }
+
+ // If vertically centered, offset the starting m_fCursorY value
+ if( dwFlags & FONT_CENTER_Y )
+ {
+ float w, h;
+ GetTextExtent( strText, &w, &h );
+ m_fCursorY = floorf( m_fCursorY - (h * 0.5f) );
+ }
+
+ // Add window offsets
+ float Winx = static_cast(m_rcWindow.x1);
+ float Winy = static_cast(m_rcWindow.y1);
+ fOriginX += Winx;
+ fOriginY += Winy;
+ m_fCursorX += Winx;
+ m_fCursorY += Winy;
+
+ // Set a flag so we can determine initial justification effects
+ BOOL bStartingNewLine = TRUE;
+
+ unsigned long dwNumEllipsesToDraw = 0;
+
+ // Begin drawing the vertices
+
+ // Declared as volatile to force writing in ascending
+ // address order. It prevents out of sequence writing in write combined
+ // memory.
+
+ volatile float * pVertex;
+
+ unsigned long dwNumChars = wcslen( strText ) + ( dwFlags & FONT_TRUNCATED ? 3 : 0 );
+ HRESULT hr = pd3dDevice->BeginVertices( D3DPT_QUADLIST, 4 * dwNumChars, sizeof( XMFLOAT4 ) ,
+ ( VOID** )&pVertex );
+ // The ring buffer may run out of space when tiling, doing z-prepasses,
+ // or using BeginCommandBuffer. If so, make the buffer larger.
+ if( FAILED( hr ) )
+ SSNES_ERR( "Ring buffer out of memory.\n" );
+
+ bStartingNewLine = TRUE;
+
+ // Draw four vertices for each glyph
+ while( *strText )
+ {
+ wchar_t letter;
+
+ if( dwNumEllipsesToDraw )
+ letter = L'.';
+ else
+ {
+ // If starting text on a new line, determine justification effects
+ if( bStartingNewLine )
+ {
+ if( dwFlags & ( FONT_RIGHT | FONT_CENTER_X ) )
+ {
+ // Get the extent of this line
+ float w, h;
+ GetTextExtent( strText, &w, &h, TRUE );
+
+ // Offset this line's starting m_fCursorX value
+ if( dwFlags & FONT_RIGHT )
+ m_fCursorX = floorf( fOriginX - w );
+ if( dwFlags & FONT_CENTER_X )
+ m_fCursorX = floorf( fOriginX - w * 0.5f );
+ }
+ bStartingNewLine = FALSE;
+ }
+
+ // Get the current letter in the string
+ letter = *strText++;
+
+ // Handle the newline character
+ if( letter == L'\n' )
+ {
+ m_fCursorX = fOriginX;
+ m_fCursorY += m_fFontYAdvance * m_fYScaleFactor;
+ bStartingNewLine = TRUE;
+ continue;
+ }
+
+ // Handle carriage return characters by ignoring them. This helps when
+ // displaying text from a file.
+ if( letter == L'\r' )
+ continue;
+ }
+
+ // Translate unprintable characters
+ const GLYPH_ATTR * pGlyph = &m_Glyphs[ ( letter <= m_cMaxGlyph ) ? m_TranslatorTable[letter] : 0 ];
+
+ float fOffset = m_fXScaleFactor * (float)pGlyph->wOffset;
+ float fAdvance = m_fXScaleFactor * (float)pGlyph->wAdvance;
+ float fWidth = m_fXScaleFactor * (float)pGlyph->wWidth;
+ float fHeight = m_fYScaleFactor * m_fFontHeight;
+
+ if( 0 == dwNumEllipsesToDraw )
+ {
+ if( dwFlags & FONT_TRUNCATED )
+ {
+ // Check if we will be exceeded the max allowed width
+ if( m_fCursorX + fOffset + fWidth + fEllipsesPixelWidth + m_fSlantFactor > fOriginX + fMaxPixelWidth )
+ {
+ // Yup, draw the three ellipses dots instead
+ dwNumEllipsesToDraw = 3;
+ continue;
+ }
+ }
+ }
+
+ // Setup the screen coordinates
+ m_fCursorX += fOffset;
+ float X4 = m_fCursorX;
+ float X1 = X4 + m_fSlantFactor;
+ float X3 = X4 + fWidth;
+ float X2 = X1 + fWidth;
+ float Y1 = m_fCursorY;
+ float Y3 = Y1 + fHeight;
+ float Y2 = Y1;
+ float Y4 = Y3;
+
+ // Rotate the points by the rotation factor
+ if( m_bRotate )
+ {
+ RotatePoint( &X1, &Y1, fOriginX, fOriginY );
+ RotatePoint( &X2, &Y2, fOriginX, fOriginY );
+ RotatePoint( &X3, &Y3, fOriginX, fOriginY );
+ RotatePoint( &X4, &Y4, fOriginX, fOriginY );
+ }
+
+ m_fCursorX += fAdvance;
+
+ // Select the RGBA channel that the compressed glyph is stored in
+ // Takes a 4 bit per pixel ARGB value and expand it to an 8 bit per pixel ARGB value
+
+ unsigned long dwChannelSelector = pGlyph->wMask; // Convert to 32 bit
+ // Perform the conversion without branching
+
+ // Splat the 4 bit per pixels from 0x1234 to 0x01020304
+ dwChannelSelector = ((dwChannelSelector&0xF000)<<(24-12))|((dwChannelSelector&0xF00)<<(16-8))|
+ ((dwChannelSelector&0xF0)<<(8-4))|(dwChannelSelector&0xF);
+
+ // Perform a vectorized multiply to make 0x01020304 into 0x11223344
+ dwChannelSelector *= 0x11;
+
+ // Add the vertices to draw this glyph
+
+ unsigned long tu1 = pGlyph->tu1; // Convert shorts to 32 bit longs for in register merging
+ unsigned long tv1 = pGlyph->tv1;
+ unsigned long tu2 = pGlyph->tu2;
+ unsigned long tv2 = pGlyph->tv2;
+
+ // NOTE: The vertexs are 2 floats for the screen coordinates,
+ // followed by two USHORTS for the u/vs of the character,
+ // terminated with the ARGB 32 bit color.
+ // This makes for 16 bytes per vertex data (Easier to read)
+ // Second NOTE: The uvs are merged and written using a DWORD due
+ // to the write combining hardware being only able to handle 32,
+ // 64 and 128 writes. Never store to write combined memory with
+ // 8 or 16 bit instructions. You've been warned.
+
+ pVertex[0] = X1;
+ pVertex[1] = Y1;
+ reinterpret_cast(pVertex)[2] = (tu1<<16)|tv1; // Merged using big endian rules
+ reinterpret_cast(pVertex)[3] = dwChannelSelector;
+ pVertex[4] = X2;
+ pVertex[5] = Y2;
+ reinterpret_cast(pVertex)[6] = (tu2<<16)|tv1; // Merged using big endian rules
+ reinterpret_cast(pVertex)[7] = dwChannelSelector;
+ pVertex[8] = X3;
+ pVertex[9] = Y3;
+ reinterpret_cast(pVertex)[10] = (tu2<<16)|tv2; // Merged using big endian rules
+ reinterpret_cast(pVertex)[11] = dwChannelSelector;
+ pVertex[12] = X4;
+ pVertex[13] = Y4;
+ reinterpret_cast(pVertex)[14] = (tu1<<16)|tv2; // Merged using big endian rules
+ reinterpret_cast(pVertex)[15] = dwChannelSelector;
+ pVertex+=16;
+
+ // If drawing ellipses, exit when they're all drawn
+ if( dwNumEllipsesToDraw )
+ {
+ if( --dwNumEllipsesToDraw == 0 )
+ break;
+ }
+
+ dwNumChars--;
+ }
+
+ // Since we allocated vertex data space based on the string length, we now need to
+ // add some dummy verts for any skipped characters (like newlines, etc.)
+ while( dwNumChars )
+ {
+ pVertex[0] = 0;
+ pVertex[1] = 0;
+ pVertex[2] = 0;
+ pVertex[3] = 0;
+ pVertex[4] = 0;
+ pVertex[5] = 0;
+ pVertex[6] = 0;
+ pVertex[7] = 0;
+ pVertex[8] = 0;
+ pVertex[9] = 0;
+ pVertex[10] = 0;
+ pVertex[11] = 0;
+ pVertex[12] = 0;
+ pVertex[13] = 0;
+ pVertex[14] = 0;
+ pVertex[15] = 0;
+ pVertex+=16;
+ dwNumChars--;
+ }
+
+ // Stop drawing vertices
+ pd3dDevice->EndVertices();
+
+ // Undo window offsets
+ m_fCursorX -= Winx;
+ m_fCursorY -= Winy;
+
+ // Call End() to complete the begin/end pair for drawing text
+ End();
+
+ // Close off the user-defined event opened with PIXBeginNamedEvent.
+ PIXEndNamedEvent();
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: End()
+// Desc: Paired call that restores state set in the Begin() call.
+//--------------------------------------------------------------------------------------
+VOID XdkFont::End()
+{
+ if( --m_dwNestedBeginCount > 0 )
+ {
+ PIXEndNamedEvent();
+ return;
+ }
+
+ // Restore state
+ if( m_bSaveState )
+ {
+ // Cache the global pointer into a register
+ xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
+ D3DDevice *pD3dDevice = vid->xdk360_render_device;
+
+ pD3dDevice->SetTexture( 0, NULL );
+ pD3dDevice->SetVertexDeclaration( NULL );
+ pD3dDevice->SetVertexShader( NULL );
+ pD3dDevice->SetPixelShader( NULL );
+ pD3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHABLENDENABLE ] );
+ pD3dDevice->SetRenderState( D3DRS_SRCBLEND, m_dwSavedState[ SAVEDSTATE_D3DRS_SRCBLEND ] );
+ pD3dDevice->SetRenderState( D3DRS_DESTBLEND, m_dwSavedState[ SAVEDSTATE_D3DRS_DESTBLEND ] );
+ pD3dDevice->SetRenderState( D3DRS_BLENDOP, m_dwSavedState[ SAVEDSTATE_D3DRS_BLENDOP ] );
+ pD3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHATESTENABLE ] );
+ pD3dDevice->SetRenderState( D3DRS_ALPHAREF, m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAREF ] );
+ pD3dDevice->SetRenderState( D3DRS_ALPHAFUNC, m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAFUNC ] );
+ pD3dDevice->SetRenderState( D3DRS_FILLMODE, m_dwSavedState[ SAVEDSTATE_D3DRS_FILLMODE ] );
+ pD3dDevice->SetRenderState( D3DRS_CULLMODE, m_dwSavedState[ SAVEDSTATE_D3DRS_CULLMODE ] );
+ pD3dDevice->SetRenderState( D3DRS_ZENABLE, m_dwSavedState[ SAVEDSTATE_D3DRS_ZENABLE ] );
+ pD3dDevice->SetRenderState( D3DRS_STENCILENABLE, m_dwSavedState[ SAVEDSTATE_D3DRS_STENCILENABLE ] );
+ pD3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, m_dwSavedState[ SAVEDSTATE_D3DRS_VIEWPORTENABLE ] );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, m_dwSavedState[ SAVEDSTATE_D3DSAMP_MINFILTER ] );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, m_dwSavedState[ SAVEDSTATE_D3DSAMP_MAGFILTER ] );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSU ] );
+ pD3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSV ] );
+ }
+
+ PIXEndNamedEvent();
+}
+
+//--------------------------------------------------------------------------------------
+// Name: RotatePoint()
+// Desc: Rotate a 2D point around the origin
+//-------------------------------------------------------------------------------------
+void XdkFont::RotatePoint( float * X, float * Y, double OriginX, double OriginY ) const
+{
+ double dTempX = *X - OriginX;
+ double dTempY = *Y - OriginY;
+ double dXprime = OriginX + ( m_dRotCos * dTempX - m_dRotSin * dTempY );
+ double dYprime = OriginY + ( m_dRotSin * dTempX + m_dRotCos * dTempY );
+
+ *X = static_cast( dXprime );
+ *Y = static_cast( dYprime );
+}
\ No newline at end of file
diff --git a/360/xdk360_video_debugfonts.h b/360/xdk360_video_debugfonts.h
new file mode 100644
index 0000000000..3be4d62f68
--- /dev/null
+++ b/360/xdk360_video_debugfonts.h
@@ -0,0 +1,134 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2012 - Daniel De Matteis
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES 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 Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 SSNES.
+ * If not, see .
+ */
+
+#ifndef _SSNES360_DEBUG_FONTS_H
+#define _SSNES360_DEBUG_FONTS_H
+
+#include "xdk360_video_resources.h"
+
+typedef struct GLYPH_ATTR
+{
+ unsigned short tu1, tv1, tu2, tv2; // Texture coordinates for the image
+ short wOffset; // Pixel offset for glyph start
+ short wWidth; // Pixel width of the glyph
+ short wAdvance; // Pixels to advance after the glyph
+ unsigned short wMask; // Channel mask
+} GLYPH_ATTR;
+
+#define FONT_LEFT 0x00000000
+#define FONT_RIGHT 0x00000001
+#define FONT_CENTER_X 0x00000002
+#define FONT_CENTER_Y 0x00000004
+#define FONT_TRUNCATED 0x00000008
+
+enum SavedStates
+{
+ SAVEDSTATE_D3DRS_ALPHABLENDENABLE,
+ SAVEDSTATE_D3DRS_SRCBLEND,
+ SAVEDSTATE_D3DRS_DESTBLEND,
+ SAVEDSTATE_D3DRS_BLENDOP,
+ SAVEDSTATE_D3DRS_ALPHATESTENABLE,
+ SAVEDSTATE_D3DRS_ALPHAREF,
+ SAVEDSTATE_D3DRS_ALPHAFUNC,
+ SAVEDSTATE_D3DRS_FILLMODE,
+ SAVEDSTATE_D3DRS_CULLMODE,
+ SAVEDSTATE_D3DRS_ZENABLE,
+ SAVEDSTATE_D3DRS_STENCILENABLE,
+ SAVEDSTATE_D3DRS_VIEWPORTENABLE,
+ SAVEDSTATE_D3DSAMP_MINFILTER,
+ SAVEDSTATE_D3DSAMP_MAGFILTER,
+ SAVEDSTATE_D3DSAMP_ADDRESSU,
+ SAVEDSTATE_D3DSAMP_ADDRESSV,
+
+ SAVEDSTATE_COUNT
+};
+
+class XdkFont
+{
+public:
+ PackedResource m_xprResource;
+
+ // Font vertical dimensions taken from the font file
+ float m_fFontHeight; // Height of the font strike in pixels
+ float m_fFontTopPadding; // Padding above the strike zone
+ float m_fFontBottomPadding; // Padding below the strike zone
+ float m_fFontYAdvance; // Number of pixels to move the cursor for a line feed
+
+ float m_fXScaleFactor; // Scaling constants
+ float m_fYScaleFactor;
+ float m_fSlantFactor; // For italics
+ double m_dRotCos; // Precalculated sine and cosine for italic like rotation
+ double m_dRotSin;
+
+ D3DRECT m_rcWindow; // Bounds rect if the text window, modify via accessors only!
+ float m_fCursorX; // Current text cursor
+ float m_fCursorY;
+
+ // Translator table for supporting unicode ranges
+ unsigned long m_cMaxGlyph; // Number of entries in the translator table
+ wchar_t * m_TranslatorTable; // ASCII to glyph lookup table
+
+ // Glyph data for the font
+ unsigned long m_dwNumGlyphs; // Number of valid glyphs
+ const GLYPH_ATTR* m_Glyphs; // Array of glyphs
+
+ // D3D rendering objects
+ D3DTexture* m_pFontTexture;
+
+ // Saved state for rendering (if not using a pure device)
+ unsigned long m_dwSavedState[ SAVEDSTATE_COUNT ];
+ unsigned long m_dwNestedBeginCount;
+ int m_bSaveState;
+
+ int m_bRotate;
+public:
+ XdkFont();
+ ~XdkFont();
+
+ // Functions to create and destroy the internal objects
+ HRESULT Create( const char * strFontFileName );
+ void Destroy();
+
+ // Returns the dimensions of a text string
+ void GetTextExtent( const wchar_t * strText, float * pWidth,
+ float * pHeight, int bFirstLineOnly=FALSE ) const;
+ float GetTextWidth( const wchar_t * strText ) const;
+
+ void SetWindow(const D3DRECT &rcWindow );
+ void SetWindow( long x1, long y1, long x2, long y2 );
+ void GetWindow(D3DRECT &rcWindow) const;
+ void SetCursorPosition( float fCursorX, float fCursorY );
+
+ // Public calls to render text. Callers can simply call DrawText(), but for
+ // performance, they should batch multiple calls together, bracketed by calls to
+ // Begin() and End().
+ void Begin();
+ void DrawText( unsigned long dwColor, const wchar_t * strText, unsigned long dwFlags=0L,
+ float fMaxPixelWidth = 0.0f );
+ void DrawText( float sx, float sy, unsigned long dwColor, const wchar_t * strText,
+ unsigned long dwFlags=0L, float fMaxPixelWidth = 0.0f );
+ void End();
+
+private:
+ // Internal helper functions
+ HRESULT CreateFontShaders();
+ void ReleaseFontShaders();
+ void RotatePoint( float * X, float * Y, double OriginX, double OriginY ) const;
+};
+
+#endif
\ No newline at end of file
diff --git a/360/xdk360_video_resources.cpp b/360/xdk360_video_resources.cpp
new file mode 100644
index 0000000000..356f78391c
--- /dev/null
+++ b/360/xdk360_video_resources.cpp
@@ -0,0 +1,211 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2012 - Daniel De Matteis
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES 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 Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 SSNES.
+ * If not, see .
+ */
+
+#include
+#include
+#include "xdk360_video.h"
+#include "xdk360_video_resources.h"
+#include "../general.h"
+
+//--------------------------------------------------------------------------------------
+// Magic values to identify XPR files
+//--------------------------------------------------------------------------------------
+struct XPR_HEADER
+{
+ unsigned long dwMagic;
+ unsigned long dwHeaderSize;
+ unsigned long dwDataSize;
+};
+
+#define XPR2_MAGIC_VALUE (0x58505232)
+#define eXALLOCAllocatorId_AtgResource eXALLOCAllocatorId_GameMax
+
+//--------------------------------------------------------------------------------------
+// Name: PackedResource
+//--------------------------------------------------------------------------------------
+PackedResource::PackedResource()
+{
+ m_pSysMemData = NULL;
+ m_dwSysMemDataSize = 0L;
+ m_pVidMemData = NULL;
+ m_dwVidMemDataSize = 0L;
+ m_pResourceTags = NULL;
+ m_dwNumResourceTags = 0L;
+ m_bInitialized = FALSE;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: PackedResource
+//--------------------------------------------------------------------------------------
+PackedResource::~PackedResource()
+{
+ Destroy();
+}
+
+//--------------------------------------------------------------------------------------
+// Name: GetData
+// Desc: Loads all the texture resources from the given XPR.
+//--------------------------------------------------------------------------------------
+void * PackedResource::GetData( const char * strName ) const
+{
+ if( m_pResourceTags == NULL || strName == NULL )
+ return NULL;
+
+ for( unsigned long i = 0; i < m_dwNumResourceTags; i++ )
+ {
+ if( !_stricmp( strName, m_pResourceTags[i].strName ) )
+ return &m_pSysMemData[m_pResourceTags[i].dwOffset];
+ }
+
+ return NULL;
+}
+
+//--------------------------------------------------------------------------------------
+// Name: Create
+// Desc: Loads all the texture resources from the given XPR.
+//--------------------------------------------------------------------------------------
+HRESULT PackedResource::Create( const char * strFilename )
+{
+ // Open the file
+ unsigned long dwNumBytesRead;
+ HANDLE hFile = CreateFile( strFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL );
+ if( hFile == INVALID_HANDLE_VALUE )
+ {
+ SSNES_ERR( "File <%s> not found.\n", strFilename );
+ return E_FAIL;
+ }
+
+ // Read in and verify the XPR magic header
+ XPR_HEADER xprh;
+ if( !ReadFile( hFile, &xprh, sizeof( XPR_HEADER ), &dwNumBytesRead, NULL ) )
+ {
+ SSNES_ERR( "Error reading XPR header in file <%s>.\n", strFilename );
+ CloseHandle( hFile );
+ return E_FAIL;
+ }
+
+ if( xprh.dwMagic != XPR2_MAGIC_VALUE )
+ {
+ SSNES_ERR( "Invalid Xbox Packed Resource (.xpr) file: Magic = 0x%08lx.\n", xprh.dwMagic );
+ CloseHandle( hFile );
+ return E_FAIL;
+ }
+
+ // Compute memory requirements
+ m_dwSysMemDataSize = xprh.dwHeaderSize;
+ m_dwVidMemDataSize = xprh.dwDataSize;
+
+ // Allocate memory
+ m_pSysMemData = new BYTE[m_dwSysMemDataSize];
+ if( m_pSysMemData == NULL )
+ {
+ SSNES_ERR( "Could not allocate system memory.\n" );
+ m_dwSysMemDataSize = 0;
+ return E_FAIL;
+ }
+ m_pVidMemData = ( BYTE* )XMemAlloc( m_dwVidMemDataSize, MAKE_XALLOC_ATTRIBUTES( 0, 0, 0, 0, eXALLOCAllocatorId_AtgResource,
+ XALLOC_PHYSICAL_ALIGNMENT_4K, XALLOC_MEMPROTECT_WRITECOMBINE, 0, XALLOC_MEMTYPE_PHYSICAL ) );
+
+ if( m_pVidMemData == NULL )
+ {
+ SSNES_ERR( "Could not allocate physical memory.\n" );
+ m_dwSysMemDataSize = 0;
+ m_dwVidMemDataSize = 0;
+ delete[] m_pSysMemData;
+ m_pSysMemData = NULL;
+ return E_FAIL;
+ }
+
+ // Read in the data from the file
+ if( !ReadFile( hFile, m_pSysMemData, m_dwSysMemDataSize, &dwNumBytesRead, NULL ) ||
+ !ReadFile( hFile, m_pVidMemData, m_dwVidMemDataSize, &dwNumBytesRead, NULL ) )
+ {
+ SSNES_ERR( "Unable to read Xbox Packed Resource (.xpr) file.\n" );
+ CloseHandle( hFile );
+ return E_FAIL;
+ }
+
+ // Done with the file
+ CloseHandle( hFile );
+
+ // Extract resource table from the header data
+ m_dwNumResourceTags = *( unsigned long * )( m_pSysMemData + 0 );
+ m_pResourceTags = ( RESOURCE* )( m_pSysMemData + 4 );
+
+ // Patch up the resources
+ for( unsigned long i = 0; i < m_dwNumResourceTags; i++ )
+ {
+ m_pResourceTags[i].strName = ( char * )( m_pSysMemData + ( unsigned long )m_pResourceTags[i].strName );
+
+ // Fixup the texture memory
+ if( ( m_pResourceTags[i].dwType & 0xffff0000 ) == ( RESOURCETYPE_TEXTURE & 0xffff0000 ) )
+ {
+ D3DTexture* pTexture = ( D3DTexture* )&m_pSysMemData[m_pResourceTags[i].dwOffset];
+ // Adjust Base address according to where memory was allocated
+ XGOffsetBaseTextureAddress( pTexture, m_pVidMemData, m_pVidMemData );
+
+ // Let PIX know the name of the texture
+ PIXSetTextureName(pTexture, m_pResourceTags[i].strName);
+ }
+ }
+
+ m_bInitialized = TRUE;
+
+ return S_OK;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: GetResourceTags
+// Desc: Retrieves the resource tags
+//--------------------------------------------------------------------------------------
+void PackedResource::GetResourceTags( unsigned long * pdwNumResourceTags,
+ RESOURCE** ppResourceTags ) const
+{
+ if( pdwNumResourceTags )
+ ( *pdwNumResourceTags ) = m_dwNumResourceTags;
+
+ if( ppResourceTags )
+ ( *ppResourceTags ) = m_pResourceTags;
+}
+
+
+//--------------------------------------------------------------------------------------
+// Name: Destroy
+// Desc: Cleans up the packed resource data
+//--------------------------------------------------------------------------------------
+void PackedResource::Destroy()
+{
+ delete[] m_pSysMemData;
+ m_pSysMemData = NULL;
+ m_dwSysMemDataSize = 0L;
+
+ if( m_pVidMemData != NULL )
+ XMemFree( m_pVidMemData, MAKE_XALLOC_ATTRIBUTES( 0, 0, 0, 0, eXALLOCAllocatorId_AtgResource,
+ 0, 0, 0, XALLOC_MEMTYPE_PHYSICAL ) );
+
+ m_pVidMemData = NULL;
+ m_dwVidMemDataSize = 0L;
+
+ m_pResourceTags = NULL;
+ m_dwNumResourceTags = 0L;
+
+ m_bInitialized = FALSE;
+}
diff --git a/360/xdk360_video_resources.h b/360/xdk360_video_resources.h
new file mode 100644
index 0000000000..25a3cffaa8
--- /dev/null
+++ b/360/xdk360_video_resources.h
@@ -0,0 +1,155 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2012 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2012 - Daniel De Matteis
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES 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 Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 SSNES.
+ * If not, see .
+ */
+
+#pragma once
+
+#ifndef SSNES360_RESOURCES_H
+#define SSNES360_RESOURCES_H
+
+//--------------------------------------------------------------------------------------
+// Name tag for resources. An app may initialize this structure, and pass
+// it to the resource's Create() function. From then on, the app may call
+// GetResource() to retrieve a resource using an ascii name.
+//--------------------------------------------------------------------------------------
+struct RESOURCE
+{
+ unsigned long dwType;
+ unsigned long dwOffset;
+ unsigned long dwSize;
+ char * strName;
+};
+
+
+// Resource types
+enum
+{
+ RESOURCETYPE_USERDATA = ( ( 'U' << 24 ) | ( 'S' << 16 ) | ( 'E' << 8 ) | ( 'R' ) ),
+ RESOURCETYPE_TEXTURE = ( ( 'T' << 24 ) | ( 'X' << 16 ) | ( '2' << 8 ) | ( 'D' ) ),
+ RESOURCETYPE_CUBEMAP = ( ( 'T' << 24 ) | ( 'X' << 16 ) | ( 'C' << 8 ) | ( 'M' ) ),
+ RESOURCETYPE_VOLUMETEXTURE = ( ( 'T' << 24 ) | ( 'X' << 16 ) | ( '3' << 8 ) | ( 'D' ) ),
+ RESOURCETYPE_VERTEXBUFFER = ( ( 'V' << 24 ) | ( 'B' << 16 ) | ( 'U' << 8 ) | ( 'F' ) ),
+ RESOURCETYPE_INDEXBUFFER = ( ( 'I' << 24 ) | ( 'B' << 16 ) | ( 'U' << 8 ) | ( 'F' ) ),
+ RESOURCETYPE_EOF = 0xffffffff
+};
+
+
+//--------------------------------------------------------------------------------------
+// Name: PackedResource
+//--------------------------------------------------------------------------------------
+class PackedResource
+{
+protected:
+ unsigned char * m_pSysMemData; // Alloc'ed memory for resource headers etc.
+ unsigned long m_dwSysMemDataSize;
+
+ unsigned char * m_pVidMemData; // Alloc'ed memory for resource data, etc.
+ unsigned long m_dwVidMemDataSize;
+
+ RESOURCE* m_pResourceTags; // Tags to associate names with the resources
+ unsigned long m_dwNumResourceTags; // Number of resource tags
+public:
+ int m_bInitialized; // Resource is fully initialized
+ // Loads the resources out of the specified bundle
+ HRESULT Create( const char * strFilename );
+
+ void Destroy();
+
+ // Retrieves the resource tags
+ void GetResourceTags( unsigned long * pdwNumResourceTags,
+ RESOURCE** ppResourceTags ) const;
+
+ // Helper function to make sure a resource is registered
+ D3DResource* RegisterResource( D3DResource* pResource ) const
+ {
+ return pResource;
+ }
+
+ // Functions to retrieve resources by their offset
+ void * GetData( unsigned long dwOffset ) const
+ {
+ return &m_pSysMemData[dwOffset];
+ }
+
+ D3DResource* GetResource( unsigned long dwOffset ) const
+ {
+ return RegisterResource( ( D3DResource* )GetData( dwOffset ) );
+ }
+
+ D3DTexture* GetTexture( unsigned long dwOffset ) const
+ {
+ return ( D3DTexture* )GetResource( dwOffset );
+ }
+
+ D3DArrayTexture* GetArrayTexture( unsigned long dwOffset ) const
+ {
+ return ( D3DArrayTexture* )GetResource( dwOffset );
+ }
+
+ D3DCubeTexture* GetCubemap( unsigned long dwOffset ) const
+ {
+ return ( D3DCubeTexture* )GetResource( dwOffset );
+ }
+
+ D3DVolumeTexture* GetVolumeTexture( unsigned long dwOffset ) const
+ {
+ return ( D3DVolumeTexture* )GetResource( dwOffset );
+ }
+
+ D3DVertexBuffer* GetVertexBuffer( unsigned long dwOffset ) const
+ {
+ return ( D3DVertexBuffer* )GetResource( dwOffset );
+ }
+
+ // Functions to retrieve resources by their name
+ void * GetData( const char * strName ) const;
+
+ D3DResource* GetResource( const char * strName ) const
+ {
+ return RegisterResource( ( D3DResource* )GetData( strName ) );
+ }
+
+ D3DTexture* GetTexture( const char * strName ) const
+ {
+ return ( D3DTexture* )GetResource( strName );
+ }
+
+ D3DArrayTexture* GetArrayTexture( const char * strName ) const
+ {
+ return ( D3DArrayTexture* )GetResource( strName );
+ }
+
+ D3DCubeTexture* GetCubemap( const char * strName ) const
+ {
+ return ( D3DCubeTexture* )GetResource( strName );
+ }
+
+ D3DVolumeTexture* GetVolumeTexture( const char * strName ) const
+ {
+ return ( D3DVolumeTexture* )GetResource( strName );
+ }
+
+ D3DVertexBuffer* GetVertexBuffer( const char * strName ) const
+ {
+ return ( D3DVertexBuffer* )GetResource( strName );
+ }
+
+ PackedResource();
+ ~PackedResource();
+};
+
+#endif
diff --git a/msvc-360/SSNES-360/SSNES-360.vcxproj b/msvc-360/SSNES-360/SSNES-360.vcxproj
index cdfebe96ed..8f6374db83 100644
--- a/msvc-360/SSNES-360/SSNES-360.vcxproj
+++ b/msvc-360/SSNES-360/SSNES-360.vcxproj
@@ -270,6 +270,9 @@
+
+
+
@@ -460,6 +463,23 @@
$(OutDir)media\b.png;%(Outputs)
+
+
+ Document
+ Bundler %(FullPath) /o $(OutDir)media\Arial_12.xpr
+ Bundler %(FullPath) /o $(OutDir)media\Arial_12.xpr
+ Bundler %(FullPath) /o $(OutDir)media\Arial_12.xpr
+ Bundler %(FullPath) /o $(OutDir)media\Arial_12.xpr
+ Bundler %(FullPath) /o $(OutDir)media\Arial_12.xpr
+ Bundler %(FullPath) /o $(OutDir)media\Arial_12.xpr
+ $(OutDir)media\Arial_12.xpr;%(Outputs)
+ $(OutDir)media\Arial_12.xpr;%(Outputs)
+ $(OutDir)media\Arial_12.xpr;%(Outputs)
+ $(OutDir)media\Arial_12.xpr;%(Outputs)
+ $(OutDir)media\Arial_12.xpr;%(Outputs)
+ $(OutDir)media\Arial_12.xpr;%(Outputs)
+
+
diff --git a/msvc-360/SSNES-360/SSNES-360.vcxproj.filters b/msvc-360/SSNES-360/SSNES-360.vcxproj.filters
index 36ee81b2d7..015f87c201 100644
--- a/msvc-360/SSNES-360/SSNES-360.vcxproj.filters
+++ b/msvc-360/SSNES-360/SSNES-360.vcxproj.filters
@@ -131,6 +131,15 @@
Source Files\console
+
+ Source Files\360
+
+
+ Source Files\360
+
+
+ Source Files\360
+
@@ -246,5 +255,8 @@
Source Files\media
+
+ Source Files\media
+
\ No newline at end of file