Created
August 10, 2016 22:52
-
-
Save RElesgoe/5ff182c6bd0fe1fabaf9552e65bdd9bc to your computer and use it in GitHub Desktop.
Modification of Aqrit's ddraw.dll by xboi209
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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