yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
project_manager.cc
Go to the documentation of this file.
1#include "project_manager.h"
2
3#include <filesystem>
4#include <fstream>
5
6#include "absl/strings/str_format.h"
8#include "core/project.h"
9#include "util/macro.h"
10
11namespace yaze {
12namespace editor {
13
15 : toast_manager_(toast_manager) {}
16
18 const std::string& template_name) {
19 // ROM-first workflow: Creating a project requires a ROM to be loaded
20 // The actual project creation happens after ROM selection in the wizard
21
22 if (template_name.empty()) {
23 // Create default project - will be configured after ROM load
25 current_project_.name = "New Project";
27
28 if (toast_manager_) {
29 toast_manager_->Show("New project created - select a ROM to continue",
31 }
32
33 // Mark that we're waiting for ROM selection
35 return absl::OkStatus();
36 }
37
38 return CreateFromTemplate(template_name, "New Project");
39}
40
41absl::Status ProjectManager::OpenProject(const std::string& filename) {
42 if (filename.empty()) {
43 // TODO: Show file dialog
44 return absl::InvalidArgumentError("No filename provided");
45 }
46
47 return LoadProjectFromFile(filename);
48}
49
50absl::Status ProjectManager::LoadProjectFromFile(const std::string& filename) {
51 if (!IsValidProjectFile(filename)) {
52 return absl::InvalidArgumentError(
53 absl::StrFormat("Invalid project file: %s", filename));
54 }
55
56 auto status = current_project_.Open(filename);
57 if (!status.ok()) {
58 if (toast_manager_) {
60 absl::StrFormat("Failed to load project: %s",
61 status.message()),
63 }
64 return status;
65 }
66
67 if (toast_manager_) {
69 absl::StrFormat("Project loaded: %s",
72 }
73
74 return absl::OkStatus();
75}
76
78 if (!HasActiveProject()) {
79 return absl::FailedPreconditionError("No active project to save");
80 }
81
83}
84
85absl::Status ProjectManager::SaveProjectAs(const std::string& filename) {
86 if (filename.empty()) {
87 // TODO: Show save dialog
88 return absl::InvalidArgumentError("No filename provided for save as");
89 }
90
91 return SaveProjectToFile(filename);
92}
93
94absl::Status ProjectManager::SaveProjectToFile(const std::string& filename) {
95 auto status = current_project_.SaveAs(filename);
96 if (!status.ok()) {
97 if (toast_manager_) {
99 absl::StrFormat("Failed to save project: %s", status.message()),
101 }
102 return status;
103 }
104
105 if (toast_manager_) {
106 toast_manager_->Show(absl::StrFormat("Project saved: %s", filename),
108 }
109
110 return absl::OkStatus();
111}
112
113absl::Status ProjectManager::ImportProject(const std::string& project_path) {
114 if (project_path.empty()) {
115 return absl::InvalidArgumentError("No project path provided");
116 }
117
118#ifndef __EMSCRIPTEN__
119 if (!std::filesystem::exists(project_path)) {
120 return absl::NotFoundError(
121 absl::StrFormat("Project path does not exist: %s", project_path));
122 }
123#endif
124
125 project::YazeProject imported_project;
126
127 // Handle ZScream project imports (.zsproj files)
128 if (project_path.ends_with(".zsproj")) {
129 RETURN_IF_ERROR(imported_project.ImportZScreamProject(project_path));
130 if (toast_manager_) {
132 "ZScream project imported successfully. Please configure ROM and "
133 "folders.",
134 ToastType::kInfo, 5.0f);
135 }
136 } else {
137 // Standard yaze project import
138 RETURN_IF_ERROR(imported_project.Open(project_path));
139 if (toast_manager_) {
141 absl::StrFormat("Project imported: %s", project_path),
143 }
144 }
145
146 current_project_ = std::move(imported_project);
147 return absl::OkStatus();
148}
149
150absl::Status ProjectManager::ExportProject(const std::string& export_path) {
151 if (!HasActiveProject()) {
152 return absl::FailedPreconditionError("No active project to export");
153 }
154
155 if (export_path.empty()) {
156 return absl::InvalidArgumentError("No export path provided");
157 }
158
159 // TODO: Implement project export logic
160 // This would typically create a package with all project files
161
162 if (toast_manager_) {
163 toast_manager_->Show(absl::StrFormat("Project exported: %s", export_path),
165 }
166
167 return absl::OkStatus();
168}
169
171 if (!HasActiveProject()) {
172 return absl::FailedPreconditionError("No active project to repair");
173 }
174
175 // TODO: Implement project repair logic
176 // This would check for missing files, broken references, etc.
177
178 if (toast_manager_) {
179 toast_manager_->Show("Project repair completed", ToastType::kSuccess);
180 }
181
182 return absl::OkStatus();
183}
184
186 if (!HasActiveProject()) {
187 return absl::FailedPreconditionError("No active project to validate");
188 }
189
190 auto result = current_project_.Validate();
191 if (!result.ok()) {
192 if (toast_manager_) {
194 absl::StrFormat("Project validation failed: %s", result.message()),
196 }
197 return result;
198 }
199
200 if (toast_manager_) {
201 toast_manager_->Show("Project validation passed", ToastType::kSuccess);
202 }
203
204 return absl::OkStatus();
205}
206
208 return current_project_.name;
209}
210
213}
214
215std::vector<std::string> ProjectManager::GetAvailableTemplates() const {
216 // TODO: Scan templates directory and return available templates
217 return {"Empty Project", "Dungeon Editor Project", "Overworld Editor Project",
218 "Graphics Editor Project", "Full Editor Project"};
219}
220
222 const std::string& template_name, const std::string& project_name) {
223 if (template_name.empty() || project_name.empty()) {
224 return absl::InvalidArgumentError(
225 "Template name and project name required");
226 }
227
228 // TODO: Implement template-based project creation
229 // This would copy template files and customize them
230
232 current_project_.name = project_name;
234
235 if (toast_manager_) {
237 absl::StrFormat("Project created from template: %s", template_name),
239 }
240
241 return absl::OkStatus();
242}
243
245 const std::string& project_name) const {
246 // Convert project name to valid filename
247 std::string filename = project_name;
248 std::replace(filename.begin(), filename.end(), ' ', '_');
249 std::replace(filename.begin(), filename.end(), '/', '_');
250 std::replace(filename.begin(), filename.end(), '\\', '_');
251
252 return absl::StrFormat("%s.yaze", filename);
253}
254
255bool ProjectManager::IsValidProjectFile(const std::string& filename) const {
256 if (filename.empty()) {
257 return false;
258 }
259
260#ifndef __EMSCRIPTEN__
261 if (!std::filesystem::exists(filename)) {
262 return false;
263 }
264#endif
265
266 // Check file extension
267 std::string extension = std::filesystem::path(filename).extension().string();
268 return extension == ".yaze" || extension == ".json";
269}
270
272 const std::string& project_path) {
273#ifdef __EMSCRIPTEN__
274 // WASM uses virtual storage; nothing to provision eagerly.
275 return absl::OkStatus();
276#else
277 try {
278 std::filesystem::create_directories(project_path);
279 std::filesystem::create_directories(project_path + "/assets");
280 std::filesystem::create_directories(project_path + "/scripts");
281 std::filesystem::create_directories(project_path + "/output");
282 return absl::OkStatus();
283 } catch (const std::exception& e) {
284 return absl::InternalError(
285 absl::StrFormat("Failed to create project structure: %s", e.what()));
286 }
287#endif
288}
289
290// ============================================================================
291// ROM-First Workflow Implementation
292// ============================================================================
293
294absl::Status ProjectManager::SetProjectRom(const std::string& rom_path) {
295 if (rom_path.empty()) {
296 return absl::InvalidArgumentError("ROM path cannot be empty");
297 }
298
299#ifndef __EMSCRIPTEN__
300 if (!std::filesystem::exists(rom_path)) {
301 return absl::NotFoundError(
302 absl::StrFormat("ROM file not found: %s", rom_path));
303 }
304#endif
305
308
309 if (toast_manager_) {
311 absl::StrFormat("ROM set for project: %s",
312 std::filesystem::path(rom_path).filename().string()),
314 }
315
316 return absl::OkStatus();
317}
318
320 const std::string& project_name, const std::string& project_path) {
321 if (project_name.empty()) {
322 return absl::InvalidArgumentError("Project name cannot be empty");
323 }
324
325 current_project_.name = project_name;
326
327 if (!project_path.empty()) {
328 current_project_.filepath = project_path;
329 } else {
331 }
332
335
336 // Initialize project structure if we have a directory
337 std::string project_dir;
338#ifndef __EMSCRIPTEN__
339 project_dir = std::filesystem::path(current_project_.filepath)
340 .parent_path()
341 .string();
342#endif
343 if (!project_dir.empty()) {
344 auto status = InitializeProjectStructure(project_dir);
345 if (!status.ok()) {
346 if (toast_manager_) {
347 toast_manager_->Show("Could not create project directories",
349 }
350 }
351 }
352
353 if (toast_manager_) {
355 absl::StrFormat("Project created: %s", project_name),
357 }
358
359 return absl::OkStatus();
360}
361
367
368 if (toast_manager_) {
369 toast_manager_->Show("Project creation cancelled", ToastType::kInfo);
370 }
371 }
372}
373
379
380// ============================================================================
381// ZSCustomOverworld Presets
382// ============================================================================
383
384std::vector<project::ProjectManager::ProjectTemplate>
386 std::vector<project::ProjectManager::ProjectTemplate> templates;
387
388 // Vanilla ROM Hack
389 {
391 t.name = "Vanilla ROM Hack";
392 t.description = "Standard ROM editing without custom ASM patches. "
393 "Limited to vanilla game features.";
394 t.icon = "MD_GAMEPAD";
397 templates.push_back(t);
398 }
399
400 // ZSCustomOverworld v2
401 {
403 t.name = "ZSCustomOverworld v2";
404 t.description = "Basic overworld expansion with custom BG colors, "
405 "main palettes, and parent system.";
406 t.icon = "MD_MAP";
410 templates.push_back(t);
411 }
412
413 // ZSCustomOverworld v3 (Recommended)
414 {
416 t.name = "ZSCustomOverworld v3";
417 t.description = "Full overworld expansion: wide/tall areas, animated GFX, "
418 "subscreen overlays, and all custom features.";
419 t.icon = "MD_TERRAIN";
429 templates.push_back(t);
430 }
431
432 // Randomizer Compatible
433 {
435 t.name = "Randomizer Compatible";
436 t.description = "Minimal editing preset compatible with ALttP Randomizer. "
437 "Avoids changes that break randomizer compatibility.";
438 t.icon = "MD_SHUFFLE";
441 templates.push_back(t);
442 }
443
444 return templates;
445}
446
447absl::Status ProjectManager::ApplyZsoPreset(const std::string& preset_name) {
448 auto templates = GetZsoTemplates();
449
450 for (const auto& t : templates) {
451 if (t.name == preset_name) {
452 // Apply feature flags from template
453 current_project_.feature_flags = t.template_project.feature_flags;
454
455 if (toast_manager_) {
457 absl::StrFormat("Applied preset: %s", preset_name),
459 }
460
461 return absl::OkStatus();
462 }
463 }
464
465 return absl::NotFoundError(
466 absl::StrFormat("Unknown preset: %s", preset_name));
467}
468
469} // namespace editor
470} // namespace yaze
std::string GenerateProjectFilename(const std::string &project_name) const
absl::Status FinalizeProjectCreation(const std::string &project_name, const std::string &project_path)
Complete project creation after ROM is loaded.
absl::Status SaveProjectAs(const std::string &filename="")
absl::Status ExportProject(const std::string &export_path)
std::vector< std::string > GetAvailableTemplates() const
std::string GetProjectPath() const
absl::Status ApplyZsoPreset(const std::string &preset_name)
Apply a ZSO preset to the current project.
bool IsValidProjectFile(const std::string &filename) const
absl::Status SetProjectRom(const std::string &rom_path)
Set the ROM for the current project.
absl::Status CreateNewProject(const std::string &template_name="")
project::YazeProject current_project_
absl::Status OpenProject(const std::string &filename="")
void RequestRomSelection()
Request ROM selection (triggers callback)
static std::vector< project::ProjectManager::ProjectTemplate > GetZsoTemplates()
Get ZSO-specific project templates.
void CancelPendingProject()
Cancel pending project creation.
ProjectManager(ToastManager *toast_manager)
absl::Status ImportProject(const std::string &project_path)
std::function< void()> rom_selection_callback_
absl::Status LoadProjectFromFile(const std::string &filename)
absl::Status InitializeProjectStructure(const std::string &project_path)
absl::Status CreateFromTemplate(const std::string &template_name, const std::string &project_name)
absl::Status SaveProjectToFile(const std::string &filename)
std::string GetProjectName() const
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
struct yaze::core::FeatureFlags::Flags::Overworld overworld
Modern project structure with comprehensive settings consolidation.
Definition project.h:84
absl::Status ImportZScreamProject(const std::string &zscream_project_path)
Definition project.cc:677
std::string rom_filename
Definition project.h:92
std::string GetDisplayName() const
Definition project.cc:828
absl::Status SaveAs(const std::string &new_path)
Definition project.cc:198
absl::Status Open(const std::string &project_path)
Definition project.cc:151
absl::Status Validate() const
Definition project.cc:738
core::FeatureFlags::Flags feature_flags
Definition project.h:105