/// <summary>
        /// Update the level's metadata on disk.
        /// Does not change the level's timestamp.
        /// </summary>
        /// <param name="level"></param>
        public static void UpdateWorldMetadata(LevelMetadata level)
        {
            try
            {
                string bucket = Utils.FolderNameFromFlags(level.Genres);

                string fullPath = BokuGame.Settings.MediaPath + bucket + level.WorldId.ToString() + @".Xml";

                Xml.XmlWorldData xml = XmlWorldData.Load(fullPath, XnaStorageHelper.Instance);
                if (xml != null)
                {
                    level.ToXml(xml);

                    bool isDownload = (level.Genres & Genres.Downloads) != 0;

                    // Manage the stream ourselves so avoid level timestamp being changed.
                    Stream stream = Storage4.OpenWrite(fullPath);
                    xml.Save(stream, isDownload);
                    Storage4.Close(stream);
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }
        private bool IsAlreadyDownloaded(LevelMetadata level)
        {
            string filename = BokuGame.Settings.MediaPath + BokuGame.DownloadsPath + level.WorldId.ToString() + @".Xml";

            if (Storage4.FileExists(filename, StorageSource.UserSpace))
            {
                XmlWorldData xml = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                if (xml != null)
                {
                    LevelMetadata local = LevelMetadata.CreateFromXml(xml);

                    return(
                        local.WorldId == level.WorldId &&
                        local.Creator == level.Creator &&
                        local.LastWriteTime >= level.LastWriteTime);
                }
            }

            return(false);
        }
        public static LevelMetadata LoadMetadataByGenre(Guid worldId, Genres genres)
        {
            string bucket = BokuGame.MyWorldsPath;

            if (genres != 0)
            {
                bucket = Utils.FolderNameFromFlags(genres);
            }

            string fullPath = BokuGame.Settings.MediaPath + bucket + worldId.ToString() + @".Xml";

            Xml.XmlWorldData xml = XmlWorldData.Load(fullPath, XnaStorageHelper.Instance);
            if (xml != null)
            {
                LevelMetadata data = new LevelMetadata();
                data.FromXml(xml);

                //minor hackery - seems previous versions of kodu will sometimes leave the genres set to 0
                //even though they should be updated for the bucket the level is in.  Then at load time, a run-time
                //genre is set.  This code will maintain that behavior for levels loaded through this helper
                if (bucket == BokuGame.DownloadsPath)
                {
                    //ensure downloads always have the downloads flag
                    data.Genres |= Genres.Downloads;
                }
                else if (bucket == BokuGame.BuiltInWorldsPath)
                {
                    //ensure built in worlds always have the built in flag
                    data.Genres |= Genres.BuiltInWorlds;
                }
                else if (bucket == BokuGame.MyWorldsPath)
                {
                    data.Genres |= Genres.MyWorlds;
                }

                return(data);
            }

            return(null);
        }
        public static BokuShared.Wire.WorldPacket ReadWorldPacketFromDisk(string worldFullPathAndName, string bucket)
        {
            BokuShared.Wire.WorldPacket packet = null;
            Stream file = null;

            try
            {
                string localLevelPath = BokuGame.Settings.MediaPath + bucket;
                string worldFilename  = Path.GetFileName(worldFullPathAndName);

                // Read contents of world xml to retrieve the names of the dependent
                // files we need to upload
                Xml.XmlWorldData xmlWorldData = XmlWorldData.Load(localLevelPath + worldFilename, XnaStorageHelper.Instance);
                if (xmlWorldData == null)
                {
                    return(null);
                }

                // Clear virtual genre bits in case they got saved (server clears them too).
                xmlWorldData.genres &= ~(int)Genres.Virtual;

                packet = new BokuShared.Wire.WorldPacket();
                packet.Info.WorldId     = packet.Data.WorldId = xmlWorldData.id;
                packet.Info.Name        = xmlWorldData.name;
                packet.Info.Description = xmlWorldData.description;
                packet.Info.Creator     = xmlWorldData.creator;
                packet.Info.IdHash      = "";
                packet.Info.Genres      = xmlWorldData.genres;

                string imageFileName = xmlWorldData.GetImageFilenameWithoutExtension();

                // VirtualMap
                file = Storage4.OpenRead(BokuGame.Settings.MediaPath + xmlWorldData.xmlTerrainData2.virtualMapFile, StorageSource.All);
                packet.Data.VirtualMapBytes = new byte[file.Length];
                file.Read(packet.Data.VirtualMapBytes, 0, (int)file.Length);
                Storage4.Close(file);

                // Stuff xml
                file = Storage4.OpenRead(BokuGame.Settings.MediaPath + xmlWorldData.stuffFilename, StorageSource.All);
                packet.Data.StuffXmlBytes = new byte[file.Length];
                file.Read(packet.Data.StuffXmlBytes, 0, (int)file.Length);
                Storage4.Close(file);

                // Optional: don't worry if we don't have a thumbnail image.
                try
                {
                    file = null;
                    file = Storage4.TextureFileOpenRead(localLevelPath + imageFileName);

                    if (file != null)
                    {
                        packet.Info.ThumbnailBytes = new byte[file.Length];
                        file.Read(packet.Info.ThumbnailBytes, 0, (int)file.Length);
                        Storage4.Close(file);
                    }
                }
                catch { }


                // Try To load Snapshot image.
                try
                {
                    file = null;
                    file = Storage4.TextureFileOpenRead(localLevelPath + imageFileName, Storage4.TextureFileType.jpg);

                    if (file != null)
                    {
                        packet.Info.ScreenshotBytes = new byte[file.Length];
                        file.Read(packet.Info.ScreenshotBytes, 0, (int)file.Length);
                        Storage4.Close(file);
                    }
                }
                catch { }


                // We've successfully read all required files. We may now upload them to the server.
                file = Storage4.OpenRead(localLevelPath + worldFilename, StorageSource.All);
                packet.Data.WorldXmlBytes = new byte[file.Length];
                file.Read(packet.Data.WorldXmlBytes, 0, (int)file.Length);
                Storage4.Close(file);

                Instrumentation.RecordEvent(Instrumentation.EventId.LevelUploaded, xmlWorldData.name);
            }
            catch
            {
                if (file != null)
                {
                    Storage4.Close(file);
                }
                packet = null;
            }

            return(packet);
        }
        /// <summary>
        /// Delete a level from the local system.  Returns false if not yet initialized.
        /// </summary>
        /// <param name="worldId"></param>
        /// <param name="callback"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public bool StartDeletingLevel(
            Guid worldId,
            Genres bucket,
            BokuAsyncCallback callback,
            object param)
        {
            bool deleted = false;

            bucket &= Genres.SharableBins;

            // Verify exactly one bucket is specified
            Debug.Assert(bucket != 0);
            Debug.Assert((int)bucket == int.MinValue || MyMath.IsPowerOfTwo((int)bucket));

            string worldFilename = null;
            string stuffFilename = null;
            string thumbFilename = null;

            LevelMetadata record = null;

            string stuffPath = String.Empty;
            string worldPath = String.Empty;

            if (0 != (bucket & Genres.MyWorlds))
            {
                stuffPath = BokuGame.MyWorldsStuffPath;
                worldPath = BokuGame.MyWorldsPath;
            }
            else if (0 != (bucket & Genres.Downloads))
            {
                stuffPath = BokuGame.DownloadsStuffPath;
                worldPath = BokuGame.DownloadsPath;
            }


            lock (Synch)
            {
                for (int i = 0; i < allLevels.Count; ++i)
                {
                    record = allLevels[i];

                    if (record.WorldId == worldId && (record.Genres & bucket) == bucket)
                    {
                        worldFilename = Path.Combine(BokuGame.Settings.MediaPath, worldPath + worldId.ToString() + @".Xml");
                        stuffFilename = Path.Combine(BokuGame.Settings.MediaPath, stuffPath + worldId.ToString() + @".Xml");
                        thumbFilename = Path.Combine(BokuGame.Settings.MediaPath, worldPath + worldId.ToString());

                        // Need to get the terrain file before we delete the main file.  BUT the terrain should be
                        // deleted after, otherwise the usage test will find the main file and always thing that
                        // the terrain file is in use.
                        string terrainFilename = null;
                        try
                        {
                            // Only delete terrain file if no longer referenced.
                            XmlWorldData xmlWorldData = XmlWorldData.Load(worldFilename, XnaStorageHelper.Instance);
                            terrainFilename = xmlWorldData.xmlTerrainData2.virtualMapFile;
                        }
                        catch { }

                        // Note : Delete() handles non-existent files just fine.
                        Storage4.Delete(worldFilename);
                        Storage4.Delete(stuffFilename);
                        Storage4.Delete(thumbFilename + @".dds");
                        Storage4.Delete(thumbFilename + @".jpg");
                        Storage4.Delete(thumbFilename + @".png");

                        // Only deletes terrain file if no other world is using it.  (including autosaves)
                        DeleteTerrainFile(terrainFilename);

                        LevelMetadata level = allLevels[i];
                        allLevels.RemoveAt(i);

                        LevelRemoved_Synched(level);

                        deleted = true;

                        break;
                    }
                }
            }

            AsyncResult result = new AsyncResult();

            result.Success = deleted;
            result.Param   = param;
            result.Seconds = 0;

            if (callback != null)
            {
                callback(result);
            }

            return(deleted);
        }
示例#6
0
        /// <summary>
        /// Reads a directory, building the list of level metadata available for browsing.
        /// </summary>
        /// <param name="dataSource"></param>
        /// <returns></returns>
        private void ReadDataSource(string folder, Genres tag, StorageSource sources)
        {
            string path = Path.Combine(BokuGame.Settings.MediaPath, BokuGame.LevelsPath, folder);

            string[] files = null;

            try
            {
#if NETFX_CORE
                files = Storage4.GetFiles(path, @"*.Xml", sources);
#else
                files = Storage4.GetFiles(path, @"*.Xml", sources, SearchOption.TopDirectoryOnly);
#endif
            }
            catch { }

            if (files != null)
            {
                // Filter out AutoSave.Xml
                List <string> filteredFiles = new List <string>();
                for (int i = 0; i < files.Length; ++i)
                {
                    if (files[i].ToUpper().Contains("AUTOSAVE"))
                    {
                        continue;
                    }
                    filteredFiles.Add(files[i]);
                }
                files = filteredFiles.ToArray();

                // Load level metadata records
                for (int i = 0; running && i < files.Length; i++)
                {
                    try
                    {
                        string       filename = Path.GetFileName(files[i]);
                        string       fullPath = Path.Combine(path, filename);
                        XmlWorldData xml      = XmlWorldData.Load(fullPath, XnaStorageHelper.Instance, (int)sources);
                        if (xml == null)
                        {
                            continue;
                        }

                        LevelMetadata level = new LevelMetadata();
                        level.FromXml(xml);

                        level.Genres |= tag;

                        LevelBrowserState state = new LevelBrowserState();
                        state.level        = level;
                        level.BrowserState = state;

                        // Force the creator name of built-ins to "Microsoft"
                        if ((level.Genres & Genres.BuiltInWorlds) != 0)
                        {
                            level.Creator = "Kodu Team";
                        }

                        AddLevel(level);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e.Message);
                    }
                }
            }
        }
示例#7
0
            }   // end of Shared c'tor

            /// <summary>
            /// Creates and populates a grid with file elements based on the input path and filter.
            /// </summary>
            /// <param name="path">Path where files reside.</param>
            /// <param name="filter">Filter for valid file names.</param>
            /// <returns>The created grid.  Grids are created in the InActive state.  Will return null if no files found.</returns>
            public UIGrid CreateGrid(String path, String filter)
            {
                // Create a list of files in the path.
                String[] files = null;

                try
                {
#if !XBOX360
                    files = Storage.GetFiles(path, filter, SearchOption.TopDirectoryOnly);
#else
                    files = Storage.GetFiles(path, filter);
#endif
                }
                catch (DirectoryNotFoundException)
                {
                    // The directory will be empty, so no need to get files again
                    // we do this as a convenience and its really not needed.
                }

                UIGrid grid = null;

                if (files != null && files.Length > 0)
                {
                    List <string> filteredFiles = new List <string>();

                    // Filter AutoSave.Xml
                    for (int i = 0; i < files.Length; ++i)
                    {
                        if (files[i].ToUpper().Contains("AUTOSAVE"))
                        {
                            continue;
                        }
                        filteredFiles.Add(files[i]);
                    }

                    files = filteredFiles.ToArray();
                }

                if (files != null && files.Length > 0)
                {
                    // Create and populate grid.
                    grid = new UIGrid(parent.OnSelect, parent.OnCancel, new Point(1, files.Length), "App.LoadLevelMenu");

                    // Set up the blob for info common to all tiles.
                    UIGridWorldTile.ParamBlob blob = new UIGridWorldTile.ParamBlob();
                    blob.width            = 8.5f;
                    blob.height           = 1.25f;
                    blob.edgeSize         = 0.125f;
                    blob.selectedColor    = selectedColor;
                    blob.unselectedColor  = unselectedColor;
                    blob.textColor        = Color.White;
                    blob.dropShadowColor  = Color.Black;
                    blob.useDropShadow    = true;
                    blob.invertDropShadow = false;
                    blob.normalMapName    = @"QuarterRoundNormalMap";

                    int index = 0;
                    for (int i = 0; i < files.Length; i++)
                    {
                        DateTime dateTime = Storage.GetLastWriteTime(files[i]);

                        String filename = files[i].Substring(path.Length);
                        String fullPath = path + filename;

                        XmlWorldData xmlWorldData = XmlWorldData.Load(fullPath);

                        Texture thumb = null;
                        try
                        {
                            string thumbFilename = xmlWorldData.GetThumbFilenameWithoutExtension();
                            thumb = Storage.TextureLoad(path + thumbFilename);
                        }
                        catch { }

                        UIGridWorldTile tile = new UIGridWorldTile(
                            blob,
                            xmlWorldData.id,
                            fullPath,
                            xmlWorldData.name,
                            xmlWorldData.description,
                            xmlWorldData.creator,
                            dateTime,
                            xmlWorldData.rating,
                            thumb
                            );
                        grid.Add(tile, 0, index++);
                    }

                    grid.Spacing    = new Vector2(0.0f, 0.05f); // The first number doesn't really matter since we're doing a 1d column.
                    grid.Scrolling  = true;
                    grid.SlopOffset = true;
                }

                return(grid);
            }   // end of CreateGrid()
        private void ScrubTerrainFiles()
        {
            //  Build a list of all terrain files in user storage.
            //  For each world in local storage (all three bins)
            //      get the XmlWorldData file
            //      from the XmlWorldData file, get the terrain filename
            //      remove that filename from the list
            //
            //  Any terrain filenames still on the list should be deleted
            //  since they're no longer referenced by any files.

            string[] terrainFiles = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.TerrainPath, @"*.raw", StorageSource.UserSpace);

            // If nothing to scrub, just return.
            if (terrainFiles == null)
            {
                return;
            }

            string[] undoFiles          = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.UnDoPath, @"*.Xml", StorageSource.UserSpace);
            string[] myWorldsFiles      = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.MyWorldsPath, @"*.Xml", StorageSource.UserSpace);
            string[] starterWorldsFiles = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.BuiltInWorldsPath, @"*.Xml", StorageSource.TitleSpace);
            string[] downloadsFiles     = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.DownloadsPath, @"*.Xml", StorageSource.UserSpace);

            /// Undo/Resume files. We might have to fall back on these if the user deletes the
            /// world they are editing and then back back to it.
            if (undoFiles != null)
            {
                for (int i = 0; i < undoFiles.Length; ++i)
                {
                    string       filename     = undoFiles[i];
                    XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                    if (xmlWorldData == null)
                    {
                        continue;
                    }

                    if (xmlWorldData.xmlTerrainData2 != null)
                    {
                        string terrainName = Path.Combine(Storage4.UserLocation, BokuGame.Settings.MediaPath, xmlWorldData.xmlTerrainData2.virtualMapFile);
                        for (int j = 0; j < terrainFiles.Length; ++j)
                        {
                            if (terrainName == terrainFiles[j])
                            {
                                // Remove this file.
                                terrainFiles[j] = null;
                                break;
                            }
                        }
                    }
                }
            }

            // MyWorlds
            if (myWorldsFiles != null)
            {
                for (int i = 0; i < myWorldsFiles.Length; ++i)
                {
                    string       filename     = myWorldsFiles[i];
                    XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                    if (xmlWorldData == null)
                    {
                        continue;
                    }

                    if (xmlWorldData.xmlTerrainData2 != null)
                    {
                        string terrainName = Path.Combine(Storage4.UserLocation, BokuGame.Settings.MediaPath, xmlWorldData.xmlTerrainData2.virtualMapFile);
                        for (int j = 0; j < terrainFiles.Length; ++j)
                        {
                            if (terrainName == terrainFiles[j])
                            {
                                // Remove this file.
                                terrainFiles[j] = null;
                                break;
                            }
                        }
                    }
                }
            }

            // BuiltInWorlds
            if (starterWorldsFiles != null)
            {
                for (int i = 0; i < starterWorldsFiles.Length; ++i)
                {
                    try
                    {
                        string       filename     = starterWorldsFiles[i];
                        XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                        if (xmlWorldData == null)
                        {
                            continue;
                        }

                        if (xmlWorldData.xmlTerrainData2 != null)
                        {
                            string terrainName = Path.Combine(Storage4.UserLocation, BokuGame.Settings.MediaPath, xmlWorldData.xmlTerrainData2.virtualMapFile);
                            for (int j = 0; j < terrainFiles.Length; ++j)
                            {
                                if (terrainName == terrainFiles[j])
                                {
                                    // Remove this file.
                                    terrainFiles[j] = null;
                                    break;
                                }
                            }
                        }
                    }
                    catch
                    {
                    }
                }
            }

            // Downloads
            if (downloadsFiles != null)
            {
                for (int i = 0; i < downloadsFiles.Length; ++i)
                {
                    string       filename     = downloadsFiles[i];
                    XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                    if (xmlWorldData == null)
                    {
                        continue;
                    }

                    if (xmlWorldData.xmlTerrainData2 != null)
                    {
                        string terrainName = Path.Combine(Storage4.UserLocation, BokuGame.Settings.MediaPath, xmlWorldData.xmlTerrainData2.virtualMapFile);
                        for (int j = 0; j < terrainFiles.Length; ++j)
                        {
                            if (terrainName == terrainFiles[j])
                            {
                                // Remove this file.
                                terrainFiles[j] = null;
                                break;
                            }
                        }
                    }
                }
            }

            int deleteCount = 0;

            // Now, anything that's left in the list should be fair game for deletion.
            for (int i = 0; i < terrainFiles.Length; i++)
            {
                if (terrainFiles[i] != null)
                {
                    if (Storage4.FileExists(terrainFiles[i], StorageSource.UserSpace))
                    {
                        if (Storage4.Delete(terrainFiles[i]))
                        {
                            deleteCount += 1;
                        }
                    }
                }
            }

            //System.Diagnostics.Debug.WriteLine(String.Format("Scrubbed {0} terrain files", deleteCount));
        }   // end of ScrubTerrainFiles()
        }   // end of ScrubTerrainFiles()

        /// <summary>
        /// Deletes the given terrain file but only after verifying that no world is using it.
        /// It any world is found that is using the file then it is not deleted.
        ///
        /// Note this still isn't perfect since when a level is deleted, the terrain file may still be left
        /// in the undo stack and hence not deleted.  No biggy.  It's better than deleting too much and it's
        /// much quicker than the above ScrubTerrainFiles().
        /// </summary>
        /// <param name="terrainFile"></param>
        void DeleteTerrainFile(string terrainFile)
        {
            string[] undoFiles          = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.UnDoPath, @"*.Xml", StorageSource.UserSpace);
            string[] myWorldsFiles      = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.MyWorldsPath, @"*.Xml", StorageSource.UserSpace);
            string[] starterWorldsFiles = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.BuiltInWorldsPath, @"*.Xml", StorageSource.TitleSpace);
            string[] downloadsFiles     = Storage4.GetFiles(BokuGame.Settings.MediaPath + BokuGame.DownloadsPath, @"*.Xml", StorageSource.UserSpace);

            /// Undo/Resume files. We might have to fall back on these if the user deletes the
            /// world they are editing and then back back to it.
            if (undoFiles != null)
            {
                for (int i = 0; i < undoFiles.Length; ++i)
                {
                    string       filename     = undoFiles[i];
                    XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                    if (xmlWorldData == null || xmlWorldData.xmlTerrainData2 == null)
                    {
                        continue;
                    }

                    if (xmlWorldData.xmlTerrainData2.virtualMapFile == terrainFile)
                    {
                        // Found it, don't delete.
                        return;
                    }
                }
            }

            // MyWorlds
            if (myWorldsFiles != null)
            {
                for (int i = 0; i < myWorldsFiles.Length; ++i)
                {
                    string       filename     = myWorldsFiles[i];
                    XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                    if (xmlWorldData == null || xmlWorldData.xmlTerrainData2 == null)
                    {
                        continue;
                    }

                    if (xmlWorldData.xmlTerrainData2.virtualMapFile == terrainFile)
                    {
                        // Found it, don't delete.
                        return;
                    }
                }
            }

            // BuiltInWorlds
            if (starterWorldsFiles != null)
            {
                for (int i = 0; i < starterWorldsFiles.Length; ++i)
                {
                    try
                    {
                        string       filename     = starterWorldsFiles[i];
                        XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);

                        if (xmlWorldData == null || xmlWorldData.xmlTerrainData2 == null)
                        {
                            continue;
                        }

                        if (xmlWorldData.xmlTerrainData2.virtualMapFile == terrainFile)
                        {
                            // Found it, don't delete.
                            return;
                        }
                    }
                    catch
                    {
                    }
                }
            }

            // Downloads
            if (downloadsFiles != null)
            {
                for (int i = 0; i < downloadsFiles.Length; ++i)
                {
                    string       filename     = downloadsFiles[i];
                    XmlWorldData xmlWorldData = XmlWorldData.Load(filename, XnaStorageHelper.Instance);
                    if (xmlWorldData == null || xmlWorldData.xmlTerrainData2 == null)
                    {
                        continue;
                    }

                    if (xmlWorldData.xmlTerrainData2.virtualMapFile == terrainFile)
                    {
                        // Found it, don't delete.
                        return;
                    }
                }
            }

            // Nothing was found using this terrain file so we can delete it.
            try
            {
                terrainFile = Path.Combine(BokuGame.Settings.MediaPath, terrainFile);
                Storage4.Delete(terrainFile);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                }
            }
        } // end of DeleteTerrainFile()