#include "Render/DX7/RenderSystemDX7.h" #include "Render/DX7/DeviceOwnerDX7.h" #include "Render/RendererBackBuffer.h" #include "Render/RendererDX7.h" #include "Resource/Bitmap/BitmapImage.h" #include "Resource/Bitmap/BitmapResource.h" #include "Resource/Bitmap/IJL15Runtime.h" #include "Resource/ResourceMgr.h" #include "Resource/TextureNames.h" #include #include #include #include namespace { // INLINED std::uint16_t PackedColor(const DeviceOwnerDX7* owner, unsigned color) { if (!owner) { return 0; } const auto channel = [](unsigned value, const DeviceOwnerDX7::Channel& field) { return static_cast((value >> (field.expansion & 31)) << (field.shift & 31)); }; return static_cast(channel((color >> 16) & 255, owner->red) + channel((color >> 8) & 255, owner->green) + channel(color & 255, owner->blue)); } // INLINED unsigned char ExpandedChannel(unsigned value, DWORD mask, const DeviceOwnerDX7::Channel& field) { const auto component = static_cast((value & mask) >> (field.shift & 31)); return static_cast(static_cast(component) << (field.expansion & 31)); } } // OFFSET: 0x0055D860 // FUNCTION: fn_02109 bool CRendererDX7::SaveBitmap(const char* filename, CBitmapRes* overlay) { if (!this->LockBackBuffer()) { return false; } std::FILE* file = std::fopen(filename, "wb"); if (!file) { return false; } const int width = this->viewport.width; const int height = this->viewport.height; BITMAPFILEHEADER header{}; header.bfType = 0x4D42; header.bfSize = 3u * (static_cast(width) * static_cast(height) + 18u); header.bfOffBits = 54; std::fwrite(&header, sizeof(header), 1, file); BITMAPINFOHEADER information{}; information.biSize = sizeof(information); information.biWidth = width; information.biHeight = height; information.biPlanes = 1; information.biBitCount = 24; std::fwrite(&information, sizeof(information), 1, file); auto* owner = g_deviceOwnerDX7; const auto red = owner ? owner->red : DeviceOwnerDX7::Channel{0, 8}; const auto green = owner ? owner->green : DeviceOwnerDX7::Channel{0, 8}; const auto blue = owner ? owner->blue : DeviceOwnerDX7::Channel{0, 8}; const auto redMask = owner ? owner->redMask : 0u; const auto greenMask = owner ? owner->greenMask : 0u; const auto blueMask = owner ? owner->blueMask : 0u; const auto transparent = PackedColor(owner, 0xFF00FF); std::array row; for (int y = height - 1; y >= 0; --y) { const auto* source = static_cast(this->backBuffer.pixels) + y * this->backBuffer.pitch; for (int x = 0; x < width; ++x) { unsigned color = 0; const int overlayX = x + overlay->image.width - width; const int overlayY = y + overlay->image.height - height; bool useOverlay = overlayX >= 0 && overlayX < overlay->image.width && overlayY >= 0 && overlayY < overlay->image.height; if (useOverlay) { color = overlay->image.pixels[overlayX + overlayY * overlay->image.width]; useOverlay = this->backBuffer.bitsPerPixel == 16 ? PackedColor(owner, color) != transparent : (color & 0xFFFFFF) != 0xFF00FF; } if (!useOverlay) { if (this->backBuffer.bitsPerPixel == 16) { std::uint16_t pixel; std::memcpy(&pixel, source + 2 * x, sizeof(pixel)); color = (static_cast(ExpandedChannel(pixel, redMask, red)) << 16) | (static_cast(ExpandedChannel(pixel, greenMask, green)) << 8) | ExpandedChannel(pixel, blueMask, blue); } else { std::memcpy(&color, source + 4 * x, sizeof(color)); } } row[3 * x] = static_cast(color); row[3 * x + 1] = static_cast(color >> 8); row[3 * x + 2] = static_cast(color >> 16); } std::fwrite(row.data(), 3u * static_cast(width), 1, file); owner = g_deviceOwnerDX7; } this->UnlockBackBuffer(); std::fclose(file); return true; } // OFFSET: 0x0055DDF0 // FUNCTION: fn_02110 bool CRendererDX7::SaveJpeg( const char* filename, CBitmapRes* overlay ) { if (!this->LockBackBuffer()) { return false; } const int width = this->viewport.width; const int height = this->viewport.height; const unsigned size = 3u * static_cast(width) * static_cast(height); auto* pixels = static_cast(std::malloc(size)); auto* owner = g_deviceOwnerDX7; const auto red = owner ? owner->red : DeviceOwnerDX7::Channel{0, 8}; const auto green = owner ? owner->green : DeviceOwnerDX7::Channel{0, 8}; const auto blue = owner ? owner->blue : DeviceOwnerDX7::Channel{0, 8}; const auto redMask = owner ? owner->redMask : 0u; const auto greenMask = owner ? owner->greenMask : 0u; const auto blueMask = owner ? owner->blueMask : 0u; const auto transparent = PackedColor(owner, 0xFF00FF); for (int y = height - 1; y >= 0; --y) { const auto* source = static_cast(this->backBuffer.pixels) + y * this->backBuffer.pitch; auto* row = pixels + 3u * static_cast(y) * static_cast(width); for (int x = 0; x < width; ++x) { unsigned color = 0; const int overlayX = x + overlay->image.width - width; const int overlayY = y + overlay->image.height - height; bool useOverlay = overlayX >= 0 && overlayX < overlay->image.width && overlayY >= 0 && overlayY < overlay->image.height; if (useOverlay) { color = overlay->image.pixels[overlayX + overlayY * overlay->image.width]; useOverlay = this->backBuffer.bitsPerPixel == 16 ? PackedColor(owner, color) != transparent : (color & 0xFFFFFF) != 0xFF00FF; } if (!useOverlay) { if (this->backBuffer.bitsPerPixel == 16) { std::uint16_t pixel; std::memcpy(&pixel, source + 2 * x, sizeof(pixel)); color = (static_cast(ExpandedChannel(pixel, redMask, red)) << 16) | (static_cast(ExpandedChannel(pixel, greenMask, green)) << 8) | ExpandedChannel(pixel, blueMask, blue); } else { std::memcpy(&color, source + 4 * x, sizeof(color)); } } row[3 * x] = static_cast(color); row[3 * x + 1] = static_cast(color >> 8); row[3 * x + 2] = static_cast(color >> 16); } owner = g_deviceOwnerDX7; } this->UnlockBackBuffer(); IJL15Properties properties{}; if (ijlInit(&properties) != 0) { return false; } properties.dibWidth = width; properties.dibHeight = height; properties.dibBytes = pixels; properties.dibPadding = ((3u * static_cast(width) + 3u) & ~3u) - 3u * static_cast(width); properties.jpegFile = filename; properties.dibChannels = 3; properties.dibColor = 2; properties.jpegWidth = width; properties.jpegHeight = height; const int status = ijlWrite(&properties, 8); std::free(pixels); return status == 0; } // OFFSET: 0x0055E340 // FUNCTION: fn_02111 bool CRendererDX7::SaveLogo(std::string filename, ResourceMgr& resources) { auto* logo = static_cast(resources.Load(Resolve("scr_logo.bmp"))); if (!logo) { return false; } DDSURFACEDESC2 locked{}; locked.dwSize = sizeof(locked); if (FAILED(g_deviceOwnerDX7->renderTarget->Lock(nullptr, &locked, DDLOCK_WAIT, nullptr))) { return false; } const int pixelBytes = locked.lPitch / static_cast(locked.dwWidth); const int paddedWidth = locked.lPitch / pixelBytes; unsigned remaining = 3u * static_cast(paddedWidth) * locked.dwHeight; auto* pixels = static_cast(std::malloc(remaining)); auto* destination = pixels; const auto& image = logo->image; for (unsigned y = 0; y < locked.dwHeight; ++y) { const auto* source = static_cast(locked.lpSurface) + y * locked.lPitch; for (unsigned x = 0; x < locked.dwWidth && static_cast(remaining) > 0; ++x) { const unsigned overlayX = x + static_cast(image.width) - locked.dwWidth; const unsigned overlayY = y + static_cast(image.height) - locked.dwHeight; const bool inOverlay = x >= locked.dwWidth - static_cast(image.width) && y >= locked.dwHeight - static_cast(image.height); unsigned color = inOverlay ? image.pixels[overlayX + overlayY * image.width] : 0xFF00FF; if (!inOverlay || (color & 0xFFFFFF) == 0xFF00FF) { if (locked.ddpfPixelFormat.dwRGBBitCount == 16) { std::uint16_t value; std::memcpy(&value, source + x * pixelBytes, 2); color = (static_cast(ExpandedChannel(value, g_deviceOwnerDX7->redMask, g_deviceOwnerDX7->red)) << 16) | (static_cast(ExpandedChannel(value, g_deviceOwnerDX7->greenMask, g_deviceOwnerDX7->green)) << 8) | ExpandedChannel(value, g_deviceOwnerDX7->blueMask, g_deviceOwnerDX7->blue); } else { std::memcpy(&color, source + x * pixelBytes, 4); } } destination[0] = static_cast(color); destination[1] = static_cast(color >> 8); destination[2] = static_cast(color >> 16); destination += 3; remaining -= 3; } destination = pixels + 3u * static_cast(paddedWidth) * (y + 1); remaining = 3u * static_cast(paddedWidth) * (locked.dwHeight - y - 1); } g_deviceOwnerDX7->renderTarget->Unlock(nullptr); IJL15Properties properties{}; if (ijlInit(&properties) != 0) { std::free(pixels); return false; } properties.dibBytes = pixels; properties.dibWidth = paddedWidth; properties.dibHeight = locked.dwHeight; properties.dibPadding = ((3u * static_cast(paddedWidth) + 3u) & ~3u) - 3u * static_cast(paddedWidth); properties.jpegFile = filename.c_str(); properties.dibChannels = 3; properties.dibColor = 2; properties.jpegWidth = locked.dwWidth; properties.jpegHeight = locked.dwHeight; const int status = ijlWrite(&properties, 8); std::free(pixels); return status == 0; }