diff --git a/src/app/faq/entries.md b/src/app/faq/entries.md index 7d6700d..c39baea 100644 --- a/src/app/faq/entries.md +++ b/src/app/faq/entries.md @@ -79,9 +79,9 @@ Only the original creator or a member of their team can submit the hack to Hackd Account creation is required for submissions to preserve author control and attribution. This ensures your work is properly credited and you maintain control over your hack's listing. This also allows you to update your hack after submission. ### What format should I submit my hack in? -We accept `.bps` and `.xdelta` patch files, not complete ROMs. Hackdex utilizes a built-in patcher that users apply to their own legally obtained base ROMs. This helps keep the platform safer from potential legal issues. xdelta is the preferred format going forward; legacy BPS patches continue to work. +We recommend providing your modified ROM in the submission form. Hackdex uses your selected base ROM to generate an `.xdelta` patch on your device, and no ROMs are uploaded. Users receive the generated patch, not a complete ROM. -A built-in patcher is also included in the submission form, so you also have the option to provide your modified ROM and the base ROM to generate a `.xdelta` patch file automatically. +If you already have a `.bps` or `.xdelta` patch file, you can upload it as a fallback. Hackdex cannot always guarantee that an uploaded patch is compatible with the chosen base ROM, so auto-generating from your modified ROM is recommended. ### Why only BPS and Xdelta patch files? Both formats support checksum verification so the patch is linked to the correct base ROM. An incorrect base ROM will result in a corrupted game. BPS remains supported for existing hacks; new in-browser patch creation produces Xdelta files, which is the preferred format going forward. diff --git a/src/components/Hack/HackPatchForm.tsx b/src/components/Hack/HackPatchForm.tsx index af0f180..becbd06 100644 --- a/src/components/Hack/HackPatchForm.tsx +++ b/src/components/Hack/HackPatchForm.tsx @@ -10,6 +10,7 @@ import BPS from "rom-patcher-js/rom-patcher-js/modules/RomPatcher.format.bps.js" import { presignNewPatchVersion } from "@/app/hack/actions"; import { confirmPatchUpload } from "@/app/submit/actions"; import { FaInfoCircle } from "react-icons/fa"; +import { FiAlertTriangle } from "react-icons/fi"; import { patchFormatFromFilename } from "@/utils/patching"; import { encodeXdelta, trialDecodeXdelta, friendlyXdeltaError } from "@/utils/patching/xdelta"; @@ -25,7 +26,7 @@ export interface HackPatchFormProps { export default function HackPatchForm(props: HackPatchFormProps) { const { slug, baseRomId, existingVersions, isCustomPatcherActive, currentVersion } = props; const [version, setVersion] = React.useState(""); - const [patchMode, setPatchMode] = React.useState<"bps" | "rom">("bps"); + const [patchMode, setPatchMode] = React.useState<"bps" | "rom">("rom"); const [patchFile, setPatchFile] = React.useState(null); const [genStatus, setGenStatus] = React.useState<"idle" | "generating" | "ready" | "error">("idle"); const [genError, setGenError] = React.useState(""); @@ -290,23 +291,6 @@ export default function HackPatchForm(props: HackPatchFormProps) {
-
- - -
- {patchMode === "bps" && (
-

Upload a .bps or .xdelta patch file.

+

+ + Patch file upload is a fallback. Hackdex cannot always guarantee that an uploaded patch is compatible with the chosen base ROM. Auto-generating from a modified ROM is recommended. +

{checksumStatus === "validating" &&
Validating checksum…
} {checksumStatus === "valid" &&
Checksum valid.
} {checksumStatus === "invalid" && !!checksumError &&
{checksumError}
} {checksumStatus === "unknown" && !!checksumError &&
{checksumError}
} +
)} @@ -338,7 +332,7 @@ export default function HackPatchForm(props: HackPatchFormProps) { )} {baseRomMissing && ( )} @@ -347,20 +341,27 @@ export default function HackPatchForm(props: HackPatchFormProps) {
- +

We'll generate a .xdelta patch on-device. No ROMs are uploaded.

{genStatus === "generating" &&
Generating patch…
} {genStatus === "ready" && patchFile &&
Patch ready: {patchFile.name}
} {genStatus === "error" && !!genError &&
{genError}
}
+
)} diff --git a/src/components/Hack/HackSubmitForm.tsx b/src/components/Hack/HackSubmitForm.tsx index f64c910..976de61 100644 --- a/src/components/Hack/HackSubmitForm.tsx +++ b/src/components/Hack/HackSubmitForm.tsx @@ -14,7 +14,7 @@ import { CSS } from "@dnd-kit/utilities"; import Markdown from "@/components/Markdown/Markdown"; import { RxDragHandleDots2 } from "react-icons/rx"; import { FaDiscord, FaTwitter, FaGithub } from "react-icons/fa6"; -import { FiExternalLink } from "react-icons/fi"; +import { FiAlertTriangle, FiExternalLink } from "react-icons/fi"; import PokeCommunityIcon from "@/components/Icons/PokeCommunityIcon"; import { useAuthContext } from "@/contexts/AuthContext"; import { useBaseRoms } from "@/contexts/BaseRomContext"; @@ -123,7 +123,7 @@ export default function HackSubmitForm({ return initialDraftRef.current?.originalAuthor || ""; }); const [patchFile, setPatchFile] = React.useState(null); - const [patchMode, setPatchMode] = React.useState<"bps" | "rom">(() => (initialDraftRef.current?.patchMode === "rom" ? "rom" : "bps")); + const [patchMode, setPatchMode] = React.useState<"bps" | "rom">(() => (initialDraftRef.current?.patchMode === "bps" ? "bps" : "rom")); const [genStatus, setGenStatus] = React.useState<"idle" | "generating" | "ready" | "error">("idle"); const [checksumStatus, setChecksumStatus] = React.useState<"idle" | "validating" | "valid" | "invalid" | "unknown">("idle"); const [checksumError, setChecksumError] = React.useState(""); @@ -269,7 +269,7 @@ export default function HackSubmitForm({ } else if ((step === 2 && isArchive) || (step === 3 && !isArchive)) { target = screenshotsInputRef.current; } else if (step === 4 && !isArchive) { - target = patchInputRef.current; + target = patchMode === "rom" ? modifiedRomInputRef.current : patchInputRef.current; } if (!target) return; const raf1 = requestAnimationFrame(() => { @@ -280,7 +280,7 @@ export default function HackSubmitForm({ return () => cancelAnimationFrame(raf2); }); return () => cancelAnimationFrame(raf1); - }, [step, isDummy, isHydrating]); + }, [step, isDummy, isHydrating, patchMode]); const slug = slugify(title || ""); @@ -1241,23 +1241,6 @@ https://discord.gg/example`} {!isDummy ? (
-
- - -
- {patchMode === "bps" && (
-

Upload a .bps or .xdelta patch file.

+

+ + Patch file upload is a fallback. Hackdex cannot always guarantee that an uploaded patch is compatible with the chosen base ROM. Auto-generating from a modified ROM is recommended. +

{checksumStatus === "validating" &&
Validating checksum…
} {checksumStatus === "valid" &&
Checksum valid.
} {checksumStatus === "invalid" && !!checksumError &&
{checksumError}
} {checksumStatus === "unknown" && !!checksumError &&
{checksumError}
} +
)} @@ -1289,7 +1282,7 @@ https://discord.gg/example`} )} {baseRomMissing && ( )} @@ -1298,20 +1291,27 @@ https://discord.gg/example`}
- +

We'll generate a .xdelta patch on-device. No ROMs are uploaded.

{genStatus === "generating" &&
Generating patch…
} {genStatus === "ready" && patchFile &&
Patch ready: {patchFile.name}
} {genStatus === "error" && !!genError &&
{genError}
}
+ )} diff --git a/src/components/Hack/VersionActions.tsx b/src/components/Hack/VersionActions.tsx index 83fde20..955e91b 100644 --- a/src/components/Hack/VersionActions.tsx +++ b/src/components/Hack/VersionActions.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useState, useEffect, useRef } from "react"; -import { FiMoreVertical } from "react-icons/fi"; +import { FiAlertTriangle, FiMoreVertical } from "react-icons/fi"; import { FaDownload, FaTrash, @@ -68,7 +68,7 @@ export default function VersionActions({ const [showPublishModal, setShowPublishModal] = useState(false); const [showReuploadModal, setShowReuploadModal] = useState(false); const [actionLoading, setActionLoading] = useState(false); - const [patchMode, setPatchMode] = useState<"bps" | "rom">("bps"); + const [patchMode, setPatchMode] = useState<"bps" | "rom">("rom"); const [reuploadFile, setReuploadFile] = useState(null); const [reuploadError, setReuploadError] = useState(null); const [checksumStatus, setChecksumStatus] = useState<"idle" | "validating" | "valid" | "invalid" | "unknown">("idle"); @@ -397,6 +397,7 @@ export default function VersionActions({ if (confirmResult.ok) { setShowReuploadModal(false); setReuploadFile(null); + setPatchMode("rom"); onActionComplete(); } else { throw new Error(confirmResult.error || "Failed to confirm upload"); @@ -437,6 +438,7 @@ export default function VersionActions({ {/* Mobile: Use dropdown menu */} @@ -557,6 +559,7 @@ export default function VersionActions({ {/* Mobile: Use dropdown menu */} @@ -740,7 +743,7 @@ export default function VersionActions({ setGenStatus("idle"); setGenError(""); setBaseRomFile(null); - setPatchMode("bps"); + setPatchMode("rom"); } }} > @@ -749,23 +752,6 @@ export default function VersionActions({ Provide patch *
-
- - -
- {patchMode === "bps" && (
-

Upload a .bps or .xdelta patch file.

+

+ + Patch file upload is a fallback. Hackdex cannot always guarantee that an uploaded patch is compatible with the chosen base ROM. Auto-generating from a modified ROM is recommended. +

{checksumStatus === "validating" &&
Validating checksum…
} {checksumStatus === "valid" &&
Checksum valid.
} {checksumStatus === "invalid" && !!checksumError &&
{checksumError}
} {checksumStatus === "unknown" && !!checksumError &&
{checksumError}
} +
)} @@ -802,7 +798,7 @@ export default function VersionActions({ type="file" onChange={onUploadBaseRom} accept={baseRomPlatform ? platformAccept(baseRomPlatform) : "*/*"} - className="rounded-md bg-[var(--surface-2)] px-2 py-1 text-xs ring-1 ring-inset ring-[var(--border)]" + className="cursor-pointer rounded-md bg-[var(--surface-2)] px-2 py-1 text-xs italic text-foreground/50 ring-1 ring-inset ring-[var(--border)] file:bg-black/10 dark:file:bg-[var(--surface-2)] file:text-foreground/80 file:text-xs file:font-medium file:not-italic file:rounded-md file:border-0 file:px-2 file:py-1 file:mr-2 file:cursor-pointer" /> Upload base ROM @@ -812,20 +808,27 @@ export default function VersionActions({
- +

We'll generate a .xdelta patch on-device. No ROMs are uploaded.

{genStatus === "generating" &&
Generating patch…
} {genStatus === "ready" && reuploadFile &&
Patch ready: {reuploadFile.name}
} {genStatus === "error" && !!genError &&
{genError}
}
+ )} @@ -851,7 +854,7 @@ export default function VersionActions({ setGenStatus("idle"); setGenError(""); setBaseRomFile(null); - setPatchMode("bps"); + setPatchMode("rom"); }} disabled={actionLoading} className="flex-1 rounded-md border border-[var(--border)] bg-[var(--surface-2)] px-4 py-2 text-sm font-medium hover:bg-[var(--surface-3)] disabled:opacity-50"