snes9x/external/imgui/snes9x_imgui.cpp

369 lines
11 KiB
C++

#include "snes9x_imgui.h"
#include "snes9x_imgui_noto.h"
#include "imgui.h"
#include <cstdint>
#include <string>
#include <array>
#include "snes9x.h"
#include "port.h"
#include "controls.h"
#include "movie.h"
#include "gfx.h"
#include "ppu.h"
#include "cheats.h"
namespace
{
S9xImGuiInitInfo settings;
} // anonymous
static void ImGui_DrawPressedKeys(int spacing)
{
const std::array<const char *, 15> keynames =
{ " ", " ", " ", "R", "L", "X", "A", "", "", "", "", "St", "Sel", "Y", "B" };
const std::array<int, 12> keyorder =
{ 10, 9, 8, 7, 6, 14, 13, 5, 4, 3, 11, 12 }; // < ^ > v A B Y X L R S s
enum controllers controller;
int num_lines = 0;
int cell_width = ImGui::CalcTextSize("").x;
int8_t ids[4];
std::string final_string;
auto draw_list = ImGui::GetForegroundDrawList();
for (int port = 0; port < 2; port++)
{
S9xGetController(port, &controller, &ids[0], &ids[1], &ids[2], &ids[3]);
if (controller == CTL_JOYPAD || controller == CTL_MOUSE)
num_lines++;
}
if (num_lines == 0)
return;
for (int port = 0; port < 2; port++)
{
S9xGetController(port, &controller, &ids[0], &ids[1], &ids[2], &ids[3]);
switch (controller)
{
case CTL_MOUSE: {
uint8_t buf[5];
char string[256];
if (!MovieGetMouse(port, buf))
break;
int16_t mouse_x = READ_WORD(buf);
int16_t mouse_y = READ_WORD(buf + 2);
uint8_t buttons = buf[4];
sprintf(string, "#%d %d: (%03d,%03d) %c%c", port + 1, ids[0] + 1, mouse_x, mouse_y,
(buttons & 0x40) ? 'L' : ' ', (buttons & 0x80) ? 'R' : ' ');
auto string_size = ImGui::CalcTextSize(string);
int box_width = 2 * spacing + string_size.x;
int box_height = 2 * spacing + string_size.y;
int x = (ImGui::GetIO().DisplaySize.x - box_width) / 2;
int y = ImGui::GetIO().DisplaySize.y - (spacing + box_height) * num_lines;
draw_list->AddRectFilled(ImVec2(x, y),
ImVec2(x + box_width, y + box_height),
settings.box_color,
spacing / 2.0f);
draw_list->AddText(ImVec2(x + spacing, y + spacing), settings.text_color, string);
break;
}
case CTL_JOYPAD: {
std::string prefix = "#" + std::to_string(port + 1) + " ";
auto prefix_size = ImGui::CalcTextSize(prefix.c_str());
int box_width = 2 * spacing + prefix_size.x + cell_width * keyorder.size();
int box_height = 2 * spacing + prefix_size.y;
int x = (ImGui::GetIO().DisplaySize.x - box_width) / 2;
int y = ImGui::GetIO().DisplaySize.y - (spacing + box_height) * num_lines;
draw_list->AddRectFilled(ImVec2(x, y),
ImVec2(x + box_width, y + box_height),
settings.box_color,
spacing / 2.0f);
x += spacing;
y += spacing;
draw_list->AddText(ImVec2(x, y), settings.text_color, prefix.c_str());
x += prefix_size.x;
uint16 pad = MovieGetJoypad(ids[0]);
for (size_t i = 0; i < keyorder.size(); i++)
{
int j = keyorder[i];
int mask = (1 << (j + 1));
auto color = (pad & mask) ? settings.text_color : settings.inactive_text_color;
draw_list->AddText(ImVec2(x, y), color, keynames[j]);
x += cell_width;
}
num_lines--;
break;
}
default:
break;
}
}
}
static void ImGui_GetWatchesText(std::string& osd_text)
{
for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
{
if (!watches[i].on)
break;
int32 displayNumber = 0;
char buf[64];
for (int r = 0; r < watches[i].size; r++)
displayNumber += (Cheat.CWatchRAM[(watches[i].address - 0x7E0000) + r]) << (8 * r);
if (watches[i].format == 1)
sprintf(buf, "%s,%du = %u", watches[i].desc, watches[i].size, (unsigned int)displayNumber);
else
if (watches[i].format == 3)
sprintf(buf, "%s,%dx = %X", watches[i].desc, watches[i].size, (unsigned int)displayNumber);
else // signed
{
if (watches[i].size == 1)
displayNumber = (int32)((int8)displayNumber);
else if (watches[i].size == 2)
displayNumber = (int32)((int16)displayNumber);
else if (watches[i].size == 3)
if (displayNumber >= 8388608)
displayNumber -= 16777216;
sprintf(buf, "%s,%ds = %d", watches[i].desc, watches[i].size, (int)displayNumber);
}
osd_text += buf;
osd_text += '\n';
}
}
static void ImGui_DrawTextOverlay(const char *text,
int x, int y,
int padding,
ImGui::DrawTextAlignment halign = ImGui::DrawTextAlignment::BEGIN,
ImGui::DrawTextAlignment valign = ImGui::DrawTextAlignment::BEGIN,
int wrap_at = 0)
{
auto text_size = ImGui::CalcTextSize(text, nullptr, false, wrap_at);
auto box_size = ImVec2(text_size.x + padding * 2, text_size.y + padding * 2);
auto draw_list = ImGui::GetForegroundDrawList();
if (halign == ImGui::DrawTextAlignment::END)
x = x - box_size.x;
else if (halign == ImGui::DrawTextAlignment::CENTER)
x = x - box_size.x / 2;
if (valign == ImGui::DrawTextAlignment::END)
y = y - box_size.y;
draw_list->AddRectFilled(ImVec2(x, y),
ImVec2(x + box_size.x, y + box_size.y),
settings.box_color,
settings.spacing / 2.0f);
draw_list->AddText(nullptr, 0.0f, ImVec2(x + padding, y + padding), settings.text_color, text, nullptr, wrap_at);
}
static std::string sjis_to_utf8(std::string in)
{
std::string out;
for (const auto &i : in)
{
unsigned char c = i;
if (c > 160 && c < 192)
{
out += "\357\275";
out += c;
}
else if (c >= 192)
{
out += "\357\276";
c -= 0x40;
out += c;
}
else
out += c;
}
return out;
}
bool S9xImGuiDraw(int width, int height)
{
if (Memory.ROMFilename.empty())
return false;
if (!ImGui::GetCurrentContext())
return false;
ImGui::GetIO().DisplaySize.x = width;
ImGui::GetIO().DisplaySize.y = height;
ImGui::GetIO().DisplayFramebufferScale.x = 1.0;
ImGui::GetIO().DisplayFramebufferScale.y = 1.0;
ImGui::NewFrame();
if (Settings.DisplayTime)
{
char string[256];
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
sprintf(string, "%02u:%02u", timeinfo->tm_hour, timeinfo->tm_min);
ImGui_DrawTextOverlay(string,
width - settings.spacing,
height - settings.spacing,
settings.spacing,
ImGui::DrawTextAlignment::END,
ImGui::DrawTextAlignment::END);
}
if (Settings.DisplayFrameRate)
{
char string[256];
static uint32 lastFrameCount = 0, calcFps = 0;
static time_t lastTime = time(NULL);
time_t currTime = time(NULL);
if (lastTime != currTime)
{
if (lastFrameCount < IPPU.TotalEmulatedFrames)
{
calcFps = (IPPU.TotalEmulatedFrames - lastFrameCount) / (uint32)(currTime - lastTime);
}
lastTime = currTime;
lastFrameCount = IPPU.TotalEmulatedFrames;
}
sprintf(string, "%u fps\n%02d/%02d", calcFps, (int)IPPU.DisplayedRenderedFrameCount, (int)Memory.ROMFramesPerSecond);
ImGui_DrawTextOverlay(string,
width - settings.spacing,
settings.spacing,
settings.spacing,
ImGui::DrawTextAlignment::END,
ImGui::DrawTextAlignment::BEGIN);
}
if (Settings.DisplayPressedKeys)
{
ImGui_DrawPressedKeys(settings.spacing / 2);
}
if (Settings.DisplayIndicators)
{
if (Settings.Paused || Settings.ForcedPause)
{
ImGui_DrawTextOverlay("❚❚",
settings.spacing,
settings.spacing,
settings.spacing);
}
else if (Settings.TurboMode)
{
ImGui_DrawTextOverlay("▶▶",
settings.spacing,
settings.spacing,
settings.spacing);
}
}
std::string utf8_message;
if (Settings.DisplayWatchedAddresses)
{
ImGui_GetWatchesText(utf8_message);
}
if (!GFX.InfoString.empty())
{
utf8_message += sjis_to_utf8(GFX.InfoString);
}
if (Settings.DisplayMovieFrame && S9xMovieActive())
{
// move movie frame count into its own line if info message is active and not already a newline at end
if (!utf8_message.empty() && utf8_message.back() != '\n')
{
utf8_message += '\n';
}
utf8_message += GFX.FrameDisplayString;
}
if (!utf8_message.empty())
{
ImGui_DrawTextOverlay(utf8_message.c_str(),
settings.spacing,
height - settings.spacing,
settings.spacing,
ImGui::DrawTextAlignment::BEGIN,
ImGui::DrawTextAlignment::END,
width - settings.spacing * 4);
}
ImGui::Render();
return true;
}
bool S9xImGuiRunning()
{
if (ImGui::GetCurrentContext())
return true;
return false;
}
void S9xImGuiDeinit()
{
if (S9xImGuiRunning())
ImGui::DestroyContext();
}
S9xImGuiInitInfo S9xImGuiGetDefaults()
{
return { 24, 10, 0x88000000, 0xffffffff, 0x44ffffff };
}
void S9xImGuiInit(S9xImGuiInitInfo *init_info)
{
static ImVector<ImWchar> ranges;
if (ImGui::GetCurrentContext())
return;
if (init_info)
{
::settings = *init_info;
}
else
{
settings = S9xImGuiGetDefaults();
}
ImGui::CreateContext();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouse;
ImGui::StyleColorsLight();
ImFontGlyphRangesBuilder builder;
builder.Clear();
builder.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesDefault());
builder.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesJapanese());
builder.AddText("←↑→↓▶❚");
ranges.clear();
builder.BuildRanges(&ranges);
ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(imgui_noto_font_compressed_data, imgui_noto_font_compressed_size, settings.font_size, nullptr, ranges.Data);
}