Пример #1
0
PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)
{
	VfsPath filename_xml = pathname.ChangeExtension(L".xml");

	CXeromyces xmb_file;
	if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK)
		return PSRETURN_File_ReadFailed;

	// Define all the relevant elements used in the XML file
	#define EL(x) int el_##x = xmb_file.GetElementID(#x)
	#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
	EL(scenario);
	EL(scriptsettings);
	#undef AT
	#undef EL

	XMBElement root = xmb_file.GetRoot();
	ENSURE(root.GetNodeName() == el_scenario);

	XERO_ITER_EL(root, child)
	{
		int child_name = child.GetNodeName();
		if (child_name == el_scriptsettings)
		{
			m_ScriptSettings = child.GetText();
		}
	}
Пример #2
0
///////////////////////////////////////////////////////////////////////////////////////////////////
// SaveMap: try to save the current map to the given file
void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
						 WaterManager* pWaterMan, SkyManager* pSkyMan,
						 CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
						 CPostprocManager* pPostproc,
						 CSimulation2* pSimulation2)
{
	CFilePacker packer(FILE_VERSION, "PSMP");

	// build necessary data
	PackMap(packer, pTerrain);

	try
	{
		// write it out
		packer.Write(pathname);
	}
	catch (PSERROR_File_WriteFailed&)
	{
		LOGERROR("Failed to write map '%s'", pathname.string8());
		return;
	}

	VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
	WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema, pPostproc, pSimulation2);
}
Пример #3
0
Status CComponentManager::FindJSONFilesCallback(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
{
	FindJSONFilesCallbackData* data = (FindJSONFilesCallbackData*)cbData;
	
	VfsPath pathstem = pathname.ChangeExtension(L"");
	// Strip the root from the path
	std::wstring name = pathstem.string().substr(data->path.string().length());

	data->templates.push_back(std::string(name.begin(), name.end()));

	return INFO::OK;
}
Пример #4
0
// <extension> identifies the file format that is to be written
// (case-insensitive). examples: "bmp", "png", "jpg".
// BMP is good for quick output at the expense of large files.
void WriteScreenshot(const VfsPath& extension)
{
    // get next available numbered filename
    // note: %04d -> always 4 digits, so sorting by filename works correctly.
    const VfsPath basenameFormat(L"screenshots/screenshot%04d");
    const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
    VfsPath filename;
    vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);

    const size_t w = (size_t)g_xres, h = (size_t)g_yres;
    const size_t bpp = 24;
    GLenum fmt = GL_RGB;
    int flags = TEX_BOTTOM_UP;
    // we want writing BMP to be as fast as possible,
    // so read data from OpenGL in BMP format to obviate conversion.
    if(extension == L".bmp")
    {
#if !CONFIG2_GLES // GLES doesn't support BGR
        fmt = GL_BGR;
        flags |= TEX_BGR;
#endif
    }

    // Hide log messages and re-render
    RenderLogger(false);
    Render();
    RenderLogger(true);

    const size_t img_size = w * h * bpp/8;
    const size_t hdr_size = tex_hdr_size(filename);
    shared_ptr<u8> buf;
    AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
    GLvoid* img = buf.get() + hdr_size;
    Tex t;
    if(t.wrap(w, h, bpp, flags, buf, hdr_size) < 0)
        return;
    glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img);

    if (tex_write(&t, filename) == INFO::OK)
    {
        OsPath realPath;
        g_VFS->GetRealPath(filename, realPath);

        LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());

        debug_printf(
            CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
            realPath.string8().c_str());
    }
    else
        LOGERROR("Error writing screenshot to '%s'", filename.string8());
}
Пример #5
0
static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData)
{
	std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;

	// Strip the .xml extension
	VfsPath pathstem = pathname.ChangeExtension(L"");
	// Strip the root from the path
	std::wstring name = pathstem.string().substr(ARRAY_SIZE(TEMPLATE_ROOT)-1);

	// We want to ignore template_*.xml templates, since they should never be built in the editor
	if (name.substr(0, 9) == L"template_")
		return INFO::OK;

	templates.push_back(std::string(name.begin(), name.end()));
	return INFO::OK;
}
Пример #6
0
///////////////////////////////////////////////////////////////////////////////////////////////////
// SaveMap: try to save the current map to the given file
void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
						 WaterManager* pWaterMan, SkyManager* pSkyMan,
						 CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
						 CPostprocManager* pPostproc,
						 CSimulation2* pSimulation2)
{
	CFilePacker packer(FILE_VERSION, "PSMP");

	// build necessary data
	PackMap(packer, pTerrain);

	// write it out
	packer.Write(pathname);

	VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
	WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema, pPostproc, pSimulation2);
}
Пример #7
0
Status SavedGames::Load(const std::wstring& name, ScriptInterface& scriptInterface, CScriptValRooted& metadata, std::string& savedState)
{
	// Determine the filename to load
	const VfsPath basename(L"saves/" + name);
	const VfsPath filename = basename.ChangeExtension(L".0adsave");

	OsPath realPath;
	WARN_RETURN_STATUS_IF_ERR(g_VFS->GetRealPath(filename, realPath));

	PIArchiveReader archiveReader = CreateArchiveReader_Zip(realPath);
	if (!archiveReader)
		WARN_RETURN(ERR::FAIL);

	CGameLoader loader(scriptInterface, &metadata, &savedState);
	WARN_RETURN_STATUS_IF_ERR(archiveReader->ReadEntries(CGameLoader::ReadEntryCallback, (uintptr_t)&loader));

	return INFO::OK;	
}
Пример #8
0
// basename is e.g. "console"; the files are "fonts/console.fnt" and "fonts/console.png"
// [10..70ms]
static Status UniFont_reload(UniFont* f, const PIVFS& vfs, const VfsPath& basename, Handle UNUSED(h))
{
	// already loaded
	if(f->ht > 0)
		return INFO::OK;

	f->glyphs = new glyphmap();

	const VfsPath path(L"fonts/");

	// Read font definition file into a stringstream
	shared_ptr<u8> buf; size_t size;
	const VfsPath fntName(basename.ChangeExtension(L".fnt"));
	RETURN_STATUS_IF_ERR(vfs->LoadFile(path / fntName, buf, size));	// [cumulative for 12: 36ms]
	std::istringstream FNTStream(std::string((const char*)buf.get(), size));

	int Version;
	FNTStream >> Version;
	if (Version < 100 || Version > 101) // Make sure this is from a recent version of the font builder
		WARN_RETURN(ERR::FAIL);

	int TextureWidth, TextureHeight;
	FNTStream >> TextureWidth >> TextureHeight;

	GLenum fmt_ovr = GL_ALPHA;
	if (Version >= 101)
	{
		std::string Format;
		FNTStream >> Format;
		if (Format == "rgba")
			fmt_ovr = GL_RGBA;
		else if (Format == "a")
			fmt_ovr = GL_ALPHA;
		else
			debug_warn(L"Invalid .fnt format string");
	}
Пример #9
0
// Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles.
void WriteBigScreenshot(const VfsPath& extension, int tiles)
{
    // If the game hasn't started yet then use WriteScreenshot to generate the image.
    if(g_Game == NULL) {
        WriteScreenshot(L".bmp");
        return;
    }

    // get next available numbered filename
    // note: %04d -> always 4 digits, so sorting by filename works correctly.
    const VfsPath basenameFormat(L"screenshots/screenshot%04d");
    const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
    VfsPath filename;
    vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);

    // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
    // hope the screen is actually large enough for that.
    const int tile_w = 640, tile_h = 480;
    ENSURE(g_xres >= tile_w && g_yres >= tile_h);

    const int img_w = tile_w*tiles, img_h = tile_h*tiles;
    const int bpp = 24;
    GLenum fmt = GL_RGB;
    int flags = TEX_BOTTOM_UP;
    // we want writing BMP to be as fast as possible,
    // so read data from OpenGL in BMP format to obviate conversion.
    if(extension == L".bmp")
    {
#if !CONFIG2_GLES // GLES doesn't support BGR
        fmt = GL_BGR;
        flags |= TEX_BGR;
#endif
    }

    const size_t img_size = img_w * img_h * bpp/8;
    const size_t tile_size = tile_w * tile_h * bpp/8;
    const size_t hdr_size = tex_hdr_size(filename);
    void* tile_data = malloc(tile_size);
    if(!tile_data)
    {
        WARN_IF_ERR(ERR::NO_MEM);
        return;
    }
    shared_ptr<u8> img_buf;
    AllocateAligned(img_buf, hdr_size+img_size, maxSectorSize);

    Tex t;
    GLvoid* img = img_buf.get() + hdr_size;
    if(t.wrap(img_w, img_h, bpp, flags, img_buf, hdr_size) < 0)
    {
        free(tile_data);
        return;
    }

    ogl_WarnIfError();

    // Resize various things so that the sizes and aspect ratios are correct
    {
        g_Renderer.Resize(tile_w, tile_h);
        SViewPort vp = { 0, 0, tile_w, tile_h };
        g_Game->GetView()->GetCamera()->SetViewPort(vp);
        g_Game->GetView()->SetCameraProjection();
    }

#if !CONFIG2_GLES
    // Temporarily move everything onto the front buffer, so the user can
    // see the exciting progress as it renders (and can tell when it's finished).
    // (It doesn't just use SwapBuffers, because it doesn't know whether to
    // call the SDL version or the Atlas version.)
    GLint oldReadBuffer, oldDrawBuffer;
    glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer);
    glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer);
    glDrawBuffer(GL_FRONT);
    glReadBuffer(GL_FRONT);
#endif

    // Hide the cursor
    CStrW oldCursor = g_CursorName;
    g_CursorName = L"";

    // Render each tile
    for (int tile_y = 0; tile_y < tiles; ++tile_y)
    {
        for (int tile_x = 0; tile_x < tiles; ++tile_x)
        {
            // Adjust the camera to render the appropriate region
            g_Game->GetView()->GetCamera()->SetProjectionTile(tiles, tile_x, tile_y);

            RenderLogger(false);
            RenderGui(false);
            Render();
            RenderGui(true);
            RenderLogger(true);

            // Copy the tile pixels into the main image
            glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data);
            for (int y = 0; y < tile_h; ++y)
            {
                void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8;
                void* src = (char*)tile_data + y * tile_w * bpp/8;
                memcpy(dest, src, tile_w * bpp/8);
            }
        }
    }

    // Restore the old cursor
    g_CursorName = oldCursor;

#if !CONFIG2_GLES
    // Restore the buffer settings
    glDrawBuffer(oldDrawBuffer);
    glReadBuffer(oldReadBuffer);
#endif

    // Restore the viewport settings
    {
        g_Renderer.Resize(g_xres, g_yres);
        SViewPort vp = { 0, 0, g_xres, g_yres };
        g_Game->GetView()->GetCamera()->SetViewPort(vp);
        g_Game->GetView()->SetCameraProjection();
        g_Game->GetView()->GetCamera()->SetProjectionTile(1, 0, 0);
    }

    if (tex_write(&t, filename) == INFO::OK)
    {
        OsPath realPath;
        g_VFS->GetRealPath(filename, realPath);

        LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());

        debug_printf(
            CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
            realPath.string8().c_str());
    }
    else
        LOGERROR("Error writing screenshot to '%s'", filename.string8());

    free(tile_data);
}
Пример #10
0
VfsPath CColladaManager::GetLoadableFilename(const VfsPath& pathnameNoExtension, FileType type)
{
	std::wstring extn;
	switch (type)
	{
	case PMD: extn = L".pmd"; break;
	case PSA: extn = L".psa"; break;
		// no other alternatives
	}

	/*

	If there is a .dae file:
		* Calculate a hash to identify it.
		* Look for a cached .pmd file matching that hash.
		* If it exists, load it. Else, convert the .dae into .pmd and load it.
	Otherwise, if there is a (non-cache) .pmd file:
		* Load it.
	Else, fail.

	The hash calculation ought to be fast, since normally (during development)
	the .dae file will exist but won't have changed recently and so the cache
	would be used. Hence, just hash the file's size, mtime, and the converter
	version number (so updates of the converter can cause regeneration of .pmds)
	instead of the file's actual contents.

	TODO (maybe): The .dae -> .pmd conversion may fail (e.g. if the .dae is
	invalid or unsupported), but it may take a long time to start the conversion
	then realise it's not going to work. That will delay the loading of the game
	every time, which is annoying, so maybe it should cache the error message
	until the .dae is updated and fixed. (Alternatively, avoid having that many
	broken .daes in the game.)

	*/

	// (TODO: the comments and variable names say "pmd" but actually they can
	// be "psa" too.)

	VfsPath dae(pathnameNoExtension.ChangeExtension(L".dae"));
	if (! VfsFileExists(dae))
	{
		// No .dae - got to use the .pmd, assuming there is one
		return pathnameNoExtension.ChangeExtension(extn);
	}

	// There is a .dae - see if there's an up-to-date cached copy

	FileInfo fileInfo;
	if (g_VFS->GetFileInfo(dae, &fileInfo) < 0)
	{
		// This shouldn't occur for any sensible reasons
		LOGERROR(L"Failed to stat DAE file '%ls'", dae.string().c_str());
		return VfsPath();
	}

	// Build a struct of all the data we want to hash.
	// (Use ints and not time_t/off_t because we don't care about overflow
	// but do care about the fields not being 64-bit aligned)
	// (Remove the lowest bit of mtime because some things round it to a
	// resolution of 2 seconds)
#pragma pack(push, 1)
	struct { int version; int mtime; int size; } hashSource
		= { COLLADA_CONVERTER_VERSION, (int)fileInfo.MTime() & ~1, (int)fileInfo.Size() };
	cassert(sizeof(hashSource) == sizeof(int) * 3); // no padding, because that would be bad
#pragma pack(pop)

	// Calculate the hash, convert to hex
	u32 hash = fnv_hash(static_cast<void*>(&hashSource), sizeof(hashSource));
	wchar_t hashString[9];
	swprintf_s(hashString, ARRAY_SIZE(hashString), L"%08x", hash);
	std::wstring extension(L"_");
	extension += hashString;
	extension += extn;

	// realDaePath_ is "[..]/mods/whatever/art/meshes/whatever.dae"
	OsPath realDaePath_;
	Status ret = g_VFS->GetRealPath(dae, realDaePath_);
	ENSURE(ret == INFO::OK);
	wchar_t realDaeBuf[PATH_MAX];
	wcscpy_s(realDaeBuf, ARRAY_SIZE(realDaeBuf), realDaePath_.string().c_str());
	std::replace(realDaeBuf, realDaeBuf+ARRAY_SIZE(realDaeBuf), '\\', '/');
	const wchar_t* realDaePath = wcsstr(realDaeBuf, L"mods/");

	// cachedPmdVfsPath is "cache/mods/whatever/art/meshes/whatever_{hash}.pmd"
	VfsPath cachedPmdVfsPath = VfsPath("cache") / realDaePath;
	cachedPmdVfsPath = cachedPmdVfsPath.ChangeExtension(extension);

	// If it's not in the cache, we'll have to create it first
	if (! VfsFileExists(cachedPmdVfsPath))
	{
		if (! m->Convert(dae, cachedPmdVfsPath, type))
			return L""; // failed to convert
	}

	return cachedPmdVfsPath;
}
Пример #11
0
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
						 WaterManager* pWaterMan_, SkyManager* pSkyMan_,
						 CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_,
						 CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities)
{
	// latch parameters (held until DelayedLoadFinished)
	pTerrain = pTerrain_;
	pLightEnv = pLightEnv_;
	pGameView = pGameView_;
	pWaterMan = pWaterMan_;
	pSkyMan = pSkyMan_;
	pCinema = pCinema_;
	pTrigMan = pTrigMan_;
	pSimulation2 = pSimulation2_;
	pSimContext = pSimContext_;
	m_PlayerID = playerID_;
	m_SkipEntities = skipEntities;
	m_StartingCameraTarget = INVALID_ENTITY;

	filename_xml = pathname.ChangeExtension(L".xml");

	// In some cases (particularly tests) we don't want to bother storing a large
	// mostly-empty .pmp file, so we let the XML file specify basic terrain instead.
	// If there's an .xml file and no .pmp, then we're probably in this XML-only mode
	only_xml = false;
	if (!VfsFileExists(pathname) && VfsFileExists(filename_xml))
	{
		only_xml = true;
	}

	file_format_version = CMapIO::FILE_VERSION; // default if there's no .pmp

	if (!only_xml)
	{
		// [25ms]
		unpacker.Read(pathname, "PSMP");
		file_format_version = unpacker.GetVersion();
	}

	// check oldest supported version
	if (file_format_version < FILE_READ_VERSION)
		throw PSERROR_File_InvalidVersion();

	// delete all existing entities
	if (pSimulation2)
		pSimulation2->ResetState();

	// load map settings script
	RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50);

	// load player settings script (must be done before reading map)
	RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50);

	// unpack the data
	if (!only_xml)
		RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200);

	// read the corresponding XML file
	RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800);

	// apply data to the world
	RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);

	// load map settings script (must be done after reading map)
	RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5);

	RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5);
}
Пример #12
0
VfsPath CCacheLoader::LooseCachePath(const VfsPath& sourcePath, const MD5& initialHash, u32 version)
{
	CFileInfo fileInfo;
	if (m_VFS->GetFileInfo(sourcePath, &fileInfo) < 0)
	{
		debug_warn(L"source file disappeared"); // this should never happen
		return VfsPath();
	}

	u64 mtime = (u64)fileInfo.MTime() & ~1; // skip lowest bit, since zip and FAT don't preserve it
	u64 size = (u64)fileInfo.Size();

	// Construct a hash of the file data and settings.

	MD5 hash = initialHash;
	hash.Update((const u8*)&mtime, sizeof(mtime));
	hash.Update((const u8*)&size, sizeof(size));
	hash.Update((const u8*)&version, sizeof(version));
	// these are local cached files, so we don't care about endianness etc

	// Use a short prefix of the full hash (we don't need high collision-resistance),
	// converted to hex
	u8 digest[MD5::DIGESTSIZE];
	hash.Final(digest);
	std::wstringstream digestPrefix;
	digestPrefix << std::hex;
	for (size_t i = 0; i < 8; ++i)
		digestPrefix << std::setfill(L'0') << std::setw(2) << (int)digest[i];

	// Get the mod path
	OsPath path;
	m_VFS->GetRealPath(sourcePath, path);

	// Construct the final path
	return VfsPath("cache") / path_name_only(path.BeforeCommon(sourcePath).Parent().string().c_str()) / sourcePath.ChangeExtension(sourcePath.Extension().string() + L"." + digestPrefix.str() + m_FileExtension);
}
Пример #13
0
VfsPath CCacheLoader::ArchiveCachePath(const VfsPath& sourcePath)
{
	return sourcePath.ChangeExtension(sourcePath.Extension().string() + L".cached" + m_FileExtension);
}
Пример #14
0
Status SavedGames::Save(const std::wstring& prefix, CSimulation2& simulation, CGUIManager* gui, int playerID)
{
	// Determine the filename to save under
	const VfsPath basenameFormat(L"saves/" + prefix + L"-%04d");
	const VfsPath filenameFormat = basenameFormat.ChangeExtension(L".0adsave");
	VfsPath filename;

	// Don't make this a static global like NextNumberedFilename expects, because
	// that wouldn't work when 'prefix' changes, and because it's not thread-safe
	size_t nextSaveNumber = 0;
	vfs::NextNumberedFilename(g_VFS, filenameFormat, nextSaveNumber, filename);

	// ArchiveWriter_Zip can only write to OsPaths, not VfsPaths,
	// but we'd like to handle saved games via VFS.
	// To avoid potential confusion from writing with non-VFS then
	// reading the same file with VFS, we'll just write to a temporary
	// non-VFS path and then load and save again via VFS,
	// which is kind of a hack.

	OsPath tempSaveFileRealPath;
	WARN_RETURN_STATUS_IF_ERR(g_VFS->GetDirectoryRealPath("cache/", tempSaveFileRealPath));
	tempSaveFileRealPath = tempSaveFileRealPath / "temp.0adsave";

	time_t now = time(NULL);

	// Construct the serialized state to be saved

	std::stringstream simStateStream;
	if (!simulation.SerializeState(simStateStream))
		WARN_RETURN(ERR::FAIL);

	CScriptValRooted metadata;
	simulation.GetScriptInterface().Eval("({})", metadata);
	simulation.GetScriptInterface().SetProperty(metadata.get(), "version_major", SAVED_GAME_VERSION_MAJOR);
	simulation.GetScriptInterface().SetProperty(metadata.get(), "version_minor", SAVED_GAME_VERSION_MINOR);
	simulation.GetScriptInterface().SetProperty(metadata.get(), "time", (double)now);
	simulation.GetScriptInterface().SetProperty(metadata.get(), "player", playerID);
	simulation.GetScriptInterface().SetProperty(metadata.get(), "initAttributes", simulation.GetInitAttributes());
	if (gui)
	{
		CScriptVal guiMetadata = simulation.GetScriptInterface().CloneValueFromOtherContext(gui->GetScriptInterface(), gui->GetSavedGameData().get());
		simulation.GetScriptInterface().SetProperty(metadata.get(), "gui", guiMetadata);
	}
	
	std::string metadataString = simulation.GetScriptInterface().StringifyJSON(metadata.get(), true);
	
	// Write the saved game as zip file containing the various components
	PIArchiveWriter archiveWriter = CreateArchiveWriter_Zip(tempSaveFileRealPath, false);
	if (!archiveWriter)
		WARN_RETURN(ERR::FAIL);

	WARN_RETURN_STATUS_IF_ERR(archiveWriter->AddMemory((const u8*)metadataString.c_str(), metadataString.length(), now, "metadata.json"));
	WARN_RETURN_STATUS_IF_ERR(archiveWriter->AddMemory((const u8*)simStateStream.str().c_str(), simStateStream.str().length(), now, "simulation.dat"));
	archiveWriter.reset(); // close the file

	WriteBuffer buffer;
	FileInfo tempSaveFile;
	WARN_RETURN_STATUS_IF_ERR(GetFileInfo(tempSaveFileRealPath, &tempSaveFile));
	buffer.Reserve(tempSaveFile.Size());
	WARN_RETURN_STATUS_IF_ERR(io::Load(tempSaveFileRealPath, buffer.Data().get(), buffer.Size()));
	WARN_RETURN_STATUS_IF_ERR(g_VFS->CreateFile(filename, buffer.Data(), buffer.Size()));

	OsPath realPath;
	WARN_RETURN_STATUS_IF_ERR(g_VFS->GetRealPath(filename, realPath));
	LOGMESSAGERENDER(L"Saved game to %ls\n", realPath.string().c_str());

	return INFO::OK;
}
Пример #15
0
VfsPath CColladaManager::GetLoadablePath(const VfsPath& pathnameNoExtension, FileType type)
{
	std::wstring extn;
	switch (type)
	{
	case PMD: extn = L".pmd"; break;
	case PSA: extn = L".psa"; break;
		// no other alternatives
	}

	/*

	Algorithm:
	* Calculate hash of skeletons.xml and converter version.
	* Use CCacheLoader to check for archived or loose cached .pmd/psa.
	* If cached version exists:
		* Return pathname of cached .pmd/psa.
	* Else, if source .dae for this model exists:
		* Convert it to cached .pmd/psa.
		* If converter succeeded:
			* Return pathname of cached .pmd/psa.
		* Else, fail (return empty path).
	* Else, if uncached .pmd/psa exists:
		* Return pathname of uncached .pmd/psa.
	* Else, fail (return empty path).

	Since we use CCacheLoader which automatically hashes file size and mtime,
	and handles archived files and loose cache, when preparing the cache key
	we add converter version number (so updates of the converter cause
	regeneration of the .pmd/psa) and the global skeletons.xml file size and
	mtime, as modelers frequently change the contents of skeletons.xml and get
	perplexed if the in-game models haven't updated as expected (we don't know
	which models were affected by the skeletons.xml change, if any, so we just
	regenerate all of them)

	TODO (maybe): The .dae -> .pmd/psa conversion may fail (e.g. if the .dae is
	invalid or unsupported), but it may take a long time to start the conversion
	then realise it's not going to work. That will delay the loading of the game
	every time, which is annoying, so maybe it should cache the error message
	until the .dae is updated and fixed. (Alternatively, avoid having that many
	broken .daes in the game.)

	*/

	// Now we're looking for cached files
	CCacheLoader cacheLoader(m_VFS, extn);
	MD5 hash;
	u32 version;
	m->PrepareCacheKey(hash, version);

	VfsPath cachePath;
	VfsPath sourcePath = pathnameNoExtension.ChangeExtension(L".dae");
	Status ret = cacheLoader.TryLoadingCached(sourcePath, hash, version, cachePath);

	if (ret == INFO::OK)
		// Found a valid cached version
		return cachePath;

	// No valid cached version, check if we have a source .dae
	if (ret != INFO::SKIPPED)
	{
		// No valid cached version was found, and no source .dae exists
		ENSURE(ret < 0);

		// Check if source (uncached) .pmd/psa exists
		sourcePath = pathnameNoExtension.ChangeExtension(extn);
		if (m_VFS->GetFileInfo(sourcePath, NULL) != INFO::OK)
		{
			// Broken reference, the caller will need to handle this
			return L"";
		}
		else
		{
			return sourcePath;
		}
	}

	// No valid cached version was found - but source .dae exists
	// We'll try converting it

	// We have a source .dae and invalid cached version, so regenerate cached version
	if (! m->Convert(sourcePath, cachePath, type))
	{
		// The COLLADA converter failed for some reason, this will need to be handled
		//	by the caller
		return L"";
	}

	return cachePath;
}