mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-10-19 04:55:19 +00:00
feat: unlink fastfiles via ModMan
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "Binds.h"
|
||||
|
||||
#include "UnlinkingBinds.h"
|
||||
#include "Web/Binds/DialogBinds.h"
|
||||
#include "ZoneBinds.h"
|
||||
|
||||
@@ -8,6 +9,7 @@ namespace ui
|
||||
void RegisterAllBinds(webview::webview& wv)
|
||||
{
|
||||
RegisterDialogHandlerBinds(wv);
|
||||
RegisterUnlinkingBinds(wv);
|
||||
RegisterZoneBinds(wv);
|
||||
}
|
||||
} // namespace ui
|
||||
|
93
src/ModMan/Web/Binds/UnlinkingBinds.cpp
Normal file
93
src/ModMan/Web/Binds/UnlinkingBinds.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "UnlinkingBinds.h"
|
||||
|
||||
#include "Context/ModManContext.h"
|
||||
#include "IObjWriter.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "Utils/PathUtils.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
|
||||
#include "Json/JsonExtension.h"
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ZoneLoadedDto
|
||||
{
|
||||
public:
|
||||
std::string zoneName;
|
||||
std::string filePath;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath);
|
||||
|
||||
class ZoneUnloadedDto
|
||||
{
|
||||
public:
|
||||
std::string zoneName;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName);
|
||||
|
||||
result::Expected<NoResult, std::string> UnlinkZoneInDbThread(const std::string& zoneName)
|
||||
{
|
||||
const auto& context = ModManContext::Get().m_fast_file;
|
||||
const auto existingZone = std::ranges::find_if(context.m_loaded_zones,
|
||||
[&zoneName](const std::unique_ptr<Zone>& zone)
|
||||
{
|
||||
return zone->m_name == zoneName;
|
||||
});
|
||||
|
||||
if (existingZone == context.m_loaded_zones.end())
|
||||
return result::Unexpected(std::format("No zone with name {} loaded", zoneName));
|
||||
|
||||
const auto& zone = *existingZone->get();
|
||||
|
||||
const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id);
|
||||
|
||||
const auto outputFolderPath = fs::path(utils::GetExecutablePath()).parent_path() / "zone_dump" / zoneName;
|
||||
const auto outputFolderPathStr = outputFolderPath.string();
|
||||
|
||||
OutputPathFilesystem outputFolderOutputPath(outputFolderPath);
|
||||
SearchPaths searchPaths;
|
||||
AssetDumpingContext dumpingContext(zone, outputFolderPathStr, outputFolderOutputPath, searchPaths);
|
||||
objWriter->DumpZone(dumpingContext);
|
||||
|
||||
return NoResult();
|
||||
}
|
||||
|
||||
void UnlinkZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety
|
||||
{
|
||||
ModManContext::Get().m_db_thread.Dispatch(
|
||||
[&wv, id, zoneName]
|
||||
{
|
||||
auto result = UnlinkZoneInDbThread(zoneName);
|
||||
|
||||
if (result)
|
||||
{
|
||||
con::debug("Unlinked zone \"{}\"", zoneName);
|
||||
ui::PromiseResolve(wv, id, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
con::warn("Failed to unlink zone \"{}\": {}", zoneName, result.error());
|
||||
ui::PromiseReject(wv, id, std::move(result).error());
|
||||
}
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void RegisterUnlinkingBinds(webview::webview& wv)
|
||||
{
|
||||
BindAsync<std::string>(wv,
|
||||
"unlinkZone",
|
||||
[&wv](const std::string& id, std::string zoneName)
|
||||
{
|
||||
UnlinkZone(wv, id, std::move(zoneName));
|
||||
});
|
||||
}
|
||||
} // namespace ui
|
8
src/ModMan/Web/Binds/UnlinkingBinds.h
Normal file
8
src/ModMan/Web/Binds/UnlinkingBinds.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void RegisterUnlinkingBinds(webview::webview& wv);
|
||||
} // namespace ui
|
@@ -33,7 +33,12 @@ namespace
|
||||
|
||||
if (maybeZone)
|
||||
{
|
||||
ui::PromiseResolve(wv, id, true);
|
||||
ui::PromiseResolve(wv,
|
||||
id,
|
||||
ZoneLoadedDto{
|
||||
.zoneName = maybeZone.value()->m_name,
|
||||
.filePath = path,
|
||||
});
|
||||
con::debug("Loaded zone \"{}\"", maybeZone.value()->m_name);
|
||||
}
|
||||
else
|
||||
|
@@ -1,21 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { webviewBinds } from "@/native";
|
||||
import { useZoneStore } from "@/stores/ZoneStore";
|
||||
import SpinningLoader from "@/components/SpinningLoader.vue";
|
||||
|
||||
const zoneStore = useZoneStore();
|
||||
const lastPath = ref("");
|
||||
const loadingFastFile = ref(false);
|
||||
const unlinkingFastFile = ref(false);
|
||||
|
||||
async function onOpenFastfileClick() {
|
||||
lastPath.value =
|
||||
(await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] })) ?? "";
|
||||
const performingAction = computed<boolean>(() => loadingFastFile.value || unlinkingFastFile.value);
|
||||
|
||||
async function openFastFileSelect() {
|
||||
return await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] });
|
||||
}
|
||||
|
||||
async function onOpenFastFileClick() {
|
||||
if (performingAction.value) return;
|
||||
|
||||
const fastFilePath = await openFastFileSelect();
|
||||
if (!fastFilePath) return;
|
||||
|
||||
loadingFastFile.value = true;
|
||||
|
||||
webviewBinds
|
||||
.loadFastFile(lastPath.value)
|
||||
.loadFastFile(fastFilePath)
|
||||
.catch((e: string) => {
|
||||
console.error("Failed to load fastfile:", e);
|
||||
})
|
||||
@@ -24,6 +32,36 @@ async function onOpenFastfileClick() {
|
||||
});
|
||||
}
|
||||
|
||||
async function onUnlinkFastFileClick() {
|
||||
if (performingAction.value) return;
|
||||
|
||||
const fastFilePath = await openFastFileSelect();
|
||||
if (!fastFilePath) return;
|
||||
|
||||
try {
|
||||
unlinkingFastFile.value = true;
|
||||
|
||||
let loadedZoneName: string;
|
||||
try {
|
||||
loadedZoneName = (await webviewBinds.loadFastFile(fastFilePath)).zoneName;
|
||||
} catch (e: unknown) {
|
||||
console.error("Failed to load fastfile:", e as string);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await webviewBinds.unlinkZone(loadedZoneName);
|
||||
} catch (e: unknown) {
|
||||
console.error("Failed to unlink fastfile:", e as string);
|
||||
return;
|
||||
} finally {
|
||||
webviewBinds.unloadZone(loadedZoneName);
|
||||
}
|
||||
} finally {
|
||||
unlinkingFastFile.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onUnloadClicked(zoneName: string) {
|
||||
webviewBinds.unloadZone(zoneName).catch((e: string) => {
|
||||
console.error("Failed to unload zone:", e);
|
||||
@@ -36,18 +74,23 @@ function onUnloadClicked(zoneName: string) {
|
||||
<h1>Welcome to ModMan</h1>
|
||||
<small>Nothing to see here yet, this is mainly for testing</small>
|
||||
|
||||
<p>
|
||||
<button :disabled="loadingFastFile" @click="onOpenFastfileClick">
|
||||
<div class="actions">
|
||||
<button :disabled="performingAction" @click="onOpenFastFileClick">
|
||||
<SpinningLoader v-if="loadingFastFile" class="loading" />
|
||||
<span>Load fastfile</span>
|
||||
</button>
|
||||
</p>
|
||||
<button :disabled="performingAction" @click="onUnlinkFastFileClick">
|
||||
<SpinningLoader v-if="unlinkingFastFile" class="loading" />
|
||||
<span>Unlink fastfile</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Loaded zones:</h3>
|
||||
<div class="zone-list">
|
||||
<div v-for="zone in zoneStore.loadedZones" :key="zone" class="zone">
|
||||
<span>{{ zone }}</span>
|
||||
<button @click="onUnloadClicked(zone)">Unload</button>
|
||||
<button :disabled="performingAction" @click="onUnloadClicked(zone)">Unload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,6 +98,12 @@ function onUnloadClicked(zoneName: string) {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
column-gap: 0.5em;
|
||||
}
|
||||
|
||||
.loading {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
3
src/ModManUi/src/native/UnlinkingBinds.ts
Normal file
3
src/ModManUi/src/native/UnlinkingBinds.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface UnlinkingBinds {
|
||||
unlinkZone(zoneName: string): Promise<void>;
|
||||
}
|
@@ -8,7 +8,7 @@ export interface ZoneUnloadedDto {
|
||||
}
|
||||
|
||||
export interface ZoneBinds {
|
||||
loadFastFile(path: string): Promise<void>;
|
||||
loadFastFile(path: string): Promise<ZoneLoadedDto>;
|
||||
unloadZone(zoneName: string): Promise<void>;
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import type { DialogBinds } from "./DialogBinds";
|
||||
import type { UnlinkingBinds } from "./UnlinkingBinds";
|
||||
import type { ZoneBinds, ZoneEventMap } from "./ZoneBinds";
|
||||
|
||||
export type NativeMethods = DialogBinds & ZoneBinds;
|
||||
export type NativeMethods = DialogBinds & UnlinkingBinds & ZoneBinds;
|
||||
|
||||
type NativeEventMap = ZoneEventMap;
|
||||
|
||||
|
Reference in New Issue
Block a user