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", status.message()),
62 }
63 return status;
64 }
65
66 if (toast_manager_) {
67 toast_manager_->Show(absl::StrFormat("Project loaded: %s",
70 }
71
72 return absl::OkStatus();
73}
74
76 if (!HasActiveProject()) {
77 return absl::FailedPreconditionError("No active project to save");
78 }
79
81}
82
83absl::Status ProjectManager::SaveProjectAs(const std::string& filename) {
84 if (filename.empty()) {
85 // TODO: Show save dialog
86 return absl::InvalidArgumentError("No filename provided for save as");
87 }
88
89 return SaveProjectToFile(filename);
90}
91
92absl::Status ProjectManager::SaveProjectToFile(const std::string& filename) {
93 auto status = current_project_.SaveAs(filename);
94 if (!status.ok()) {
95 if (toast_manager_) {
97 absl::StrFormat("Failed to save project: %s", status.message()),
99 }
100 return status;
101 }
102
103 if (toast_manager_) {
104 toast_manager_->Show(absl::StrFormat("Project saved: %s", filename),
106 }
107
108 return absl::OkStatus();
109}
110
111absl::Status ProjectManager::ImportProject(const std::string& project_path) {
112 if (project_path.empty()) {
113 return absl::InvalidArgumentError("No project path provided");
114 }
115
116#ifndef __EMSCRIPTEN__
117 if (!std::filesystem::exists(project_path)) {
118 return absl::NotFoundError(
119 absl::StrFormat("Project path does not exist: %s", project_path));
120 }
121#endif
122
123 project::YazeProject imported_project;
124
125 // Handle ZScream project imports (.zsproj files)
126 if (project_path.ends_with(".zsproj")) {
127 RETURN_IF_ERROR(imported_project.ImportZScreamProject(project_path));
128 if (toast_manager_) {
130 "ZScream project imported successfully. Please configure ROM and "
131 "folders.",
132 ToastType::kInfo, 5.0f);
133 }
134 } else {
135 // Standard yaze project import
136 RETURN_IF_ERROR(imported_project.Open(project_path));
137 if (toast_manager_) {
139 absl::StrFormat("Project imported: %s", project_path),
141 }
142 }
143
144 current_project_ = std::move(imported_project);
145 return absl::OkStatus();
146}
147
148absl::Status ProjectManager::ExportProject(const std::string& export_path) {
149 if (!HasActiveProject()) {
150 return absl::FailedPreconditionError("No active project to export");
151 }
152
153 if (export_path.empty()) {
154 return absl::InvalidArgumentError("No export path provided");
155 }
156
157 // TODO: Implement project export logic
158 // This would typically create a package with all project files
159
160 if (toast_manager_) {
161 toast_manager_->Show(absl::StrFormat("Project exported: %s", export_path),
163 }
164
165 return absl::OkStatus();
166}
167
169 if (!HasActiveProject()) {
170 return absl::FailedPreconditionError("No active project to repair");
171 }
172
173 // TODO: Implement project repair logic
174 // This would check for missing files, broken references, etc.
175
176 if (toast_manager_) {
177 toast_manager_->Show("Project repair completed", ToastType::kSuccess);
178 }
179
180 return absl::OkStatus();
181}
182
184 if (!HasActiveProject()) {
185 return absl::FailedPreconditionError("No active project to validate");
186 }
187
188 auto result = current_project_.Validate();
189 if (!result.ok()) {
190 if (toast_manager_) {
192 absl::StrFormat("Project validation failed: %s", result.message()),
194 }
195 return result;
196 }
197
198 if (toast_manager_) {
199 toast_manager_->Show("Project validation passed", ToastType::kSuccess);
200 }
201
202 return absl::OkStatus();
203}
204
206 return current_project_.name;
207}
208
211}
212
213std::vector<std::string> ProjectManager::GetAvailableTemplates() const {
214 // TODO: Scan templates directory and return available templates
215 return {"Empty Project", "Dungeon Editor Project", "Overworld Editor Project",
216 "Graphics Editor Project", "Full Editor Project"};
217}
218
220 const std::string& template_name, const std::string& project_name) {
221 if (template_name.empty() || project_name.empty()) {
222 return absl::InvalidArgumentError(
223 "Template name and project name required");
224 }
225
226 // TODO: Implement template-based project creation
227 // This would copy template files and customize them
228
230 current_project_.name = project_name;
232
233 if (toast_manager_) {
235 absl::StrFormat("Project created from template: %s", template_name),
237 }
238
239 return absl::OkStatus();
240}
241
243 const std::string& project_name) const {
244 // Convert project name to valid filename
245 std::string filename = project_name;
246 std::replace(filename.begin(), filename.end(), ' ', '_');
247 std::replace(filename.begin(), filename.end(), '/', '_');
248 std::replace(filename.begin(), filename.end(), '\\', '_');
249
250 return absl::StrFormat("%s.yaze", filename);
251}
252
253bool ProjectManager::IsValidProjectFile(const std::string& filename) const {
254 if (filename.empty()) {
255 return false;
256 }
257
258#ifndef __EMSCRIPTEN__
259 if (!std::filesystem::exists(filename)) {
260 return false;
261 }
262#endif
263
264 // Check file extension
265 std::string extension = std::filesystem::path(filename).extension().string();
266 return extension == ".yaze" || extension == ".json";
267}
268
270 const std::string& project_path) {
271#ifdef __EMSCRIPTEN__
272 // WASM uses virtual storage; nothing to provision eagerly.
273 return absl::OkStatus();
274#else
275 try {
276 std::filesystem::create_directories(project_path);
277 std::filesystem::create_directories(project_path + "/assets");
278 std::filesystem::create_directories(project_path + "/scripts");
279 std::filesystem::create_directories(project_path + "/output");
280 return absl::OkStatus();
281 } catch (const std::exception& e) {
282 return absl::InternalError(
283 absl::StrFormat("Failed to create project structure: %s", e.what()));
284 }
285#endif
286}
287
288// ============================================================================
289// ROM-First Workflow Implementation
290// ============================================================================
291
292absl::Status ProjectManager::SetProjectRom(const std::string& rom_path) {
293 if (rom_path.empty()) {
294 return absl::InvalidArgumentError("ROM path cannot be empty");
295 }
296
297#ifndef __EMSCRIPTEN__
298 if (!std::filesystem::exists(rom_path)) {
299 return absl::NotFoundError(
300 absl::StrFormat("ROM file not found: %s", rom_path));
301 }
302#endif
303
306
307 if (toast_manager_) {
309 absl::StrFormat("ROM set for project: %s",
310 std::filesystem::path(rom_path).filename().string()),
312 }
313
314 return absl::OkStatus();
315}
316
318 const std::string& project_name, const std::string& project_path) {
319 if (project_name.empty()) {
320 return absl::InvalidArgumentError("Project name cannot be empty");
321 }
322
323 current_project_.name = project_name;
324
325 if (!project_path.empty()) {
326 current_project_.filepath = project_path;
327 } else {
329 }
330
333
334 // Initialize project structure if we have a directory
335 std::string project_dir;
336#ifndef __EMSCRIPTEN__
337 project_dir =
338 std::filesystem::path(current_project_.filepath).parent_path().string();
339#endif
340 if (!project_dir.empty()) {
341 auto status = InitializeProjectStructure(project_dir);
342 if (!status.ok()) {
343 if (toast_manager_) {
344 toast_manager_->Show("Could not create project directories",
346 }
347 }
348 }
349
350 if (toast_manager_) {
351 toast_manager_->Show(absl::StrFormat("Project created: %s", project_name),
353 }
354
355 return absl::OkStatus();
356}
357
363
364 if (toast_manager_) {
365 toast_manager_->Show("Project creation cancelled", ToastType::kInfo);
366 }
367 }
368}
369
375
376// ============================================================================
377// ZSCustomOverworld Presets
378// ============================================================================
379
380std::vector<project::ProjectManager::ProjectTemplate>
382 std::vector<project::ProjectManager::ProjectTemplate> templates;
383
384 // Vanilla ROM Hack
385 {
387 t.name = "Vanilla ROM Hack";
388 t.description =
389 "Standard ROM editing without custom ASM patches. "
390 "Limited to vanilla game features.";
391 t.icon = "MD_GAMEPAD";
394 templates.push_back(t);
395 }
396
397 // ZSCustomOverworld v2
398 {
400 t.name = "ZSCustomOverworld v2";
401 t.description =
402 "Basic overworld expansion with custom BG colors, "
403 "main palettes, and parent system.";
404 t.icon = "MD_MAP";
408 templates.push_back(t);
409 }
410
411 // ZSCustomOverworld v3 (Recommended)
412 {
414 t.name = "ZSCustomOverworld v3";
415 t.description =
416 "Full overworld expansion: wide/tall areas, animated GFX, "
417 "subscreen overlays, and all custom features.";
418 t.icon = "MD_TERRAIN";
428 templates.push_back(t);
429 }
430
431 // Randomizer Compatible
432 {
434 t.name = "Randomizer Compatible";
435 t.description =
436 "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_) {
456 toast_manager_->Show(absl::StrFormat("Applied preset: %s", preset_name),
458 }
459
460 return absl::OkStatus();
461 }
462 }
463
464 return absl::NotFoundError(
465 absl::StrFormat("Unknown preset: %s", preset_name));
466}
467
468} // namespace editor
469} // 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:197
absl::Status Open(const std::string &project_path)
Definition project.cc:150
absl::Status Validate() const
Definition project.cc:738
core::FeatureFlags::Flags feature_flags
Definition project.h:106