feat: Add support for Studio single-room dumps in RoomZipImportController and update ImportRoom UI

This commit is contained in:
Alexa
2026-06-25 15:25:58 +03:00
parent c8a71afd3d
commit 00e73875bf
2 changed files with 18 additions and 0 deletions
@@ -140,6 +140,14 @@ public class RoomZipImportController(
{ {
PropertyNameCaseInsensitive = true, PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
// RecNet serializes large snowflake-style IDs (SubRoomId, save IDs)
// as JSON strings to avoid JS 2^53 precision loss, while smaller/older
// IDs stay as raw numbers — and a single SubRooms[] array can mix both
// forms (Showdown's export is numeric for entries 0-17, then string
// from "SD_Archive_120822_Catalyst" on). Allow numeric properties to
// read from either token so the import doesn't crash on the first
// string-encoded ID.
NumberHandling = JsonNumberHandling.AllowReadingFromString,
}; };
// ── RoomDetails.json DTOs ────────────────────────────────────────── // ── RoomDetails.json DTOs ──────────────────────────────────────────
@@ -95,6 +95,9 @@ interface PreviewRoom {
/// release window — see CLIENT_BUILD_CUTOFF. /// release window — see CLIENT_BUILD_CUTOFF.
latestModifiedAt: string | null; latestModifiedAt: string | null;
postBuildCutoff: boolean; postBuildCutoff: boolean;
/// True when this room came from a Studio single-room dump (room.json +
/// SubRooms/<scene>/ saves) rather than a standard RoomDetails.json export.
isStudio: boolean;
} }
interface PreviewSubroom { interface PreviewSubroom {
@@ -545,6 +548,11 @@ export function ImportRoom() {
<span className="font-medium text-ink-50">{r.name}</span> <span className="font-medium text-ink-50">{r.name}</span>
</label> </label>
<span className="text-xs text-ink-500 font-mono">{r.folder.split('/').pop()}</span> <span className="text-xs text-ink-500 font-mono">{r.folder.split('/').pop()}</span>
{r.isStudio && (
<span className="badge-online" title="Imported from a RR Studio single-room dump (room.json + SubRooms/<scene>/ saves).">
Studio
</span>
)}
{!r.hasImage && <span className="badge-junior">no image</span>} {!r.hasImage && <span className="badge-junior">no image</span>}
{r.postBuildCutoff && ( {r.postBuildCutoff && (
<span <span
@@ -1088,6 +1096,7 @@ async function parseZipPreview(file: File, onProgress?: (percent: number) => voi
promoImageCount, promoImageCount,
latestModifiedAt, latestModifiedAt,
postBuildCutoff, postBuildCutoff,
isStudio: false,
}); });
bump(); bump();
} }
@@ -1213,6 +1222,7 @@ async function parseZipPreview(file: File, onProgress?: (percent: number) => voi
promoImageCount, promoImageCount,
latestModifiedAt, latestModifiedAt,
postBuildCutoff, postBuildCutoff,
isStudio: true,
}); });
bump(); bump();
} }