private void WriteManifestEntry(Utf8JsonWriter writer, ResourceHeader header, string relativePath) { writer.WriteStartObject(); writer.WriteString("Name", header.resourceName); if (header.filename.StartsWith(relativePath)) { writer.WriteString("Filename", header.filename.Remove(0, relativePath.Length)); } else { writer.WriteString("Filename", header.filename); } writer.WriteNumber("FileStart", header.fileStart); writer.WriteNumber("FileLength", header.fileLength); writer.WriteBoolean("CanUnload", header.canUnload); writer.WriteString("LoaderExtension", header.loaderExt); writer.WriteStartArray("CustomAttribs"); foreach (KeyValuePair <string, string> custom in header.customAttribs) { writer.WriteStartObject(); writer.WriteString("Key", custom.Key); writer.WriteString("Value", custom.Value); writer.WriteEndObject(); } writer.WriteEndArray(); writer.WriteEndObject(); }
public List <ResourceHeader> GetHeaders() { //return a copy so the headers cant be meddled with ResourceHeader[] hArr = new ResourceHeader[headers.Count]; headers.Values.CopyTo(hArr, 0); return(new List <ResourceHeader>(hArr)); }
public ResourceBase CreateResource(Type t, string resourceName, byte[] initData = null, bool canStore = false) { if (!resCreatorMaps.ContainsKey(t)) { Log.WriteError("Cannot find creator resource of type " + t.Name + ", no creator delegate exists."); return(null); } if (headers.ContainsKey(resourceName)) { Log.WriteError("Cannot create resource, name is already in use: " + resourceName); return(null); } ResourceHeader createdHeader = new ResourceHeader(resourceName, string.Empty, 0, 0, false, canStore, t.Name, new Dictionary <string, string>()); headers.Add(createdHeader.resourceName, createdHeader); ResourceBase resource = resCreatorMaps[t].Invoke(this); if (initData == null) { initData = new byte[0]; } resource.Load(initData); resources.Add(resource.rid, resource); createdHeader.loaded = true; createdHeader.loadedId = resource.rid; return(resource); }
private void ProcessManifest(string data, string filename) { JsonDocument jdoc = JsonDocument.Parse(data); JsonElement jroot = jdoc.RootElement; string packageName = string.Empty; string packageDesc = string.Empty; string packageAuthor = string.Empty; Version packageVersion = new Version(0, 0, 0); if (jroot.TryGetProperty("Name", out JsonElement nameProp)) { packageName = nameProp.GetString(); } if (jroot.TryGetProperty("Description", out JsonElement descProp)) { packageDesc = descProp.GetString(); } if (jroot.TryGetProperty("Author", out JsonElement authorProp)) { packageAuthor = authorProp.GetString(); } if (jroot.TryGetProperty("Version", out JsonElement versionProp)) { packageVersion = new Version(versionProp.GetString()); } if (manifestHeaders.ContainsKey(packageName)) { Log.WriteError($"Manifest with name {packageName} is already loaded. Aborting read operation."); return; } Log.WriteLine($"Loading manifest: {packageName} by {packageAuthor} (v {packageVersion.ToString(3)}), {packageDesc}"); string relativePath = Path.GetRelativePath(Environment.CurrentDirectory, filename); relativePath = Path.GetDirectoryName(relativePath); JsonElement resElement = jroot.GetProperty("Resources"); List <string> validResHeaders = new List <string>(); foreach (JsonElement ele in resElement.EnumerateArray()) { ProcessManifestEntry(ele, relativePath, ref validResHeaders); ResourceHeader lastHeader = headers.LastOrDefault().Value; if (lastHeader != null && !lastHeader.manifests.Contains(packageName)) //check is necessary since an entry can be discarded if malformed. { lastHeader.manifests.Add(packageName); } } ManifestHeader manifestHeader = new ManifestHeader(packageName, packageAuthor, packageDesc, packageVersion, validResHeaders.ToArray(), filename); manifestHeaders.Add(manifestHeader.name, manifestHeader); Log.WriteLine("Manifest " + manifestHeader.name + " loaded. " + manifestHeader.headerNames.Length + " headers processed."); }
private void LoadResource(string resourceName) { if (!headers.ContainsKey(resourceName)) { Log.WriteError("Cannot get resource with name=" + resourceName + ", does not exist."); } ResourceHeader header = headers[resourceName]; if (!File.Exists(header.filename)) { Log.WriteError("Cannot get resource with name=" + resourceName + ", specified file does not exist."); } try { byte[] rawData; using (FileStream fstream = File.OpenRead(header.filename)) using (BinaryReader reader = new BinaryReader(fstream)) { if (header.fileStart > 0) { reader.BaseStream.Seek((long)header.fileStart, SeekOrigin.Begin); } int readLen = header.fileLength == 0 ? (int)reader.BaseStream.Length - (int)reader.BaseStream.Position : (int)header.fileLength; rawData = reader.ReadBytes(readLen); } if (!resLoaderMaps.ContainsKey(header.loaderExt)) { throw new Exception("Loader not found for extension: " + header.loaderExt + ". Unable to load resource: " + resourceName); } Type resourceType = resLoaderMaps[header.loaderExt]; ResourceBase resource = resCreatorMaps[resourceType].Invoke(this); resource.Load(rawData); resources.Add(resource.rid, resource); ridToHeaderMap.Add(resource.rid, header.resourceName); header.loaded = true; header.loadedId = resource.rid; Log.WriteLine("Loaded resource: " + resourceName); } catch (Exception e) { Log.WriteError("Error occured during resource load: " + e.ToString()); } }
public static void ShowHeader(string[] args) { if (args.Length != 1) { Log.WriteError("Expected resource name."); return; } ResourceHeader header = ResourceManager.Global.GetHeader(args[0]); if (header == null) { return; //since we got null, an error will already have been emitted (hopefully) } Log.WriteLine(header.ToString()); }
public void UnloadManifest(string name, bool forceUnload = false) { if (!manifestHeaders.ContainsKey(name)) { Log.WriteError($"Cannot unload manifest, none loaded with name {name}"); return; } ManifestHeader header = manifestHeaders[name]; manifestHeaders.Remove(name); for (int i = 0; i < header.headerNames.Length; i++) { ResourceHeader resHeader = headers[header.headerNames[i]]; if (!resHeader.canUnload && !forceUnload) { continue; //cant unload it anyway, so leave it as is. } if (resHeader.loaded) { UnloadResource(resHeader.resourceName); } if (resHeader.manifests.Count == 1 && resHeader.manifests[0] == header.name) { //resource is only part of this manifest, we can safely remove the header too headers.Remove(resHeader.resourceName); Log.WriteLine($"Unloaded manifest entry {header.name}->{resHeader.resourceName}"); } else { resHeader.manifests.Remove(header.name); Log.WriteLine($"Manifest entry {header.name}->{resHeader.resourceName} is loaded by {resHeader.manifests.Count} other manifests."); } } Log.WriteLine("Successfully unloaded manifest and any exclusive resources: " + header.name); header = null; }
public ResourceBase GetResource(string resourceName) { if (!headers.ContainsKey(resourceName)) { Log.WriteError("Cannot get resource with name=" + resourceName + ", does not exist."); return(null); } ResourceHeader header = headers[resourceName]; if (header.loaded) { return(GetResource(header.loadedId)); } LoadResource(resourceName); if (header.loaded) { return(GetResource(header.loadedId)); } return(null); //If we reach here, LoadResource() should have emitted an error anyway. }
} //TODO: have this run on a background thread, and return a task-like object that can querry the loading status public void UnloadResource(string resourceName, bool force = false) { if (!headers.ContainsKey(resourceName)) { Log.WriteError("Cannot unload resource, no resource exists with name=" + resourceName); return; } ResourceHeader header = headers[resourceName]; if (!header.loaded) { Log.WriteError("Cannot unload resource, it's currently not loaded. Name=" + resourceName); return; } if (!header.canUnload) { if (!force) { Log.WriteError("Resource marked as never-unload, cannot unload. Name=" + resourceName); return; } else { Log.WriteLine("Resource marked as never-unload, but force was specified. This may lead to cascading errors. Name=" + resourceName); } } ResourceBase res = GetResource(header.loadedId); resources.Remove(res.rid); ridGenerator.FreeId(res.rid); res.Unload(); res = null; header.loadedId = 0; header.loaded = false; Log.WriteLine("Unloaded resource: " + header.resourceName); }
private void ProcessManifestEntry(JsonElement entry, string relativePath, ref List <string> processedEntries) { string name = "<name missing>"; string filename; ulong fileStart; ulong fileLen; bool canUnload; string loaderExt; Dictionary <string, string> attribs = null; //Weirdly written, but essentially each if statement is getting the json property, then trying to read it (checking for non-empty strings). //If either of those operations fail, it'll jump to the detail missing block, otherwise process as normal. if (!(entry.TryGetProperty("Name", out JsonElement nameProp) && (name = nameProp.GetString()).Length > 0)) { goto CRITICAL_DETAIL_MISSING; } if (!(entry.TryGetProperty("Filename", out JsonElement filenameProp) && (filename = filenameProp.GetString()).Length > 0)) { goto CRITICAL_DETAIL_MISSING; } if (!(entry.TryGetProperty("FileStart", out JsonElement fileStartProp) && fileStartProp.TryGetUInt64(out fileStart))) { goto CRITICAL_DETAIL_MISSING; } if (!(entry.TryGetProperty("FileLength", out JsonElement fileLenProp) && fileLenProp.TryGetUInt64(out fileLen))) { goto CRITICAL_DETAIL_MISSING; } if (!(entry.TryGetProperty("LoaderExtension", out JsonElement loadExtProp) && (loaderExt = loadExtProp.GetString()).Length > 0)) { goto CRITICAL_DETAIL_MISSING; } if (!entry.TryGetProperty("CanUnload", out JsonElement canUnloadProp)) { goto CRITICAL_DETAIL_MISSING; } else { canUnload = canUnloadProp.GetBoolean(); } //custom attribs is completely optional if (entry.TryGetProperty("CustomAttribs", out JsonElement customAttribsProp)) { attribs = new Dictionary <string, string>(); foreach (JsonElement attrib in customAttribsProp.EnumerateArray()) { if (attrib.TryGetProperty("Key", out JsonElement keyProp) && attrib.TryGetProperty("Value", out JsonElement valueProp)) { attribs.Add(keyProp.GetString(), valueProp.GetString()); } } } //validate the required loader for this resource is known if (!resLoaderMaps.ContainsKey(loaderExt)) { goto CRITICAL_LOADER_MISSING; } if (headers.ContainsKey(name)) { Log.WriteError($"Cannot added manifest entry {name}, an entry already exists with this name."); return; } filename = Path.Combine(relativePath, filename); ResourceHeader header = new ResourceHeader(name, filename, fileStart, fileLen, canUnload, true, loaderExt, attribs); headers.Add(header.resourceName, header); processedEntries.Add(header.resourceName); return; CRITICAL_LOADER_MISSING: Log.WriteError($"Manifest entry {name} ({filename}) requires resource loader {loaderExt}, which is unknown to this resource manager."); return; CRITICAL_DETAIL_MISSING: Log.WriteError($"Missing critical element for manifest entry: " + name + ". Entry is being dropped (but will remain in file)."); return; }