Skip to content

Instantly share code, notes, and snippets.

@RElesgoe
Created August 10, 2016 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RElesgoe/5ff182c6bd0fe1fabaf9552e65bdd9bc to your computer and use it in GitHub Desktop.
Save RElesgoe/5ff182c6bd0fe1fabaf9552e65bdd9bc to your computer and use it in GitHub Desktop.
Modification of Aqrit's ddraw.dll by xboi209
#include <array>
#include <chrono>
#include <cstring>
#include <string>
#include <thread>
#include <vector>
#include <windows.h>
#include <d3d9.h>
#include "display.h"
#include "dxerr.h"
#include "header.h"
#include "PaletteShader.h"
HWND hwnd_main = NULL;
BOOL IsWindowed = FALSE;
BOOL import_gdi_bits = FALSE;
WORD prtscn_toggle;
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
LPDIRECT3DVERTEXBUFFER9 d3dvb = NULL;
IDirect3DTexture9* d3dtex = NULL;
IDirect3DTexture9* d3dpal = NULL;
IDirect3DPixelShader9* shader = NULL;
__declspec(align(128)) BYTE client_bits[640 * 480];
BYTE* dib_bits = nullptr;
HDC hdc_offscreen;
HBITMAP hbmp_old;
COLORREF clear_color;
// double buffered (discard) with no vsync seems best,
// as there doesn't appear to be a way to drop frames while triple buffering w/vsync,
// ...which might be needed???,
// ...since we don't want to alter how many fps the game can draw
D3DPRESENT_PARAMETERS d3dpp = {
d3dpp.BackBufferWidth = 640,
d3dpp.BackBufferHeight = 480,
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8,
d3dpp.BackBufferCount = 1,
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE,
d3dpp.MultiSampleQuality = 0,
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD,
d3dpp.hDeviceWindow = 0,
d3dpp.Windowed = TRUE,
d3dpp.EnableAutoDepthStencil = FALSE,
d3dpp.AutoDepthStencilFormat = D3DFMT_UNKNOWN,
d3dpp.Flags = 0,
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT,
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE
};
BITMAPINFO256 bmi = {
bmi.h.biSize = sizeof(BITMAPINFOHEADER),
bmi.h.biWidth = 640,
bmi.h.biHeight = 0 - 480, // origin top-left
bmi.h.biPlanes = 1,
bmi.h.biBitCount = 8,
bmi.h.biCompression = BI_RGB,
bmi.h.biSizeImage = 0,
bmi.h.biXPelsPerMeter = 0,
bmi.h.biYPelsPerMeter = 0,
bmi.h.biClrUsed = 0,
bmi.h.biClrImportant = 0
};
constexpr DWORD custom_fvf = D3DFVF_XYZRHW | D3DFVF_TEX1;
struct CUSTOMVERTEX
{
float x;
float y;
float z;
float rhw;
float u;
float v;
};
constexpr std::array<CUSTOMVERTEX, 4> vertices =
{{
{ 0.0f - 0.5f, 480.0f - 0.5f, 0.0f, 1.0f, 0.0f, 480.0f / 1024.0f },
{ 0.0f - 0.5f, 0.0f - 0.5f, 0.0f, 1.0f, 0.0f, 0.0f },
{ 640.0f - 0.5f, 480.0f - 0.5f, 0.0f, 1.0f, 640.0f / 1024.0f, 480.0f / 1024.0f },
{ 640.0f - 0.5f, 0.0f - 0.5f, 0.0f, 1.0f, 640.0f / 1024.0f, 0.0f }
}};
////////////////////////////////////////////////////////////////////////////////
HRESULT lock(LONG* pitch, void** surf_bits)
{
if (SDlgDialog_count == 0)
{
D3DLOCKED_RECT lock_rc = {};
const RECT rc = { 0, 0, 640, 480 };
HRESULT hr = d3dtex->LockRect(0, &lock_rc, &rc, 0);
*pitch = lock_rc.Pitch;
*surf_bits = lock_rc.pBits;
return hr;
}
*pitch = 640;
*surf_bits = dib_bits;
if (import_gdi_bits == FALSE)
{
WORD key_state = GetKeyState(VK_SNAPSHOT);
// if_not (current_toggle_state ^ is_key_down ^ pev_toggle_state) ret;
if (!((key_state & 1) ^ (key_state >> 15) ^ prtscn_toggle))
return 0;
// else update prev state then fall thru to import gdi bits for screenshot
prtscn_toggle ^= 1;
}
// assumes SDlgDialog_cache is sorted by z-order...(it is sorted by age not z-order)
// color converts from 32bpp to 8bpp... ( slow, is gdi trust worthy for this? )
for (DWORD i = 0; i < SDlgDialog_count; i++)
{
SDLGDIALOG_CACHE_ENTRY ce = SDlgDialog_cache[i];
HDC hdc = GetDC(ce.hwnd);
BitBlt(hdc_offscreen, ce.x, ce.y, ce.cx, ce.cy, hdc, 0, 0, SRCCOPY);
ReleaseDC(ce.hwnd, hdc);
}
GdiFlush();
return 0;
}
void unlock(void* surface)
{
if (surface != dib_bits)
{
d3dtex->UnlockRect(0);
d3ddev->BeginScene();
d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
d3ddev->EndScene();
if (FAILED(d3ddev->Present(nullptr, nullptr, nullptr, nullptr)))
d3d_reset();
return;
}
D3DLOCKED_RECT lock_rc = {};
RECT rc = { 0, 0, 640, 480 };
if (SUCCEEDED(d3dtex->LockRect(0, &lock_rc, &rc, 0)))
{
HDC hdc = nullptr;
SDLGDIALOG_CACHE_ENTRY* ce = nullptr;
if (import_gdi_bits != FALSE)
{
for (DWORD i = 0; i < SDlgDialog_count; i++)
{
ce = &SDlgDialog_cache[i];
hdc = GetDCEx(ce->hwnd, nullptr, DCX_PARENTCLIP | DCX_CACHE);
BitBlt(hdc, 0, 0, ce->cx, ce->cy, hdc_offscreen, ce->x, ce->y, SRCCOPY);
ReleaseDC(ce->hwnd, hdc);
}
GdiFlush();
for (int j = 0; j < 480; j++)
{ // for each scanline
color_convert(&(dib_bits[j * 640]), bmi.palette, static_cast<DWORD*>(lock_rc.pBits) + (lock_rc.Pitch * j), 640 / 4);
}
}
else
{
for (DWORD k = 0; k < SDlgDialog_count; k++)
{
ce = &SDlgDialog_cache[k];
hdc = GetDCEx(ce->hwnd, nullptr, DCX_PARENTCLIP | DCX_CACHE);
GdiTransparentBlt(hdc, 0, 0, ce->cx, ce->cy,
hdc_offscreen, ce->x, ce->y, ce->cx, ce->cy, clear_color);
ReleaseDC(ce->hwnd, hdc);
}
GdiFlush();
multiblt(lock_rc.Pitch, static_cast<DWORD*>(lock_rc.pBits));
}
return unlock(lock_rc.pBits);
}
}
void cleanup()
{
return; // todo
}
void set_palette(PALETTEENTRY* colors)
{
// not to speed critical here...
// so don't diff between "in-game" and "bnet menu" modes
for (DWORD i = 0; i < 256; i++)
{ // convert 0xFFBBGGRR to 0x00RRGGBB ( X8R8G8B8 )
// speed is unknown
*(reinterpret_cast<DWORD*>(&bmi.palette[i])) = _byteswap_ulong(*reinterpret_cast<DWORD*>(&colors[i])) >> 8;
}
D3DLOCKED_RECT lock_rc = {};
const RECT rc = { 0, 0, 256, 1 };
if (SUCCEEDED(d3dpal->LockRect(0, &lock_rc, &rc, 0)))
{
std::memcpy(lock_rc.pBits, bmi.palette, 4 * 256);
d3dpal->UnlockRect(0);
}
clear_color = *(reinterpret_cast<DWORD*>(&colors[254])) & 0x00FFFFFF;
SetDIBColorTable(hdc_offscreen, 0, 256, bmi.palette);
// animate palette
unlock(nullptr);
}
HRESULT init(HWND hwnd)
{
hwnd_main = hwnd;
d3dpp.hDeviceWindow = hwnd;
if (!IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE))
{
import_gdi_bits = TRUE; // sse2 not supported use only gdi instead
}
// create dib_section ( offscreen gdi drawing surface )
HBITMAP hbmp = CreateDIBSection(nullptr, reinterpret_cast<const BITMAPINFO*>(&bmi), DIB_RGB_COLORS, reinterpret_cast<void**>(&dib_bits), nullptr, 0);
if (hbmp == nullptr)
{
MessageBoxW(nullptr, L"CreateDIBSection() failed", L"Aqrit's ddraw.dll", MB_OK | MB_ICONSTOP);
return E_FAIL;
}
hdc_offscreen = CreateCompatibleDC(nullptr);
hbmp_old = static_cast<HBITMAP>(SelectObject(hdc_offscreen, hbmp));
if (hbmp_old == nullptr || hbmp_old == HGDI_ERROR)
{
MessageBoxW(nullptr, L"CreateDIBSection() failed", L"Aqrit's ddraw.dll", MB_OK | MB_ICONSTOP);
return E_FAIL;
}
HookWndProcs(hwnd);
// needed on bnet screens
// because the sibling windows are not scaled to new resolutions.
set_resolution();
// the window size is originally the size of the desktop...
SetWindowPos(hwnd, HWND_TOP, 0, 0, 640, 480, SWP_NOOWNERZORDER | SWP_NOZORDER);
// init d3d
// must not be exclusive, to keep gdi drawing visible
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d == nullptr)
{
MessageBoxW(nullptr, L"Direct3DCreate9() failed", L"Aqrit's ddraw.dll", MB_OK | MB_ICONSTOP);
return E_FAIL;
}
HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_NOWINDOWCHANGES | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &d3ddev);
auto d3derror = [=](std::wstring fn)
{
std::vector<wchar_t> description(256);
DXGetErrorDescriptionW(hr, description.data(), description.size());
MessageBoxW(nullptr, std::wstring(fn + L" failed: " + DXGetErrorStringW(hr) + L"\nDescription: "
+ std::wstring(description.begin(), description.end())).c_str(), L"Aqrit's ddraw.dll", MB_OK | MB_ICONSTOP);
};
if (FAILED(hr))
{
// back up in case graphics card does not support D3DCREATE_HARDWARE_VERTEXPROCESSING and D3DCREATE_PUREDEVICE
hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_NOWINDOWCHANGES | D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &d3ddev);
if (FAILED(hr))
{
d3derror(L"d3d->CreateDevice()");
return E_FAIL;
}
}
hr = d3ddev->SetFVF(custom_fvf);
if (FAILED(hr))
{
d3derror(L"d3ddev->SetFVF()");
return E_FAIL;
}
hr = d3ddev->CreateVertexBuffer(sizeof(vertices), 0, custom_fvf, D3DPOOL_MANAGED, &d3dvb, nullptr);
if (FAILED(hr))
{
d3derror(L"d3ddev->CreateVertexBuffer()");
return E_FAIL;
}
void* vb = nullptr;
hr = d3dvb->Lock(0, 0, reinterpret_cast<void**>(&vb), 0);
if (FAILED(hr))
{
d3derror(L"d3dvb->Lock()");
return E_FAIL;
}
std::memcpy(vb, vertices.data(), sizeof(vertices));
hr = d3dvb->Unlock();
if (FAILED(hr))
{
d3derror(L"d3dvb->Unlock()");
return E_FAIL;
}
hr = d3ddev->SetStreamSource(0, d3dvb, 0, sizeof(CUSTOMVERTEX));
if (FAILED(hr))
{
d3derror(L"d3ddev->SetStreamSource()");
return E_FAIL;
}
// todo: NPOT if supported
hr = d3ddev->CreateTexture(1024, 1024, 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, &d3dtex, nullptr);
if (FAILED(hr))
{
d3derror(L"d3ddev->CreateTexture()");
return E_FAIL;
}
hr = d3ddev->SetTexture(0, d3dtex);
if (FAILED(hr))
{
d3derror(L"d3ddev->SetTexture()");
return E_FAIL;
}
// todo: NPOT if supported
hr = d3ddev->CreateTexture(256, 256, 1, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &d3dpal, 0);
if (FAILED(hr))
{
d3derror(L"d3ddev->CreateTexture()");
return E_FAIL;
}
hr = d3ddev->SetTexture(1, d3dpal);
if (FAILED(hr))
{
d3derror(L"d3ddev->SetTexture()");
return E_FAIL;
}
hr = d3ddev->CreatePixelShader((DWORD*)g_ps20_main, &shader);
if (FAILED(hr))
{
d3derror(L"d3ddev->CreatePixelShader()");
return E_FAIL;
}
hr = d3ddev->SetPixelShader(shader);
if (FAILED(hr))
{
d3derror(L"d3ddev->SetPixelShader()");
return E_FAIL;
}
// ...?
d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
d3ddev->SetRenderState(D3DRS_COLORVERTEX, FALSE);
d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
d3ddev->SetRenderState(D3DRS_ZENABLE, FALSE);
// ...?
d3ddev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
d3ddev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
d3ddev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
// success
return S_OK;
}
/**
* Sets resolution to 640x480 by default
*/
bool set_resolution(int width/*=640*/, int height/*=480*/)
{
// FIXME: Using DEVMODEW and ChangeDisplaySettingsW() causes BW to close when connecting to bnet
DEVMODEA devMode = {};
devMode.dmSize = sizeof(devMode);
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
devMode.dmPelsWidth = width;
devMode.dmPelsHeight = height;
return ChangeDisplaySettingsA(&devMode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL;
}
void d3d_reset()
{
if (SUCCEEDED(d3ddev->Reset(&d3dpp)))
{
d3ddev->SetFVF(custom_fvf);
d3ddev->SetStreamSource(0, d3dvb, 0, sizeof(CUSTOMVERTEX));
d3ddev->SetTexture(0, d3dtex);
d3ddev->SetTexture(1, d3dpal);
d3ddev->SetPixelShader(shader);
// ...?
d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);
d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
d3ddev->SetRenderState(D3DRS_COLORVERTEX, FALSE);
d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
d3ddev->SetRenderState(D3DRS_ZENABLE, FALSE);
// ...?
d3ddev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
d3ddev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
d3ddev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
void* vb = nullptr;
if (SUCCEEDED(d3dvb->Lock(0, 0, reinterpret_cast<void**>(&vb), 0)))
{
std::memcpy(vb, vertices.data(), sizeof(vertices));
d3dvb->Unlock();
}
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment