1 /*
2  *  BSD 3-Clause License
3  *  
4  *  Copyright (c) 2016, Mango-Engine Team
5  *  All rights reserved.
6  *  
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are met:
9  *  
10  *  * Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *  
13  *  * Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *  
17  *  * Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from
19  *    this software without specific prior written permission.
20  *  
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 module mango_engine.resource;
33 
34 import mango_engine.game;
35 import mango_engine.world;
36 import mango_engine.util;
37 import mango_engine.graphics.texture;
38 import mango_engine.graphics.shader;
39 
40 import std.conv;
41 import std.zip;
42 import std.file;
43 import std.json;
44 import std.array;
45 import std.algorithm;
46 import std.exception;
47 
48 private void checkTextureJSON(ZipArchive archive, JSONValue root) @trusted {
49     enforce(root["file"].str in archive.directory, new MArchiveParseException("Invalid Archive! Texture file could not be found in archive!"));
50     enforce(root["useAlpha"].type == JSON_TYPE.TRUE || root["useAlpha"].type == JSON_TYPE.FALSE, new MArchiveParseException("Invalid Archive! useAlpha must be true or false!"));
51 }
52 
53 class ResourceManager {
54     private shared GameManager _game;
55 
56     private shared Texture[string] loadedTextures;
57 
58     @property GameManager game() @trusted nothrow { return cast(GameManager) _game; }
59 
60     this(GameManager game) @trusted nothrow {
61         this._game = cast(shared) game;
62     }
63 
64     Texture loadTexture(in string location) @trusted {
65         enforce(exists(location), "File " ~ location ~ " does not exist!");
66         enforce(location.endsWith(".marchive"), "File " ~ location ~ " is not a valid .marchive file!");
67 
68         auto zip = new ZipArchive(read(location));
69         foreach(name, am; zip.directory) {
70             if(name == "texture.json") {
71                 zip.expand(am); // Uncompress the JSON information
72 
73                 string data = cast(string) am.expandedData; // the contents of the JSON file
74                 
75                 JSONValue root = parseJSON(data);
76                 checkTextureJSON(zip, root); // Check the texture JSON for correct elements
77 
78                 string textureName = root["name"].str;
79                 string textureFile = root["file"].str;
80                 bool useAlpha;
81                 switch(root["useAlpha"].type) {
82                     case JSON_TYPE.TRUE:
83                         useAlpha = true;
84                         break;
85                     case JSON_TYPE.FALSE:
86                         useAlpha = false;
87                         break;
88                     default:
89                         break;
90                 }
91 
92                 enforce(!(textureName in loadedTextures), new Exception("Texture is already loaded!"));
93 
94                 zip.expand(zip.directory[textureFile]); // Uncompress the texture file
95 
96                 // The path in mango-engine's temp directory which will contain the uncompressed texture
97                 auto uncompressedPath = getTempDirectoryPath() ~ PATH_SEPERATOR ~ "mango-engine" ~ PATH_SEPERATOR ~ "texture-" ~ split(location, ".")[0];
98 
99                 write(uncompressedPath, zip.directory[textureFile].expandedData); // Write the uncompressed texture to disk
100                 
101                 Texture texture = Texture.build(game, textureName, uncompressedPath, useAlpha);
102 
103                 loadedTextures[textureName] = cast(shared) texture;
104 
105                 return texture;
106             }
107         }
108 
109         throw new MArchiveParseException("Invalid Archive! Failed to find texture.json!");
110     }
111 
112     Texture getLoadedTexture(in string name) @trusted {
113         enforce(name in loadedTextures, new Exception("The texture is not loaded!"));
114 
115         return cast(Texture) loadedTextures[name];
116     }
117 
118     void unloadTexture(in string name) @trusted {
119         enforce(name in loadedTextures, new Exception("The texture is not loaded!"));
120 
121         Texture t = cast(Texture) loadedTextures[name];
122         t.cleanup();
123 
124         loadedTextures.remove(name);
125     }
126 
127     bool isTextureLoaded(in string name) @safe {
128         return name in loadedTextures ? true : false;
129     }
130 }
131 
132 class MArchiveParseException : Exception {
133 
134     this(in string message) @safe nothrow {
135         super(message);
136     }
137 }