feat: xbox360+ps3->windows64 world conversion

This commit is contained in:
neoapps-dev 2026-07-15 23:34:49 +03:00
parent 4a561a0c78
commit aec0307939
5 changed files with 1319 additions and 42 deletions

View file

@ -184,6 +184,7 @@ sudo apt install --reinstall libwebkit2gtk-4.1-0
- **4J Studios & Mojang** - Original creators of Legacy Console Edition
- **The LCE Community** - Research and foundations for LCE on PC
- **Str1k3r** - Original creator of LCEOnline
- **DTention** - Original creator of [LCE-Save-Converter](https://github.com/dtentiion/LCE-Save-Converter)
---

File diff suppressed because it is too large Load diff

View file

@ -108,6 +108,7 @@ pub fn run() {
game::backup_instance,
game::restore_instance,
commands::console2lce::import_world,
commands::console2lce::import_lce_save,
stun::stun_discover,
relay::start_relay_proxy,
relay::start_host_relay,

View file

@ -1,7 +1,6 @@
import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import { TauriService } from "../../services/TauriService";
export default function ImportWorldModal({
isOpen,
onClose,
@ -20,7 +19,6 @@ export default function ImportWorldModal({
const [status, setStatus] = useState("");
const [error, setError] = useState("");
const [isImporting, setIsImporting] = useState(false);
useEffect(() => {
if (!isOpen) {
setStatus("");
@ -29,19 +27,18 @@ export default function ImportWorldModal({
}
}, [isOpen]);
const handleImport = async () => {
const handleImportMs = async () => {
if (!targetInstanceId) return;
playPressSound();
setIsImporting(true);
setError("");
setStatus("Selecting source...");
try {
setStatus("Selecting LCE save folder or .ms file...");
const picked = await TauriService.pickFile(
"Select saveData.ms or GameHDD folder",
["*.ms", "*"],
);
setStatus("Selecting saveData.ms file...");
const picked = await TauriService.pickFile("Select saveData.ms", [
"*.ms",
"*",
]);
if (!picked) {
setIsImporting(false);
return;
@ -64,6 +61,65 @@ export default function ImportWorldModal({
}
};
const handleImportXbox = async () => {
if (!targetInstanceId) return;
playPressSound();
setIsImporting(true);
setError("");
setStatus("Selecting source...");
try {
setStatus("Selecting Xbox 360 save (.bin)...");
const picked = await TauriService.pickFile(
"Select Xbox 360 Minecraft save",
["*.bin", "*"],
);
if (!picked) {
setIsImporting(false);
return;
}
setStatus("Converting Xbox 360 save...");
const instancePath = await TauriService.getInstancePath(targetInstanceId);
const gameHdd = `${instancePath}/Windows64/GameHDD`;
await TauriService.importLceSave(picked, gameHdd);
setStatus(`Xbox 360 save converted into "${targetInstanceName}"!`);
setTimeout(() => {
onClose();
}, 2000);
} catch (e: unknown) {
setError(e instanceof Error ? e.message : String(e));
setStatus("");
setIsImporting(false);
}
};
const handleImportPs3 = async () => {
if (!targetInstanceId) return;
playPressSound();
setIsImporting(true);
setError("");
setStatus("Selecting source...");
try {
setStatus("Selecting PS3 save folder...");
const picked = await TauriService.pickFolder();
if (!picked) {
setIsImporting(false);
return;
}
setStatus("Converting PS3 save...");
const instancePath = await TauriService.getInstancePath(targetInstanceId);
const gameHdd = `${instancePath}/Windows64/GameHDD`;
await TauriService.importLceSave(picked, gameHdd);
setStatus(`PS3 save converted into "${targetInstanceName}"!`);
setTimeout(() => {
onClose();
}, 2000);
} catch (e: unknown) {
setError(e instanceof Error ? e.message : String(e));
setStatus("");
setIsImporting(false);
}
};
const handleKey = (e: KeyboardEvent) => {
if (e.key === "Escape") {
playBackSound();
@ -104,9 +160,49 @@ export default function ImportWorldModal({
Import into:{" "}
<span className="text-[#FFFF55]">{targetInstanceName}</span>
</p>
<p className="text-gray-400 text-xs mc-text-shadow mb-4 text-center">
Select an existing LCE save (.ms file or GameHDD folder)
<p className="text-gray-400 text-xs mc-text-shadow mb-2 text-center">
Import an existing .ms save file:
</p>
<button
onClick={handleImportMs}
className="w-48 h-9 flex items-center justify-center text-sm text-white mc-text-shadow hover:text-[#FFFF55] mb-4"
style={{
backgroundImage: "url('/images/Button_Background.png')",
backgroundSize: "100% 100%",
imageRendering: "pixelated",
}}
>
Select .ms File
</button>
<p className="text-gray-400 text-xs mc-text-shadow mb-2 text-center">
Convert an Xbox 360 or PS3 save:
</p>
<div className="flex gap-3 mb-2">
<button
onClick={handleImportXbox}
className="w-40 h-9 flex items-center justify-center text-sm text-white mc-text-shadow hover:text-[#FFFF55]"
style={{
backgroundImage: "url('/images/Button_Background.png')",
backgroundSize: "100% 100%",
imageRendering: "pixelated",
}}
>
Xbox 360 (.bin)
</button>
<button
onClick={handleImportPs3}
className="w-40 h-9 flex items-center justify-center text-sm text-white mc-text-shadow hover:text-[#FFFF55]"
style={{
backgroundImage: "url('/images/Button_Background.png')",
backgroundSize: "100% 100%",
imageRendering: "pixelated",
}}
>
PS3 (Folder)
</button>
</div>
{error && (
<div className="text-red-500 text-center mc-text-shadow uppercase text-xs tracking-widest mb-3">
@ -114,33 +210,20 @@ export default function ImportWorldModal({
</div>
)}
<div className="flex gap-4 w-full justify-center">
<button
onClick={() => {
playBackSound();
onClose();
}}
className="w-32 h-10 flex items-center justify-center text-xl text-white mc-text-shadow"
style={{
backgroundImage: "url('/images/Button_Background.png')",
backgroundSize: "100% 100%",
imageRendering: "pixelated",
}}
>
Cancel
</button>
<button
onClick={handleImport}
className="w-40 h-10 flex items-center justify-center text-xl text-white mc-text-shadow hover:text-[#FFFF55]"
style={{
backgroundImage: "url('/images/button_highlighted.png')",
backgroundSize: "100% 100%",
imageRendering: "pixelated",
}}
>
Select File
</button>
</div>
<button
onClick={() => {
playBackSound();
onClose();
}}
className="w-32 h-10 flex items-center justify-center text-xl text-white mc-text-shadow mt-2"
style={{
backgroundImage: "url('/images/Button_Background.png')",
backgroundSize: "100% 100%",
imageRendering: "pixelated",
}}
>
Cancel
</button>
</>
) : (
<>

View file

@ -420,4 +420,11 @@ export class TauriService {
): Promise<string> {
return invoke("import_world", { inputPath, outputPath });
}
static async importLceSave(
inputPath: string,
outputDir: string,
): Promise<string> {
return invoke("import_lce_save", { inputPath, outputDir });
}
}