123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- // AzCore
- #include <AzCore/IO/IStreamer.h>
- #include <AzCore/std/parallel/binary_semaphore.h>
- // CryCommon
- #include <CryCommon/ILevelSystem.h>
- // Editor
- #include "GameExporter.h"
- #include "GameEngine.h"
- #include "CryEditDoc.h"
- #include "UsedResources.h"
- #include "WaitProgress.h"
- #include "Util/CryMemFile.h"
- #include "Objects/ObjectManager.h"
- #include "Objects/EntityObject.h"
- #include <AzToolsFramework/Entity/EditorEntityContextBus.h>
- //////////////////////////////////////////////////////////////////////////
- #define MUSIC_LEVEL_LIBRARY_FILE "music.xml"
- #define MATERIAL_LEVEL_LIBRARY_FILE "materials.xml"
- #define RESOURCE_LIST_FILE "resourcelist.txt"
- #define USED_RESOURCE_LIST_FILE "usedresourcelist.txt"
- #define SHADER_LIST_FILE "shaderslist.txt"
- #define GetAValue(rgb) ((BYTE)((rgb) >> 24))
- //////////////////////////////////////////////////////////////////////////
- // SGameExporterSettings
- //////////////////////////////////////////////////////////////////////////
- SGameExporterSettings::SGameExporterSettings()
- : iExportTexWidth(4096)
- , nApplySS(1)
- {
- }
- //////////////////////////////////////////////////////////////////////////
- void SGameExporterSettings::SetLowQuality()
- {
- iExportTexWidth = 4096;
- nApplySS = 0;
- }
- //////////////////////////////////////////////////////////////////////////
- void SGameExporterSettings::SetHiQuality()
- {
- iExportTexWidth = 16384;
- nApplySS = 1;
- }
- CGameExporter* CGameExporter::m_pCurrentExporter = nullptr;
- //////////////////////////////////////////////////////////////////////////
- // CGameExporter
- //////////////////////////////////////////////////////////////////////////
- CGameExporter::CGameExporter()
- : m_bAutoExportMode(false)
- {
- m_pCurrentExporter = this;
- }
- CGameExporter::~CGameExporter()
- {
- m_pCurrentExporter = nullptr;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CGameExporter::Export(unsigned int flags, [[maybe_unused]] EEndian eExportEndian, const char* subdirectory)
- {
- CAutoDocNotReady autoDocNotReady;
- QWaitCursor waitCursor;
- IEditor* pEditor = GetIEditor();
- CGameEngine* pGameEngine = pEditor->GetGameEngine();
- if (pGameEngine->GetLevelPath().isEmpty())
- {
- return false;
- }
- bool exportSuccessful = true;
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorBeginLevelExport);
- bool usePrefabSystemForLevels = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(
- usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
- if (usePrefabSystemForLevels)
- {
- // Level.pak and all the data contained within it is unused when using the prefab system for levels, so there's nothing
- // to do here.
- CCryEditDoc* pDocument = pEditor->GetDocument();
- pDocument->SetLevelExported(true);
- }
- else
- {
- QDir::setCurrent(pEditor->GetPrimaryCDFolder());
- QString sLevelPath = Path::AddSlash(pGameEngine->GetLevelPath());
- if (subdirectory && subdirectory[0] && strcmp(subdirectory, ".") != 0)
- {
- sLevelPath = Path::AddSlash(sLevelPath + subdirectory);
- QDir().mkpath(sLevelPath);
- }
- m_levelPak.m_sPath = QString(sLevelPath) + GetLevelPakFilename();
- m_levelPath = Path::RemoveBackslash(sLevelPath);
- QString rootLevelPath = Path::AddSlash(pGameEngine->GetLevelPath());
- CCryEditDoc* pDocument = pEditor->GetDocument();
- if (flags & eExp_Fast)
- {
- m_settings.SetLowQuality();
- }
- else if (m_bAutoExportMode)
- {
- m_settings.SetHiQuality();
- }
- AZStd::scoped_lock autoLock(CGameEngine::GetPakModifyMutex());
- // Close this pak file.
- if (!CloseLevelPack(m_levelPak, true))
- {
- Error("Cannot close Pak file " + m_levelPak.m_sPath);
- exportSuccessful = false;
- }
- if (exportSuccessful && m_bAutoExportMode)
- {
- // Remove read-only flags.
- auto perms = QFile::permissions(m_levelPak.m_sPath) | QFile::Permission::WriteOwner;
- QFile::setPermissions(m_levelPak.m_sPath, perms);
- }
- //////////////////////////////////////////////////////////////////////////
- if (exportSuccessful)
- {
- if (!CFileUtil::OverwriteFile(m_levelPak.m_sPath))
- {
- Error("Cannot overwrite Pak file " + m_levelPak.m_sPath);
- exportSuccessful = false;
- }
- }
- if (exportSuccessful)
- {
- if (!OpenLevelPack(m_levelPak, false))
- {
- Error("Cannot open Pak file " + m_levelPak.m_sPath + " for writing.");
- exportSuccessful = false;
- }
- }
- ////////////////////////////////////////////////////////////////////////
- // Export all data to the game
- ////////////////////////////////////////////////////////////////////////
- if (exportSuccessful)
- {
- ////////////////////////////////////////////////////////////////////////
- // Exporting map setttings
- ////////////////////////////////////////////////////////////////////////
- //! Export Level data.
- CLogFile::WriteLine("Exporting leveldata.xml");
- ExportLevelData(sLevelPath);
- CLogFile::WriteLine("Exporting leveldata.xml done.");
- ExportLevelInfo(sLevelPath);
- ExportLevelResourceList(sLevelPath);
- ExportLevelUsedResourceList(sLevelPath);
- //////////////////////////////////////////////////////////////////////////
- // End Exporting Game data.
- //////////////////////////////////////////////////////////////////////////
- // Close all packs.
- CloseLevelPack(m_levelPak, false);
- // m_texturePakFile.Close();
- pEditor->SetStatusText(QObject::tr("Ready"));
- // Reopen this pak file.
- if (!OpenLevelPack(m_levelPak, true))
- {
- Error("Cannot open Pak file " + m_levelPak.m_sPath);
- exportSuccessful = false;
- }
- }
- if (exportSuccessful)
- {
- // Commit changes to the disk.
- _flushall();
- // finally create filelist.xml
- QString levelName = Path::GetFileName(pGameEngine->GetLevelPath());
- ExportFileList(sLevelPath, levelName);
- pDocument->SetLevelExported(true);
- }
- }
- // Always notify that we've finished exporting, whether it was successful or not.
- CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorEndLevelExport, exportSuccessful);
- if (exportSuccessful)
- {
- // Notify the level system that there's a new level, so that the level info is populated.
- gEnv->pSystem->GetILevelSystem()->Rescan(ILevelSystem::GetLevelsDirectoryName());
- CLogFile::WriteLine("Exporting was successful.");
- }
- return exportSuccessful;
- }
- //////////////////////////////////////////////////////////////////////////
- void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/)
- {
- IEditor* pEditor = GetIEditor();
- pEditor->SetStatusText(QObject::tr("Exporting leveldata.xml..."));
- char versionString[256];
- pEditor->GetFileVersion().ToString(versionString);
- XmlNodeRef root = XmlHelpers::CreateXmlNode("leveldata");
- root->setAttr("SandboxVersion", versionString);
- XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("leveldataaction");
- rootAction->setAttr("SandboxVersion", versionString);
- //////////////////////////////////////////////////////////////////////////
- // Save Level Data XML
- //////////////////////////////////////////////////////////////////////////
- QString levelDataFile = path + "leveldata.xml";
- XmlString xmlData = root->getXML();
- CCryMemFile file;
- file.Write(xmlData.c_str(), static_cast<unsigned int>(xmlData.length()));
- m_levelPak.m_pakFile.UpdateFile(levelDataFile.toUtf8().data(), file);
- QString levelDataActionFile = path + "leveldataaction.xml";
- XmlString xmlDataAction = rootAction->getXML();
- CCryMemFile fileAction;
- fileAction.Write(xmlDataAction.c_str(), static_cast<unsigned int>(xmlDataAction.length()));
- m_levelPak.m_pakFile.UpdateFile(levelDataActionFile.toUtf8().data(), fileAction);
- AZStd::vector<char> entitySaveBuffer;
- AZ::IO::ByteContainerStream<AZStd::vector<char> > entitySaveStream(&entitySaveBuffer);
- bool savedEntities = false;
- AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
- savedEntities,
- &AzToolsFramework::EditorEntityContextRequestBus::Events::SaveToStreamForGame,
- entitySaveStream,
- AZ::DataStream::ST_BINARY);
- if (savedEntities)
- {
- QString entitiesFile;
- entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "mission0");
- m_levelPak.m_pakFile.UpdateFile(entitiesFile.toUtf8().data(), entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CGameExporter::ExportLevelInfo(const QString& path)
- {
- //////////////////////////////////////////////////////////////////////////
- // Export short level info xml.
- //////////////////////////////////////////////////////////////////////////
- IEditor* pEditor = GetIEditor();
- XmlNodeRef root = XmlHelpers::CreateXmlNode("LevelInfo");
- char versionString[256];
- pEditor->GetFileVersion().ToString(versionString);
- root->setAttr("SandboxVersion", versionString);
- QString levelName = pEditor->GetGameEngine()->GetLevelPath();
- root->setAttr("Name", levelName.toUtf8().data());
- //////////////////////////////////////////////////////////////////////////
- // Save LevelInfo file.
- //////////////////////////////////////////////////////////////////////////
- QString filename = path + "levelinfo.xml";
- XmlString xmlData = root->getXML();
- CCryMemFile file;
- file.Write(xmlData.c_str(), static_cast<unsigned int>(xmlData.length()));
- m_levelPak.m_pakFile.UpdateFile(filename.toUtf8().data(), file);
- }
- //////////////////////////////////////////////////////////////////////////
- void CGameExporter::ExportLevelResourceList(const QString& path)
- {
- auto pResList = gEnv->pCryPak->GetResourceList(AZ::IO::IArchive::RFOM_Level);
- // Write resource list to file.
- CCryMemFile memFile;
- for (const char* filename = pResList->GetFirst(); filename; filename = pResList->GetNext())
- {
- memFile.Write(filename, static_cast<unsigned int>(strlen(filename)));
- memFile.Write("\n", 1);
- }
- QString resFile = Path::Make(path, RESOURCE_LIST_FILE);
- m_levelPak.m_pakFile.UpdateFile(resFile.toUtf8().data(), memFile, true);
- }
- //////////////////////////////////////////////////////////////////////////
- void CGameExporter::ExportLevelUsedResourceList(const QString& path)
- {
- // Write used resource list to file.
- CCryMemFile memFile;
- CUsedResources resources;
- GetIEditor()->GetObjectManager()->GatherUsedResources(resources);
- for (CUsedResources::TResourceFiles::const_iterator it = resources.files.begin(); it != resources.files.end(); it++)
- {
- QString filePath = Path::MakeGamePath(*it).toLower();
- memFile.Write(filePath.toUtf8().data(), filePath.toUtf8().length());
- memFile.Write("\n", 1);
- }
- QString resFile = Path::Make(path, USED_RESOURCE_LIST_FILE);
- m_levelPak.m_pakFile.UpdateFile(resFile.toUtf8().data(), memFile, true);
- }
- //////////////////////////////////////////////////////////////////////////
- void CGameExporter::ExportFileList(const QString& path, const QString& levelName)
- {
- // process the folder of the specified map name, producing a filelist.xml file
- // that can later be used for map downloads
- AZStd::string newpath;
- AZStd::string filename = levelName.toUtf8().data();
- AZStd::string mapname = (filename + ".dds");
- AZStd::string metaname = (filename + ".xml");
- XmlNodeRef rootNode = gEnv->pSystem->CreateXmlNode("download");
- rootNode->setAttr("name", filename.c_str());
- rootNode->setAttr("type", "Map");
- XmlNodeRef indexNode = rootNode->newChild("index");
- if (indexNode)
- {
- indexNode->setAttr("src", "filelist.xml");
- indexNode->setAttr("dest", "filelist.xml");
- }
- XmlNodeRef filesNode = rootNode->newChild("files");
- if (filesNode)
- {
- newpath = GetIEditor()->GetGameEngine()->GetLevelPath().toUtf8().data();
- newpath += "/*";
- AZ::IO::ArchiveFileIterator handle = gEnv->pCryPak->FindFirst(newpath.c_str());
- if (!handle)
- {
- return;
- }
- do
- {
- // ignore "." and ".."
- if (handle.m_filename.front() == '.')
- {
- continue;
- }
- // do we need any files from sub directories?
- if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
- {
- continue;
- }
- // only need the following files for multiplayer downloads
- if (!_stricmp(handle.m_filename.data(), GetLevelPakFilename())
- || !_stricmp(handle.m_filename.data(), mapname.c_str())
- || !_stricmp(handle.m_filename.data(), metaname.c_str()))
- {
- XmlNodeRef newFileNode = filesNode->newChild("file");
- if (newFileNode)
- {
- // TEMP: this is just for testing. src probably needs to be blank.
- // string src = "http://server41/updater/";
- // src += m_levelName;
- // src += "/";
- // src += fileinfo.name;
- newFileNode->setAttr("src", handle.m_filename.data());
- newFileNode->setAttr("dest", handle.m_filename.data());
- newFileNode->setAttr("size", handle.m_fileDesc.nSize);
- }
- }
- } while (handle = gEnv->pCryPak->FindNext(handle));
- gEnv->pCryPak->FindClose (handle);
- }
- // save filelist.xml
- newpath = path.toUtf8().data();
- newpath += "/filelist.xml";
- rootNode->saveToFile(newpath.c_str());
- }
- void CGameExporter::Error(const QString& error)
- {
- if (m_bAutoExportMode)
- {
- CLogFile::WriteLine((QString("Export failed! ") + error).toUtf8().data());
- }
- else
- {
- Warning((QString("Export failed! ") + error).toUtf8().data());
- }
- }
- bool CGameExporter::OpenLevelPack(SLevelPakHelper& lphelper, bool bCryPak)
- {
- bool bRet = false;
- assert(lphelper.m_bPakOpened == false);
- assert(lphelper.m_bPakOpenedCryPak == false);
- if (bCryPak)
- {
- assert(!lphelper.m_sPath.isEmpty());
- bRet = gEnv->pCryPak->OpenPack(lphelper.m_sPath.toUtf8().data());
- assert(bRet);
- lphelper.m_bPakOpenedCryPak = true;
- }
- else
- {
- bRet = lphelper.m_pakFile.Open(lphelper.m_sPath.toUtf8().data());
- assert(bRet);
- lphelper.m_bPakOpened = true;
- }
- return bRet;
- }
- bool CGameExporter::CloseLevelPack(SLevelPakHelper& lphelper, bool bCryPak)
- {
- bool bRet = false;
- if (bCryPak)
- {
- assert(lphelper.m_bPakOpenedCryPak == true);
- assert(!lphelper.m_sPath.isEmpty());
- bRet = gEnv->pCryPak->ClosePack(lphelper.m_sPath.toUtf8().data());
- assert(bRet);
- lphelper.m_bPakOpenedCryPak = false;
- }
- else
- {
- assert(lphelper.m_bPakOpened == true);
- lphelper.m_pakFile.Close();
- bRet = true;
- lphelper.m_bPakOpened = false;
- }
- assert(lphelper.m_bPakOpened == false);
- assert(lphelper.m_bPakOpenedCryPak == false);
- return bRet;
- }
|