/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include #include "stdafx.h" #include "FRED.h" #include //#include //#include #include #include "FREDDoc.h" #include "FREDView.h" #include "EventEditor.h" #include "FredRender.h" #include "MainFrm.h" #include "Management.h" #include "MessageEditorDlg.h" #include "ai/ai.h" #include "ai/aigoals.h" #include "cfile/cfile.h" #include "cfile/cfilesystem.h" #include "cmdline/cmdline.h" #include "globalincs/linklist.h" #include "localization/fhash.h" #include "localization/localize.h" #include "mission/missiongoals.h" #include "mission/missiongrid.h" #include "mission/missionparse.h" #include "missioneditor/common.h" #include "object/object.h" #include "render/3d.h" #include "ship/ship.h" #include "starfield/starfield.h" #include "weapon/weapon.h" #include "scripting/global_hooks.h" extern int Num_objects; #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // In editor mode, use class CFile, in game, use CFILE (our file) #define XFILE CFile IMPLEMENT_DYNCREATE(CFREDDoc, CDocument) BEGIN_MESSAGE_MAP(CFREDDoc, CDocument) //{{AFX_MSG_MAP(CFREDDoc) ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) ON_COMMAND(ID_FILE_IMPORT_FSM, OnFileImportFSM) //}}AFX_MSG_MAP END_MESSAGE_MAP() CFREDDoc *FREDDoc_ptr = NULL; int Local_modified = 0; int Undo_available = 0; int Undo_count = 0; CFREDDoc::CFREDDoc() { int i; FREDDoc_ptr = this; for (i = 0; i < MISSION_BACKUP_DEPTH; i++) undo_desc[i].Empty(); } CFREDDoc::~CFREDDoc() {} #ifdef _DEBUG void CFREDDoc::AssertValid() const { CDocument::AssertValid(); } #endif //_DEBUG bool CFREDDoc::autoload() { char name[256], backup_name[256]; int i; bool r; FILE *fp; if ( !cf_create_default_path_string(name, sizeof(name) - 1, CF_TYPE_MISSIONS) ) { return 0; } strcat_s(name, MISSION_BACKUP_NAME); strcpy_s(backup_name, name); strcat_s(name, ".002"); // Check if Backup.002 exists fp = fopen(name, "r"); if (!fp) return 0; fclose(fp); clean_up_selections(); // Load Backup.002 r = load_mission(name, MPF_FAST_RELOAD); Update_window = 1; // Delete Backup.001 auto len = strlen(backup_name); strcat_s(backup_name, ".001"); cf_delete(backup_name, CF_TYPE_MISSIONS); // Rename Backups. .002 becomes .001, .003 becomes .002, etc. for (i = 1; i < MISSION_BACKUP_DEPTH; i++) { sprintf(backup_name + len, ".%.3d", i + 1); sprintf(name + len, ".%.3d", i); cf_rename(backup_name, name, CF_TYPE_MISSIONS); undo_desc[i] = undo_desc[i + 1]; } // Reduce the undo count and check if we can do another undo op. Undo_count--; check_undo(); return r; } int CFREDDoc::autosave(char *desc) { int i; Fred_mission_save save; if (Mission_save_format == FSO_FORMAT_RETAIL) { save.set_save_format(MissionFormat::RETAIL); } else if (Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) { save.set_save_format(MissionFormat::COMPATIBILITY_MODE); } else { save.set_save_format(MissionFormat::STANDARD); } save.set_always_save_display_names(Always_save_display_names); save.set_view_pos(view_pos); save.set_view_orient(view_orient); save.set_fred_alt_names(Fred_alt_names); save.set_fred_callsigns(Fred_callsigns); CWaitCursor wait; if (Autosave_disabled) { return 0; } if (Briefing_dialog) { Briefing_dialog->update_data(1); } if (save.autosave_mission_file(MISSION_BACKUP_NAME)) { Undo_count = Undo_available = 0; return -1; } for (i = MISSION_BACKUP_DEPTH; i > 1; i--) { undo_desc[i] = undo_desc[i - 1]; } if (desc) { undo_desc[1] = desc; } else { undo_desc[1].Empty(); } Undo_count++; check_undo(); return 0; } int CFREDDoc::check_undo() { char name[256]; FILE *fp; Undo_available = 0; if (!Undo_count) { return 0; } if ( !cf_create_default_path_string(name, sizeof(name) - 1, CF_TYPE_MISSIONS) ) { return 0; } strcat_s(name, MISSION_BACKUP_NAME); strcat_s(name, ".002"); // Check if Backup.002 exists fp = fopen(name, "r"); if (!fp) return 0; fclose(fp); Undo_available = 1; return 1; } void CFREDDoc::DeleteContents() { editor_init_mission(); } #ifdef _DEBUG void CFREDDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif // _DEBUG void CFREDDoc::editor_init_mission() { reset_mission(); SetModifiedFlag(FALSE); recreate_dialogs(); } bool CFREDDoc::load_mission(const char *pathname, int flags) { // make sure we're in the correct working directory!!!!!! chdir(Fred_base_dir); char name[512], *old_name; int i, j, ob; SCP_map used_pool; object *objp; Parse_viewer_pos = view_pos; Parse_viewer_orient = view_orient; // preserve the editor grid across the reload; it's display state, not part of the mission, // but clear_mission() recreates it via fred_render_init(). (See the view_pos/view_orient handling above and below.) matrix grid_orient = The_grid->gmatrix; vec3d grid_center = The_grid->center; int grid_nrows = The_grid->nrows; int grid_ncols = The_grid->ncols; float grid_square_size = The_grid->square_size; // activate the localizer hash table fhash_flush(); // Guard against reentrant ship-editor write-back while the mission is loaded. The guard is released once the mission has stabilized. Ship_editor_dialog.bypass_all++; clear_mission(flags & MPF_FAST_RELOAD); // message 1: required version if (!parse_main(pathname, flags)) { auto term = (flags & MPF_IMPORT_FSM) ? "import" : "load"; // the version will have been assigned before loading was aborted if (!gameversion::check_at_least(The_mission.required_fso_version)) { sprintf(name, "The file \"%s\" cannot be %sed because it requires FSO version %s", pathname, term, format_version(The_mission.required_fso_version, true).c_str()); } else { sprintf(name, "Unable to %s the file \"%s\".", term, pathname); } Fred_view_wnd->MessageBox(name); Ship_editor_dialog.bypass_all--; create_new_mission(); return false; } // message 2: unknown classes if ((Num_unknown_ship_classes > 0) || (Num_unknown_prop_classes > 0) || (Num_unknown_weapon_classes > 0) || (Num_unknown_loadout_classes > 0)) { if (flags & MPF_IMPORT_FSM) { SCP_string msg; sprintf(msg, "Fred encountered unknown ship/prop/weapon classes when importing \"%s\" (path \"%s\"). You will have to manually edit the converted mission to correct this.", The_mission.name.c_str(), pathname); Fred_view_wnd->MessageBox(msg.c_str()); } else { Fred_view_wnd->MessageBox("Fred encountered unknown ship/prop/weapon classes when parsing the mission file. This may be due to mission disk data you do not have."); } } // message 3: warning about saving under a new version if (!(flags & MPF_IMPORT_FSM) && (The_mission.required_fso_version != LEGACY_MISSION_VERSION) && (MISSION_VERSION > The_mission.required_fso_version)) { SCP_string msg = "This mission's file format is "; msg += format_version(The_mission.required_fso_version, true); msg += ". When you save this mission, the file format will be migrated to "; msg += format_version(MISSION_VERSION, true); msg += ". FSO versions earlier than this will not be able to load the mission."; Fred_view_wnd->MessageBox(msg.c_str()); } // message 4: check for "immobile" flag migration if (!Fred_migrated_immobile_ships.empty()) { SCP_string msg = "The \"immobile\" ship flag has been superseded by the \"don't-change-position\", and \"don't-change-orientation\" flags. " "All ships which previously had \"Does Not Move\" checked in the ship flags editor will now have both \"Does Not Change Position\" and " "\"Does Not Change Orientation\" checked. After you close this dialog, the error checker will check for any potential issues, including " "issues involving these flags.\n\nThe following ships have been migrated:"; for (int shipnum : Fred_migrated_immobile_ships) { msg += "\n\t"; msg += Ships[shipnum].ship_name; } truncate_message_lines(msg, 30); Fred_view_wnd->MessageBox(msg.c_str()); Error_checker_checks_potential_issues_once = true; } obj_merge_created_list(); objp = GET_FIRST(&obj_used_list); while (objp != END_OF_LIST(&obj_used_list)) { if (objp->flags[Object::Object_Flags::Player_ship]) { Assert(objp->type == OBJ_SHIP); objp->type = OBJ_START; // Player_starts++; } objp = GET_NEXT(objp); } for (i = 0; i < Num_wings; i++) { for (j = 0; j < Wings[i].wave_count; j++) { ob = Ships[Wings[i].ship_index[j]].objnum; wing_objects[i][j] = ob; Ships[Wings[i].ship_index[j]].wingnum = i; Ships[Wings[i].ship_index[j]].arrival_cue = Locked_sexp_false; } // fix old ship names for ships in wings if needed while (j--) { if ((Objects[wing_objects[i][j]].type == OBJ_SHIP) || (Objects[wing_objects[i][j]].type == OBJ_START)) { // don't change player ship names wing_bash_ship_name(name, Wings[i].name, j + 1); old_name = Ships[Wings[i].ship_index[j]].ship_name; if (stricmp(name, old_name) != 0) { // need to fix name rename_ship(Wings[i].ship_index[j], name); // bash it again so that we handle display names if needed wing_bash_ship_name(&Ships[Wings[i].ship_index[j]], &Wings[i], j + 1, true); } } } } for (i = 0; i < Num_teams; i++) { generate_weaponry_usage_list_team(i, used_pool); for (auto &entry : Team_data[i].weapon_choices) { // The amount used in wings is always set by a static loadout entry so skip any that were set by Sexp variables if (entry.class_variable.empty() && entry.count_variable.empty()) { // convert weaponry pool to be extras available beyond the current ships weapons entry.count -= used_pool.value_or(entry.class_index, 0); if (entry.count < 0) { entry.count = 0; } // zero the used pool entry used_pool.erase(entry.class_index); } } // double check the used pool is empty if (!Team_data[i].do_not_validate) { for (const auto &[weapon_class, count] : used_pool) { if (count != 0) { Warning(LOCATION, "%s is used in wings of team %d but was not in the loadout. Fixing now", Weapon_info[weapon_class].name, i + 1); // add the weapon as a new entry auto &entry = Team_data[i].weapon_choices.emplace_back(); entry.class_index = weapon_class; entry.count = count; } } } } Assert(Mission_palette >= 0); Assert(Mission_palette <= 98); // go through all ships and translate their callsign and alternate name indices objp = GET_FIRST(&obj_used_list); while (objp != END_OF_LIST(&obj_used_list)) { // if this is a ship, check it, and mark its possible alternate name down in the auxiliary array if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->instance >= 0)) { if (Ships[objp->instance].alt_type_index >= 0) { strcpy_s(Fred_alt_names[objp->instance], mission_parse_lookup_alt_index(Ships[objp->instance].alt_type_index)); // also zero it Ships[objp->instance].alt_type_index = -1; } if (Ships[objp->instance].callsign_index >= 0) { strcpy_s(Fred_callsigns[objp->instance], mission_parse_lookup_callsign_index(Ships[objp->instance].callsign_index)); // also zero it Ships[objp->instance].callsign_index = -1; } } objp = GET_NEXT(objp); } view_pos = Parse_viewer_pos; view_orient = Parse_viewer_orient; // restore the editor grid that was preserved above create_grid(The_grid, &grid_orient.vec.fvec, &grid_orient.vec.rvec, &grid_center, grid_nrows, grid_ncols, grid_square_size); set_modified(0); stars_post_level_init(); // mission is fully built and consistent now; allow the editors to repopulate Ship_editor_dialog.bypass_all--; recreate_dialogs(); // This hook will allow for scripts to know when a mission has been loaded // which will then allow them to update any LuaEnums that may be related to sexps if (scripting::hooks::FredOnMissionLoad->isActive()) { scripting::hooks::FredOnMissionLoad->run(); } return true; } void CFREDDoc::OnEditClearAll() { DeleteContents(); } void CFREDDoc::OnEditUndo() { // TODO: Add your command handler code here } void CFREDDoc::OnFileImportFSM() { char fs1_mission_path[MAX_PATH_LEN]; char fs2_mission_path[MAX_PATH_LEN]; char dest_directory[MAX_PATH + 1]; // path stuff { char *ch; // get base paths strcpy_s(fs1_mission_path, Fred_exe_dir); ch = strrchr(fs1_mission_path, DIR_SEPARATOR_CHAR); if (ch != NULL) *ch = '\0'; strcpy_s(fs2_mission_path, Fred_exe_dir); ch = strrchr(fs2_mission_path, DIR_SEPARATOR_CHAR); if (ch != NULL) *ch = '\0'; // estimate the mission path for FS1 if ((ch = stristr(fs1_mission_path, "FreeSpace2")) != NULL) { strcpy(ch, "FreeSpace\\Data\\Missions"); } // estimate the mission path for FS2 strcat_s(fs2_mission_path, "\\Data\\Missions"); } // if mission has been modified, offer to save before continuing. if (!SaveModified()) return; // get location to import from CFileDialog dlgFile(TRUE, "fsm", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_ALLOWMULTISELECT, "FreeSpace Missions (*.fsm)|*.fsm|All files (*.*)|*.*||"); dlgFile.m_ofn.lpstrTitle = "Select one or more missions to import"; dlgFile.m_ofn.lpstrInitialDir = fs1_mission_path; // get FSM files if (dlgFile.DoModal() != IDOK) return; memset(dest_directory, 0, sizeof(dest_directory)); // get location to save to BROWSEINFO bi; bi.hwndOwner = theApp.GetMainWnd()->GetSafeHwnd(); bi.pidlRoot = NULL; bi.pszDisplayName = dest_directory; bi.lpszTitle = "Select a location to save in"; bi.ulFlags = 0; bi.lpfn = NULL; bi.lParam = NULL; bi.iImage = NULL; LPCITEMIDLIST ret_val = SHBrowseForFolder(&bi); if (ret_val == NULL) return; SHGetPathFromIDList(ret_val, dest_directory); if (*dest_directory == '\0') return; clear_mission(true); int num_files = 0; int successes = 0; char dest_path[MAX_PATH_LEN] = ""; // process all missions POSITION pos(dlgFile.GetStartPosition()); while (pos) { char *ch; char filename[1024]; char fs1_path[MAX_PATH_LEN]; CString fs1_path_mfc(dlgFile.GetNextPathName(pos)); num_files++; Fred_mission_save save; if (Mission_save_format == FSO_FORMAT_RETAIL) { save.set_save_format(MissionFormat::RETAIL); } else if (Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) { save.set_save_format(MissionFormat::COMPATIBILITY_MODE); } else { save.set_save_format(MissionFormat::STANDARD); } save.set_always_save_display_names(Always_save_display_names); save.set_view_pos(view_pos); save.set_view_orient(view_orient); save.set_fred_alt_names(Fred_alt_names); save.set_fred_callsigns(Fred_callsigns); DWORD attrib; FILE *fp; // path name too long? if (strlen(fs1_path_mfc) > MAX_PATH_LEN - 1) continue; // nothing here? if (!strlen(fs1_path_mfc)) continue; // get our mission strcpy_s(fs1_path, fs1_path_mfc); // load mission into memory if (!load_mission(fs1_path, MPF_IMPORT_FSM | MPF_FAST_RELOAD)) continue; // get filename ch = strrchr(fs1_path, DIR_SEPARATOR_CHAR) + 1; if (ch != NULL) strcpy_s(filename, ch); else strcpy_s(filename, fs1_path); // truncate extension ch = strrchr(filename, '.'); if (ch != NULL) *ch = '\0'; // add new extension strcat_s(filename, ".fs2"); strcpy_s(Mission_filename, filename); // get new path strcpy_s(dest_path, dest_directory); strcat_s(dest_path, "\\"); strcat_s(dest_path, filename); // check attributes fp = fopen(dest_path, "r"); if (fp) { fclose(fp); attrib = GetFileAttributes(dest_path); if (attrib & FILE_ATTRIBUTE_READONLY) continue; } // try to save it if (save.save_mission_file(dest_path)) continue; // success successes++; } if (num_files > 1) { create_new_mission(); Fred_view_wnd->MessageBox("Import complete. Please check the destination folder to verify all missions were imported successfully.", "Status", MB_OK); } else if (num_files == 1) { if (successes == 1) SetModifiedFlag(FALSE); if (Briefing_dialog) { Briefing_dialog->restore_editor_state(); Briefing_dialog->update_data(1); } if (successes == 1) { // these aren't done automatically for imports theApp.AddToRecentFileList((LPCTSTR)dest_path); SetTitle((LPCTSTR)Mission_filename); } } recreate_dialogs(); } BOOL CFREDDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // TODO: add reinitialization code here // (SDI documents will reuse this document) return TRUE; } BOOL CFREDDoc::OnOpenDocument(LPCTSTR pathname) { // don't process any objects if the window focus is lost and reacquired clean_up_selections(); auto sep_ch = strrchr(pathname, '\\'); auto filename = (sep_ch != nullptr) ? (sep_ch + 1) : pathname; auto len = strlen(filename); // drop extension and copy to Mission_filename auto ext_ch = strrchr(filename, '.'); if (ext_ch != nullptr) len = ext_ch - filename; if (len >= 80) len = 79; strncpy(Mission_filename, filename, len); Mission_filename[len] = 0; // first, just grab the info of this mission if (!parse_main(pathname, MPF_ONLY_MISSION_INFO)) { *Mission_filename = 0; return FALSE; } SCP_string created = The_mission.created; CFileLocation res = cf_find_file_location(pathname, CF_TYPE_ANY); time_t modified = res.m_time; Assertion(res.found, "Couldn't find path '%s' even though parse_main() succeeded!", pathname); if (!res.found) { created = ""; // prevent any backup check from succeeding so we just load the actual specified file } // now check all the autosaves SCP_string backup_name; CFileLocation backup_res; for (int i = 1; i <= MISSION_BACKUP_DEPTH; ++i) { backup_name = MISSION_BACKUP_NAME; char extension[5]; sprintf(extension, ".%.3d", i); backup_name += extension; backup_res = cf_find_file_location(backup_name.c_str(), CF_TYPE_MISSIONS); if (backup_res.found && parse_main(backup_res.full_name.c_str(), MPF_ONLY_MISSION_INFO)) { SCP_string this_created = The_mission.created; time_t this_modified = backup_res.m_time; if (created == this_created && this_modified > modified) break; } backup_name.clear(); } // maybe load from the backup instead if (!backup_name.empty()) { SCP_string prompt = "Autosaved file "; prompt += backup_name; prompt += " has a file modification time more recent than the specified file. Do you want to load the autosave instead?"; int z = Fred_view_wnd->MessageBox(prompt.c_str(), "Recover from autosave", MB_ICONQUESTION | MB_YESNO); if (z == IDYES) pathname = backup_res.full_name.c_str(); } // now we can actually load either the original path or the backup path if (!load_mission(pathname)) { *Mission_filename = 0; return FALSE; } Fred_view_wnd->global_error_check(); autosave("nothing"); Undo_count = 0; return TRUE; } // For tokenizing #define MAX_FILENAME_LEN_1 31 #if (MAX_FILENAME_LEN_1) != (MAX_FILENAME_LEN - 1) #error MAX_FILENAME_LEN_1 must be equal to MAX_FILENAME_LEN - 1! #endif BOOL CFREDDoc::OnSaveDocument(LPCTSTR pathname) { Fred_mission_save save; if (Mission_save_format == FSO_FORMAT_RETAIL) { save.set_save_format(MissionFormat::RETAIL); } else if (Mission_save_format == FSO_FORMAT_COMPATIBILITY_MODE) { save.set_save_format(MissionFormat::COMPATIBILITY_MODE); } else { save.set_save_format(MissionFormat::STANDARD); } save.set_always_save_display_names(Always_save_display_names); save.set_view_pos(view_pos); save.set_view_orient(view_orient); save.set_fred_alt_names(Fred_alt_names); save.set_fred_callsigns(Fred_callsigns); DWORD attrib; FILE *fp; auto sep_ch = strrchr(pathname, '\\'); auto filename = (sep_ch != nullptr) ? (sep_ch + 1) : pathname; auto len = strlen(filename); if (len >= MAX_FILENAME_LEN) Fred_main_wnd->MessageBox("The filename is too long for FreeSpace. The game will not be able to read this file. Max length, including extension, is " SCP_TOKEN_TO_STR(MAX_FILENAME_LEN_1) " characters.", NULL, MB_OK | MB_ICONEXCLAMATION); // drop extension and copy to Mission_filename auto ext_ch = strrchr(filename, '.'); if (ext_ch != nullptr) len = ext_ch - filename; if (len >= 80) len = 79; strncpy(Mission_filename, filename, len); Mission_filename[len] = 0; Fred_view_wnd->global_error_check(); if (Briefing_dialog) { Briefing_dialog->update_data(1); Briefing_dialog->save_editor_state(); } if (Event_editor_dlg) Fred_main_wnd->MessageBox("Event editor dialog is still open, so changes there won't be saved"); if (Message_editor_dlg) Fred_main_wnd->MessageBox("Message editor dialog is still open, so changes there won't be saved"); fp = fopen(pathname, "r"); if (fp) { fclose(fp); attrib = GetFileAttributes(pathname); if (attrib & FILE_ATTRIBUTE_READONLY) { Fred_main_wnd->MessageBox("File is read-only. You need to check it out before saving to it"); return FALSE; } } if (save.save_mission_file(pathname)) { Fred_main_wnd->MessageBox("An error occured while saving!", NULL, MB_OK | MB_ICONEXCLAMATION); return FALSE; } SetModifiedFlag(FALSE); if (!load_mission(pathname, MPF_FAST_RELOAD)) Error(LOCATION, "Failed attempting to reload mission after saving. Report this bug now!"); if (Briefing_dialog) { Briefing_dialog->restore_editor_state(); Briefing_dialog->update_data(1); } return TRUE; } void CFREDDoc::Serialize(CArchive& ar) { return; /* The original Serialize code int rw_flag; XFILE *fp; // CString CSfilename; // char filename[128], *tfilename; fp = ar.GetFile(); rw_flag = ar.IsStoring(); // CSfilename = fp->GetFileName(); // tfilename = CSfilename.GetBuffer(16); // strcpy_s(filename, tfilename); // CSfilename.ReleaseBuffer(); // -- Don't close this, it gets closed by MFC -- ar.Close(); cfile_serialize(fp, rw_flag); cfile_serialize_editor(fp, rw_flag); */ } void CFREDDoc::recreate_dialogs() { if (Briefing_dialog) { Briefing_dialog->OnCancel(); Briefing_dialog = new briefing_editor_dlg; Briefing_dialog->create(); Briefing_dialog->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); Briefing_dialog->ShowWindow(SW_RESTORE); } if (Bg_bitmap_dialog) { Bg_bitmap_dialog->OnClose(); Bg_bitmap_dialog = new bg_bitmap_dlg; Bg_bitmap_dialog->create(); Bg_bitmap_dialog->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); Bg_bitmap_dialog->ShowWindow(SW_RESTORE); } } void CFREDDoc::UpdateStatus(int flags) { if (FREDDoc_ptr) if (flags & US_WORLD_CHANGED) FREDDoc_ptr->SetModifiedFlag(); } void set_modified(BOOL arg) { Local_modified = arg; FREDDoc_ptr->SetModifiedFlag(arg); } ////////////////////////////////////////////////////////////////////////// // // Below is old, obsolete code, kept around just in case it might be found // useful some time in the future for something. // ////////////////////////////////////////////////////////////////////////// /* #define SerializeFloat(fp, mode, f) if (mode == 1) fp->Write(&f, sizeof(float)); else fp->Read(&f, sizeof(float)) #define SerializeInt(fp, mode, f) if (mode == 1) fp->Write(&f, sizeof(int)); else fp->Read(&f, sizeof(int)) void SerializeVector(XFILE *fp, int mode, vector *v) { SerializeFloat(fp, mode, v->x); SerializeFloat(fp, mode, v->y); SerializeFloat(fp, mode, v->z); } void SerializeMatrix(XFILE *fp, int mode, matrix *m) { SerializeVector(fp, mode, &m->rvec); SerializeVector(fp, mode, &m->uvec); SerializeVector(fp, mode, &m->fvec); } void SerializePhysicsInfo(XFILE *fp, int mode, physics_info *pi) { SerializeFloat(fp, mode, pi->mass); SerializeFloat(fp, mode, pi->drag); SerializeVector(fp, mode, &pi->max_thrust); SerializeVector(fp, mode, &pi->max_rotthrust); SerializeFloat(fp, mode, pi->turnroll); SerializeInt(fp, mode, pi->flags); SerializeVector(fp, mode, &pi->velocity); SerializeVector(fp, mode, &pi->rotvel); SerializeVector(fp, mode, &pi->thrust); SerializeVector(fp, mode, &pi->rotthrust); } ///////////////////////////////////////////////////////////////////////////// // CFREDDoc serialization void SerializeObject(XFILE *fp, int mode, object *objp) { SerializeInt(fp, mode, objp->signature); SerializeInt(fp, mode, objp->type); SerializeInt(fp, mode, objp->parent); SerializeInt(fp, mode, objp->parent_sig); SerializeInt(fp, mode, objp->instance); SerializeInt(fp, mode, objp->flags); SerializeInt(fp, mode, objp->flags2); // Goober5000 - code is obsolete, but I added this just in case SerializeFloat(fp, mode, objp->radius); // SerializeInt(fp, mode, objp->wing); SerializePhysicsInfo(fp, mode, &objp->phys_info); SerializeVector(fp, mode, &objp->pos); SerializeMatrix(fp, mode, &objp->orient); } void SerializeAI(XFILE *fp, int mode, ai_info *aip) { SerializeInt(fp, mode, aip->shipnum); SerializeInt(fp, mode, aip->type); SerializeInt(fp, mode, aip->wing); //MWA -- SerializeInt(fp, mode, aip->current_waypoint); } void SerializeShip(XFILE *fp, int mode, ship *shipp) { SerializeInt(fp, mode, shipp->objnum); SerializeInt(fp, mode, shipp->ai_index); SerializeInt(fp, mode, shipp->subtype); SerializeInt(fp, mode, shipp->modelnum); SerializeInt(fp, mode, shipp->hits); SerializeInt(fp, mode, shipp->dying); } void SerializeGrid(XFILE *fp, int mode, grid *gridp) { int i; SerializeInt(fp, mode, gridp->nrows); SerializeInt(fp, mode, gridp->ncols); SerializeMatrix(fp, mode, &gridp->gmatrix); SerializePhysicsInfo(fp, mode, &gridp->physics); SerializeFloat(fp, mode, gridp->square_size); SerializeFloat(fp, mode, gridp->planeD); for (i=0; igpoints[i]); } void cfile_serialize(XFILE *fp, int flag) { int i; int highest_object_index = 0, highest_ship_index = 0, highest_ai_index = 0; Assert((flag == 0) || (flag == 1)); // fp = cfopen(filename, flag ? "wb" : "rb"); // if (!fp) // Fred_view_wnd->MessageBox(strerror(errno), "File Open Error!", MB_ICONSTOP); // Find highest used object if writing. if (flag == 1) { for (i=MAX_OBJECTS-1; i>0; i--) if (Objects[i].type != OBJ_NONE) { highest_object_index = i; break; } } if (flag == 0) { num_ships = 0; Num_objects = 0; } SerializeInt(fp, flag, highest_object_index); for (i=1; i<=highest_object_index; i++) { SerializeObject(fp, flag, &Objects[i]); if (flag == 0) if (Objects[i].type != OBJ_NONE) Num_objects++; } // Read/write ships if (flag == 1) { for (i=MAX_SHIPS-1; i>0; i--) if (Ships[i].objnum) { highest_ship_index = i; break; } } SerializeInt(fp, flag, highest_ship_index); for (i=1; i<=highest_ship_index; i++) { SerializeShip(fp, flag, &Ships[i]); if (flag == 0) if (Ships[i].objnum) num_ships++; } // Read/write AI info if (flag == 1) { for (i=MAX_AI_INFO-1; i>0; i--) if (Ai_info[i].shipnum) { highest_ai_index = i; break; } } SerializeInt(fp, flag, highest_ai_index); for (i=1; i<=highest_ai_index; i++) SerializeAI(fp, flag, &Ai_info[i]); } void cfile_serialize_editor(XFILE *fp, int flag) { // Editor only stuff SerializeMatrix(fp, flag, &view_orient); SerializeVector(fp, flag, &view_pos); SerializeInt(fp, flag, Control_mode); SerializeInt(fp, flag, cur_object_index); SerializeInt(fp, flag, cur_wing); SerializeGrid(fp, flag, The_grid); } */