You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
9.4 KiB
C#

using SFML.Audio;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
namespace Otter {
/// <summary>
/// Manages files used for game assets. Can use a packed data file of paths and byte arrays.
/// The game will attempt to use local files before the packed data file.
/// Packed data is expected as:
/// bool: true to continue reading, false to stop
/// string: the path of the file that was packed
/// int32: the size of the file that was packed
/// bytes: the actual data from the file
/// </summary>
public class Files {
/// <summary>
/// The unpacked data from a packed data file. File paths mapped to byte arrays.
/// </summary>
public static Dictionary<string, byte[]> Data = new Dictionary<string, byte[]>();
/// <summary>
/// The root folder that assets can be found in when loading data.
/// </summary>
public static string AssetsFolderPrefix = "Assets/";
/// <summary>
/// Reads data from a uncompressed packed file
/// </summary>
/// <param name="path">The path to the packed data file.</param>
public static void LoadPackedData(string path) {
if (!File.Exists(path)) throw new FileNotFoundException("Cannot find packed data file " + path);
Data.Clear();
var bytes = new BinaryReader(File.Open(path, FileMode.Open));
int length = (int)bytes.BaseStream.Length;
var reading = bytes.ReadBoolean();
while (reading) {
var filepath = bytes.ReadString();
var fileSize = bytes.ReadInt32();
var data = bytes.ReadBytes(fileSize);
Data.Add(filepath, data);
//Console.WriteLine("Reading data {0}", filepath);
reading = bytes.ReadBoolean();
}
}
/// <summary>
/// Check if a file exists, or if it has been loaded from the packed data.
/// </summary>
/// <param name="path">The path to check.</param>
/// <returns>True if the file exists or if it has been loaded from the packed data.</returns>
public static bool FileExists(string path) {
if (File.Exists(path)) return true;
if (File.Exists(AssetsFolderPrefix + path)) return true;
if (Data.ContainsKey(path)) return true;
return false;
}
/// <summary>
/// Load a file as a memory stream from local files or packed data.
/// Probably don't use this a lot it probably is memory leak city.
/// </summary>
/// <param name="path">The path to load from.</param>
/// <returns>The stream.</returns>
public static Stream LoadFileStream(string path) {
if (FileExists(path)) {
return new MemoryStream(LoadFileBytes(path));
}
return null;
}
/// <summary>
/// Load a file as a byte array from local files or packed data.
/// </summary>
/// <param name="path">The path to load from.</param>
/// <returns>The byte array of the data from the file.</returns>
public static byte[] LoadFileBytes(string path) {
if (File.Exists(path)) {
return File.ReadAllBytes(path);
}
if (File.Exists(AssetsFolderPrefix + path)) {
return File.ReadAllBytes(AssetsFolderPrefix + path);
}
if (Data.ContainsKey(path)) {
return Data[path];
}
return null;
}
/// <summary>
/// Check if a file is being loaded from a local file or the packed data.
/// Note that the game will attempt to load from local files before packed data.
/// </summary>
/// <param name="path">The path to check.</param>
/// <returns>True if the data is coming from the packed file.</returns>
public static bool IsUsingDataPack(string path) {
if (File.Exists(path)) return false;
if (File.Exists(AssetsFolderPrefix + path)) return false;
return Data.ContainsKey(path);
}
}
#region Sounds
/// <summary>
/// Class that manages the cache of sounds.
/// </summary>
class Sounds {
static Dictionary<string, SoundBuffer> sounds = new Dictionary<string, SoundBuffer>();
public static SoundBuffer Load(string path) {
//if (!File.Exists(source)) throw new FileNotFoundException(source + " not found.");
if (!Files.FileExists(path)) throw new FileNotFoundException(path + " not found.");
if (sounds.ContainsKey(path)) {
return sounds[path];
}
sounds.Add(path, new SoundBuffer(Files.LoadFileBytes(path)));
return sounds[path];
}
}
#endregion
#region Fonts
/// <summary>
/// Class that manages the cache of fonts.
/// </summary>
class Fonts {
static Dictionary<string, SFML.Graphics.Font> fonts = new Dictionary<string, SFML.Graphics.Font>();
static Dictionary<Stream, SFML.Graphics.Font> fontsStreamed = new Dictionary<Stream, SFML.Graphics.Font>();
public static SFML.Graphics.Font DefaultFont {
get {
if (defaultFont == null) {
defaultFont = Load(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Otter.CONSOLA.TTF"));
}
return defaultFont;
}
}
static SFML.Graphics.Font defaultFont;
internal static SFML.Graphics.Font Load(string path) {
//if (!File.Exists(source)) throw new FileNotFoundException(source + " not found.");
if (!Files.FileExists(path)) throw new FileNotFoundException(path + " not found.");
if (fonts.ContainsKey(path)) {
//return new SFML.Graphics.Font(Files.LoadFileBytes(path));
return fonts[path];
}
if (Files.IsUsingDataPack(path)) {
var stream = new MemoryStream(Files.LoadFileBytes(path));
fonts.Add(path, new SFML.Graphics.Font(stream)); // SFML fix? Might be memory leaking when you have a lot of fonts.
//stream.Close();
//fonts.Add(path, new SFML.Graphics.Font(Files.LoadFileBytes(path))); // SFML fix?
}
else {
if (File.Exists(path)) {
fonts.Add(path, new SFML.Graphics.Font(path)); // Cant load font with bytes from path?
}
else { // This should work because we already checked FileExists above
fonts.Add(path, new SFML.Graphics.Font(Files.AssetsFolderPrefix + path)); // Cant load font with bytes from path?
}
}
return fonts[path];
}
internal static SFML.Graphics.Font Load(Stream stream) {
if (fontsStreamed.ContainsKey(stream)) {
return fontsStreamed[stream];
}
fontsStreamed.Add(stream, new SFML.Graphics.Font(stream));
return Load(stream);
}
}
#endregion
/// <summary>
/// Class that manages the cache of Textures.
/// </summary>
public class Textures {
#region Static Fields
static Dictionary<string, SFML.Graphics.Texture> textures = new Dictionary<string, SFML.Graphics.Texture>();
static Dictionary<Stream, SFML.Graphics.Texture> texturesStreamed = new Dictionary<Stream, SFML.Graphics.Texture>();
#endregion
#region Static Methods
/// <summary>
/// This doesn't really work right now. Textures in images wont update
/// if you do this.
/// </summary>
/// <param name="path"></param>
public static void Reload(string path) {
textures.Remove(path);
Load(path);
}
/// <summary>
/// This doesn't work right now. Textures in images wont update if you
/// do this.
/// </summary>
public static void ReloadAll() {
var keys = textures.Keys;
textures.Clear();
foreach (var k in keys) {
Load(k);
}
}
#endregion
#region Internal
internal static SFML.Graphics.Texture Load(string path) {
//if (!File.Exists(source)) throw new FileNotFoundException("Texture path " + source + " not found.");
if (!Files.FileExists(path)) throw new FileNotFoundException("Texture path " + path + " not found.");
if (textures.ContainsKey(path)) {
return textures[path];
}
textures.Add(path, new SFML.Graphics.Texture(Files.LoadFileBytes(path)));
return textures[path];
}
internal static SFML.Graphics.Texture Load(Stream stream) {
if (texturesStreamed.ContainsKey(stream)) {
return texturesStreamed[stream];
}
texturesStreamed.Add(stream, new SFML.Graphics.Texture(stream));
return texturesStreamed[stream];
}
#endregion
}
}