--
-- Copyright (c) 2012 Kevin Wellwood
-- All rights reserved.
--
-- This source code is distributed under the Modified BSD License. For terms and
-- conditions, see license.txt.
--
with Allegro.File_IO; use Allegro.File_IO;
with Interfaces; use Interfaces;
with Streams.Buffers; use Streams.Buffers;
with System; use System;
private with Ada.Strings.Unbounded;
private with Ada.Real_Time;
private with Streams;
package Resources is
-- A Resource_File represents a resource file loaded in memory. The contents
-- of the file can be accessed via various methods. Once a Resource_File is
-- closed, its backing data should no longer be relied upon. If the file
-- data is required after the Resource_File must be closed, a copy of the
-- contents should be made.
--
-- Resource files are cached to avoid the cost of searching and reading from
-- disk each time file is needed but the details of the cache and how/when
-- it is purged are private.
type Resource_File is tagged limited private;
type A_Resource_File is access all Resource_File'Class;
-- Returns the address of the resource file's data. The length of the data
-- can be determined by calling Size.
function Get_Address( this : not null access Resource_File'Class ) return Address;
-- Returns the filename of the resource.
function Get_Filename( this : not null access Resource_File'Class ) return String;
-- Returns an identifying string comprised of the original filepath and
-- group name used to find the resource. Useful for debugging purposes but
-- not absolute identification.
function Get_Id( this : not null access Resource_File'Class ) return String;
-- Returns a new Allegro_File reference to the resource. Be sure to close
-- the returned handle before unloading the resource.
function Get_File( this : not null access Resource_File'Class ) return A_Allegro_File;
-- Returns a new read-only stream backed by the resource contents. Be sure
-- to close the stream before unloading the resource.
procedure Get_Stream( this : not null access Resource_File'Class;
strm : out A_Buffer_Stream );
-- Returns the size of the file in bytes;
function Size(this : not null access Resource_File'Class ) return Unsigned_32;
-- Releases the reference to the resource. Resource files are reference
-- counted so be sure to call this when finished with a Resource_File.
procedure Release( resource : in out A_Resource_File );
pragma Postcondition( resource = null );
----------------------------------------------------------------------------
-- Searches for and loads a resource file, returning a reference to it. The
-- search order for resource files is as follows:
--
-- 1. filepath (If filepath is an absolute file path)
-- 2. working_dir/filename
-- 3. working_dir/group/filename
-- 4. working_dir/group.zip/filename
-- 5. user_dir/filename (user_dir is defined by Application)
-- 6. media_dir/filename (media_dir is defined by "media" preference)
-- 7. media_dir/group/filename
-- 8. media_dir/group.zip/filename
--
-- (Where 'filename' is the filename taken from 'filepath'.)
--
-- NULL will be returned if the file is not found or otherwise can't be
-- accessed. Resource files are reference counted, so you must call
-- Unload_Resource when finished with the Resource_File. If 'keep' is True,
-- the resource will be kept in memory even after no references remain. This
-- is useful for keeping the most frequently used resources cached in memory
-- for the life of the application.
function Load_Resource( filepath : String;
group : String;
keep : Boolean := False ) return A_Resource_File;
pragma Precondition( filepath'Length > 0 );
-- Loads a resource into memory in the same way as Load_Resource except the
-- reference will not be returned and the internal reference count will begin
-- at zero. Use this to prefetch resources from disk. No load errors are
-- reported by this procedure.
procedure Preload_Resource( filepath : String; group : String );
-- Attempts to find resource 'filepath' and then returns an absolute path
-- that can be used to overwrite or override the resource. The same search
-- algorithm as Load_Resource will be used.
--
-- If the resource is found outside of an archive, its absolute path will be
-- returned.
--
-- If the resource is found inside an archive, then the returned path will
-- point to a file of the same name in the same directory as its archive,
-- effectively allowing the new file to override its archived version. If
-- this location is not writable (entirely possible for installed
-- applications) then the caller should fallback to the current working
-- directory. If that fails, fallback to the user directory returned by
-- Application.Get_User_Directory.
--
-- If the resource is not found, then the returned path will point to a file
-- of the same name in the current working directory. If this location is
-- not writable (entirely possible for installed applications) then the
-- caller should fallback to the user directory .
function Write_Path( filepath : String; group : String ) return String;
pragma Precondition( filepath'Length > 0 );
pragma Postcondition( Write_Path'Result'Length > 0 );
----------------------------------------------------------------------------
RESOURCE_ERROR,
RESOURCE_FORMAT_ERROR : exception;
private
use Ada.Real_Time;
use Ada.Strings.Unbounded;
use Streams;
-- File extension for resolving archive files when searching for a resource.
ARCHIVE_EXTENSION : constant String := "zip";
type Resource_File is tagged limited
record
refs : Natural := 1;
keep : Boolean := False; -- keep in cache
lastUse : Time := Clock;
filepath : Unbounded_String;
group : Unbounded_String;
data : A_SEA;
end record;
-- Creates a Resource_File object from a raw buffer. The resource's path and
-- group must be specified here manually. Set 'keep' to True to indicate the
-- resource should not be removed from the cache even when all references
-- have been unloaded. 'data' will be consumed.
procedure Create_Resource( this : out A_Resource_File;
filepath : String;
group : String;
keep : Boolean;
data : in out A_SEA );
pragma Precondition( filepath'Length > 0 );
pragma Precondition( data /= null );
pragma Postcondition( data = null );
pragma Postcondition( this /= null );
-- Creates a new Resource_File encapsulating a buffer and returns
-- reference. 'filepath' is the original path of the resource, accessible
-- via Get_Filename(). 'data' will be consumed.
procedure Create_Resource( this : out A_Resource_File;
filepath : String;
data : in out A_SEA );
pragma Precondition( filepath'Length > 0 );
pragma Precondition( data /= null );
pragma Postcondition( this /= null );
pragma Postcondition( data = null );
-- Increments the reference count for the resource file.
procedure Reference( this : not null access Resource_File'Class );
-- Decrements the referece count for the resource file.
procedure Unreference( this : not null access Resource_File'Class );
procedure Delete( this : in out A_Resource_File );
end Resources;