200 if (ent.id == state_id) {
206 return absl::NotFoundError(
"State not found: " + state_id);
210 const char* home = std::getenv(
"HOME");
211 std::string socket_arg;
213 std::string path =
client_->GetSocketPath();
215 escaped.reserve(path.size() + 2);
217 for (
char c : path) {
218 if (c ==
'\\' || c ==
'"')
223 socket_arg =
" --socket " + escaped;
225 std::string cmd = absl::StrFormat(
226 "python3 %s/src/hobby/oracle-of-secrets/scripts/mesen2_client.py%s "
228 home ? home :
"", socket_arg, state_id.c_str());
230 FILE* pipe = popen(cmd.c_str(),
"r");
232 return absl::InternalError(
"Failed to execute lib-load command");
237 while (fgets(buffer,
sizeof(buffer), pipe)) {
240 int ret = pclose(pipe);
243 return absl::InternalError(
"lib-load failed: " + output);
248 return absl::OkStatus();
326 ImGui::SetNextItemWidth(150);
327 ImGui::InputTextWithHint(
"##filter",
"Filter...",
filter_text_,
339 int canon_count = 0, draft_count = 0, depr_count = 0;
341 if (e.status ==
"canon")
343 else if (e.status ==
"draft")
345 else if (e.status ==
"deprecated")
349 ImGui::TextDisabled(
"| %d canon, %d draft, %d deprecated", canon_count,
350 draft_count, depr_count);
355 std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(),
358 int visible_index = 0;
359 for (
size_t i = 0; i <
entries_.size(); ++i) {
371 if (!filter_lower.empty()) {
372 std::string label_lower = entry.label;
373 std::transform(label_lower.begin(), label_lower.end(),
374 label_lower.begin(), ::tolower);
375 std::string id_lower = entry.id;
376 std::transform(id_lower.begin(), id_lower.end(), id_lower.begin(),
378 if (label_lower.find(filter_lower) == std::string::npos &&
379 id_lower.find(filter_lower) == std::string::npos) {
385 ImGui::TextColored(GetStatusColor(entry.status),
"%s",
386 GetStatusBadge(entry.status).c_str());
391 if (ImGui::Selectable(entry.label.c_str(), is_selected,
392 ImGuiSelectableFlags_SpanAllColumns)) {
397 if (ImGui::BeginPopupContextItem()) {
405 if (entry.status ==
"draft") {
411 if (entry.status !=
"deprecated") {
421 if (ImGui::IsItemHovered() && !entry.tags.empty()) {
422 ImGui::BeginTooltip();
423 ImGui::Text(
"Tags: ");
424 for (
size_t t = 0; t < entry.tags.size(); ++t) {
427 ImGui::TextColored(ImVec4(0.6f, 0.8f, 1.0f, 1.0f),
"[%s]",
428 entry.tags[t].c_str());
440 ImGui::TextDisabled(
"Select a state to view details");
447 ImGui::TextColored(GetStatusColor(entry.status),
"%s",
448 GetStatusBadge(entry.status).c_str());
450 ImGui::Text(
"%s", entry.label.c_str());
456 ImGui::TextDisabled(
"Connect to Mesen2 to load states");
467 if (entry.status ==
"draft") {
468 if (ImGui::Button(
ICON_MD_CHECK " Verify as Canon", ImVec2(-1, 0))) {
474 if (entry.status !=
"deprecated") {
485 ImGui::Text(
"ID: %s", entry.id.c_str());
486 ImGui::Text(
"Path: %s", entry.path.c_str());
488 if (!entry.md5.empty()) {
489 ImGui::Text(
"MD5: %s", entry.md5.substr(0, 16).c_str());
492 if (!entry.location.empty()) {
493 ImGui::Text(
"Location: %s", entry.location.c_str());
496 if (entry.area > 0 || entry.link_x > 0) {
497 ImGui::Text(
"Area: 0x%02X Pos: (%d, %d)", entry.area, entry.link_x,
501 if (entry.health > 0) {
503 static_cast<float>(entry.health) / std::max(1, entry.max_health);
504 ImGui::Text(
"Health: %d/%d", entry.health, entry.max_health);
505 ImGui::ProgressBar(ratio, ImVec2(-1, 0));
508 if (!entry.captured_by.empty()) {
509 ImGui::Text(
"Captured by: %s", entry.captured_by.c_str());
512 if (!entry.verified_by.empty()) {
513 ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f),
"Verified by: %s",
514 entry.verified_by.c_str());
515 if (!entry.verified_at.empty()) {
516 ImGui::TextDisabled(
"at %s", entry.verified_at.c_str());
520 if (!entry.deprecated_reason.empty()) {
521 ImGui::TextColored(ImVec4(0.8f, 0.3f, 0.3f, 1.0f),
"Deprecated: %s",
522 entry.deprecated_reason.c_str());
526 if (!entry.tags.empty()) {
528 ImGui::Text(
"Tags:");
529 for (
const auto& tag : entry.tags) {
531 ImGui::TextColored(ImVec4(0.6f, 0.8f, 1.0f, 1.0f),
"[%s]", tag.c_str());
539 ImGui::OpenPopup(
"Verify State");
542 if (ImGui::BeginPopupModal(
"Verify State",
nullptr,
543 ImGuiWindowFlags_AlwaysAutoResize)) {
545 ImGui::TextDisabled(
"This marks the state as verified and trusted.");
549 if (ImGui::Button(
"Verify", ImVec2(120, 0))) {
556 ImGui::CloseCurrentPopup();
559 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
560 ImGui::CloseCurrentPopup();
567 ImGui::OpenPopup(
"Deprecate State");
570 if (ImGui::BeginPopupModal(
"Deprecate State",
nullptr,
571 ImGuiWindowFlags_AlwaysAutoResize)) {
573 ImGui::TextDisabled(
"This excludes the state from testing.");
577 if (ImGui::Button(
"Deprecate", ImVec2(120, 0))) {
584 ImGui::CloseCurrentPopup();
587 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
588 ImGui::CloseCurrentPopup();
State entry from the Oracle save state library.
std::vector< std::string > tags
std::string deprecated_reason