Lua VFS: Difference between revisions

From Spring
Jump to navigationJump to search
Abma (talk | contribs)
 
(26 intermediate revisions by 6 users not shown)
Line 1: Line 1:
[[Engine_Development|Development]] < [[Lua_Scripting|Lua Scripting]] < {{FULLPAGENAME}}
[[Engine_Development|Development]] < [[Lua_Scripting|Lua Scripting]] < {{FULLPAGENAME}}
= Overview =
Although Spring can access the filesystem directly (via os module) it is more common that you
would want to access files included with your game or Spring. Trouble is, most of these files are
compressed into archives (.sdz/.sd7) so random access would generally be a difficult procedure.
Fortunately, the Spring Lua system automatically provides access to mod and base files via the VFS module.
The VFS module doesn't simply open archives though. What it does is map your game files, game
dependencies and Spring content onto a virtual file tree. All archives start from the 'roots' of the tree
and share the same virtual space, meaning that if two or more archives contain the same resource
file name the resources overlap and only one of the files will be retrieved. Overlapping directories
on the other hand are merged so the resulting virtual directory contains the contents of both. Here is
an example of how this works:
'''Archive 1 (games/mygame.sd7)'''
textures<br>
|__ texture1.png<br>
models<br>
|__ model1.mdl<br>
'''Archive 2 (base/springcontent.sdz'''
textures<br>
|__ texture1.png<br>
|__ texture2.png<br>
|__ texture3.png<br>
'''VFS'''
textures<br>
|__ texture1.png<br>
|__ texture2.png<br>
|__ texture3.png<br>
models<br>
|__ model1.mdl<br>
This raises the question: If both archives have a texture1.png then which texture1.png is retreived
via the VFS? The answer depends on the order the archives are loaded and the VFS mode (more on
modes below). Generally however, each archive loaded overrides any archives loaded
before it. The standard order of loading (from first to last) is:
#  The main Spring/ game directory.
#  The automatic dependencies springcontent.sdz and maphelper.sdz.
#  Dependencies listed in your modinfo.lua (or modinfo.tdf), in the order listed.
#  Your mod archive.


= Modes =
= Modes =
'''VFS.RAW'''<br>
 
'''VFS.MOD''' <font color=red>(unimplemented, acts like VFS.ZIP)</font><br>
These modes are constants passed to VFS.Include() and friends to control the order that archives are
'''VFS.MAP''' <font color=red>(unimplemented, acts like VFS.ZIP)</font><br>
searched for a given file. They are case-sensitive.
'''VFS.BASE''' <font color=red>(unimplemented, acts like VFS.ZIP)</font><br>
 
'''VFS.ZIP'''<br>
'''VFS.RAW''' Only select uncompressed files.
'''VFS.RAW_FIRST'''<br>
 
'''VFS.ZIP_FIRST'''<br>
'''VFS.MOD''' {{new|104.0}}
'''VFS.RAW_ONLY''' <font color=#55ee99>(same as VFS.RAW)</font><br>
 
'''VFS.ZIP_ONLY''' <font color=#55ee99>(same as VFS.ZIP)</font><br>
'''VFS.MAP''' {{new|104.0}}
 
'''VFS.BASE''' {{new|104.0}}
 
'''VFS.MENU'''  {{new|104.0}}
 
'''VFS.ZIP'''   Only select compressed files (.sdz,.sd7).
 
'''VFS.RAW_FIRST''' Try uncompressed files first, then compressed.
 
'''VFS.ZIP_FIRST''' Try compressed files first, then uncompressed.
 
'''VFS.RAW_ONLY''' <font color=#55ee99>(Deprecated. Same as VFS.RAW)</font>
 
'''VFS.ZIP_ONLY''' <font color=#55ee99>(Deprecated. Same as VFS.ZIP.)</font>
 
See also: https://github.com/spring/spring/blob/develop/rts/System/FileSystem/VFSModes.h


= Files =
= Files =
'''VFS.Include''' <span class=note>(if enviroment=nil then use current one)</span>
'''VFS.Include''' <span class=note>(if enviroment=nil then use current one)</span>
   ( string "filename" [, table enviroment = nil [, number mode ] ] ) ->
   ( string "filename" [, table enviroment = nil [, number mode ] ] ) ->
<span class=note>The environment arg sets the global environment (see generic lua refs). In almost all cases, this should be left nil to preserve Springs default. 
If nil then the env will be _G (global environment) If the optional env argument is provided any non-local variables and functions defined in
filename.lua are then accessable via env or _G. Vise-versa, any variables defined in env prior to
passing to VFS.Include are available to code in the included file. Code running in filename.lua will
see the contents of env in place of the normal _G environment. </span>
<span class=note>This loads and compiles the lua code from a file in the VFS. The path is relative to the main Spring directory, e.g. VFS.Include('LuaUI/includes/filename.lua', nil, [vfsmode]) </span>
<span class=note>VFS modes are single char strings and can be concatenated; doing specifies an order of preference for the mode (=location) from which to include files.</span>
'''VFS.LoadFile'''
'''VFS.LoadFile'''
   ( string "filename" [, number mode ] ) -> nil | string data
   ( string "filename" [, number mode ] ) -> nil | string data
This is used for loading raw text data, as it will not execute the file, like VFS.Include does, just returns a string of the file contents.
'''VFS.FileExists'''
'''VFS.FileExists'''
   ( string "filename" [, number mode ] ) -> boolean
   ( string "filename" [, number mode ] ) -> boolean
 
<span class=note>example usage: if VFS.FileExists("maps/Castles.sdz") then ... end </span>
example usage: mapexits = VFS.FileExists("maps\\Castles.sdz")
 
(Also seems to work with maps downloaded by pool downloaders)


'''VFS.DirList'''
'''VFS.DirList'''
Line 30: Line 104:
   ( string "directory", [, string "pattern" = "*"  [, number mode ] ] )
   ( string "directory", [, string "pattern" = "*"  [, number mode ] ] )
     -> { [1] = string subdir1, ... }
     -> { [1] = string subdir1, ... }
'''VFS.GetAvailableAIs'''
  ( [ string gameName] [, string mapName ] )
    -> { ai1, ai2, ... }
<span class="note">Gets a list of all Spring AIs. The optional gameName and mapName parameters can be used to include game/map specific LuaAIs in the list.</span>


'''VFS.UseArchive''' <span class=warning>unsynced only!</span>
'''VFS.UseArchive''' <span class=warning>unsynced only!</span>
   ( string "filename" [, number mode ], lua_function [, arg1 [,arg2 ]] )
   ( string "filename" [, number mode ], lua_function [, arg1 [,arg2 ]] )
     -> result1,result2,... of the given lua_function
     -> result1,result2,... of the given lua_function
  <span class=note>Loads an archive '''temporarely''' in the VFS and then runs the given lua_function,<br>which can make usage of the files in the archive.</span>
<span class=note>Loads an archive '''temporarely''' in the VFS and then runs the given lua_function,<br>which can make usage of the files in the archive.</span>


'''VFS.MapArchive''' <span class=warning>unsynced only!</span>
'''VFS.MapArchive''' <span class=warning>unsynced only!</span>
   ( string "filename/modname of archive", [string checksum of archive] )
   ( string "filename/modname of archive", [string checksum of archive] )
     -> bool
     -> bool
  <span class=note>Permanently loads an archive into the VFS (to load zipped music collections etc.).<br>If checksum is given it checks if the to be loaded file is correct,<br>if not then it won't load it and return false.</span>
<span class=note>Permanently loads an archive into the VFS (to load zipped music collections etc.). <br> Does nothing if the archive is already loaded in the VFS (won't reload even if there are <br> changes made to the archive). If checksum is given it checks if the to be loaded file is correct,<br>if not then it won't load it and return false.</span>
 
'''VFS.UnmapArchive''' <span class=warning>unsynced only! available in 98.0</span>
  ( string "filename/modname of archive")
    -> bool
<span class=note>Removes an already loaded archive (see VFS.MapArchive)</span>
 
'''VFS.CompressFolder''' <span class=warning>unsynced only!</span>
  ( string "folderPath" [, string "archiveType" ] [, string "compressedFilePath" ] <br> [, bool includeFolder ] [, number mode ] ) -> nil
 
<span class=note>Compresses the specified folder. <br>archiveType defines the compression type which can currently be only "zip" <br>includeFolder specifies whether the archive should have the specified folder as root (defaults to false) </span>
 
'''VFS.GetFileAbsolutePath''' <span class=warning>unsynced only! available in 105.0</span>
  ( string "filename" [, number mode ] ) -> absPath | nil
 
'''VFS.GetArchiveContainingFile''' <span class=warning>unsynced only! available in 105.0</span>
  ( string "filename" [, number mode ] ) -> archiveNameWithVersion | nil
 
= Generic hash =
'''VFS.CalculateHash''' {{new|101.0}}
  ( string input, number hashType)
    -> string md5hash
 
Calculates hash (in base64 form) of a given string (with md5 support initially). Note supplying 0 (MD5) as hashType is mandatory
 
= Archives =
 
'''VFS.GetMaps''' {{new|98.0}}
  ( ) -> {
  [1] = string mapName, ...
}
 
'''VFS.GetGames''' {{new|98.0}}
  ( ) -> {
  [1] = string gameName, ...
}
 
'''VFS.GetAllArchives''' {{new|98.0}}
  ( ) -> {
  [1] = string archiveName, ...
}
 
'''VFS.GetLoadedArchives''' {{new|105.0}}
  ( ) -> {
  [1] = string archiveName, ...
}
 
'''VFS.GetArchivePath''' {{new|105.0}}
  ( string archiveName ) -> string archivePathOnDisk
 
'''VFS.GetNameFromRapidTag''' {{new|105.0}}
  ( string rapidTag ) -> string archiveName
 
'''VFS.HasArchive''' {{new|98.0}}
  ( string archiveName ) -> boolean
 
'''VFS.GetArchiveInfo''' {{new|98.0}}
  ( string archiveName ) - >
    {       
          name = string,
          shortname = string,
          version = string,
          mutator = string,
          game = string,
          shortgame = string,
          description = string,
          mapfile = string,
          modtype = number,   
      }
<span class="note">modtype values:1=primary, 0=hidden, 3=map</span>
 
'''VFS.GetArchiveChecksum''' {{new|98.0}}
  ( string archiveName ) -> string singleArchiveChecksum, string completeArchiveChecksum
 
'''VFS.GetArchiveDependencies''' {{new|98.0}}
  ( string archiveName ) -> {
    [1] = string archiveName, ...
  }
 
'''VFS.GetArchiveReplaces''' {{new|98.0}}
  ( string archiveName ) -> {
    [1] = string archiveName, ...
  }
 
'''VFS.DownloadArchive''' {{new|101.0}}
  ( name, category ) -> nil
 
Category must be one of: map, game, engine.
 
'''VFS.AbortDownload''' {{new|104.0}}
  ( id ) -> boolean foundAndRemoved
 
Additional functions are available as part of the [[Lua_UnsyncedCtrl#.28Virtual-.29FileSystem|unsynced ctrl]].


= Packing =
= Packing =

Latest revision as of 10:53, 8 August 2022

Development < Lua Scripting < Lua VFS

Overview

Although Spring can access the filesystem directly (via os module) it is more common that you would want to access files included with your game or Spring. Trouble is, most of these files are compressed into archives (.sdz/.sd7) so random access would generally be a difficult procedure. Fortunately, the Spring Lua system automatically provides access to mod and base files via the VFS module.

The VFS module doesn't simply open archives though. What it does is map your game files, game dependencies and Spring content onto a virtual file tree. All archives start from the 'roots' of the tree and share the same virtual space, meaning that if two or more archives contain the same resource file name the resources overlap and only one of the files will be retrieved. Overlapping directories on the other hand are merged so the resulting virtual directory contains the contents of both. Here is an example of how this works:

Archive 1 (games/mygame.sd7)

textures
|__ texture1.png
models
|__ model1.mdl

Archive 2 (base/springcontent.sdz

textures
|__ texture1.png
|__ texture2.png
|__ texture3.png

VFS

textures
|__ texture1.png
|__ texture2.png
|__ texture3.png
models
|__ model1.mdl

This raises the question: If both archives have a texture1.png then which texture1.png is retreived via the VFS? The answer depends on the order the archives are loaded and the VFS mode (more on modes below). Generally however, each archive loaded overrides any archives loaded before it. The standard order of loading (from first to last) is:

  1. The main Spring/ game directory.
  2. The automatic dependencies springcontent.sdz and maphelper.sdz.
  3. Dependencies listed in your modinfo.lua (or modinfo.tdf), in the order listed.
  4. Your mod archive.

Modes

These modes are constants passed to VFS.Include() and friends to control the order that archives are searched for a given file. They are case-sensitive.

VFS.RAW Only select uncompressed files.

VFS.MOD New in version 104.0

VFS.MAP New in version 104.0

VFS.BASE New in version 104.0

VFS.MENU New in version 104.0

VFS.ZIP Only select compressed files (.sdz,.sd7).

VFS.RAW_FIRST Try uncompressed files first, then compressed.

VFS.ZIP_FIRST Try compressed files first, then uncompressed.

VFS.RAW_ONLY (Deprecated. Same as VFS.RAW)

VFS.ZIP_ONLY (Deprecated. Same as VFS.ZIP.)

See also: https://github.com/spring/spring/blob/develop/rts/System/FileSystem/VFSModes.h

Files

VFS.Include (if enviroment=nil then use current one)

 ( string "filename" [, table enviroment = nil [, number mode ] ] ) ->

The environment arg sets the global environment (see generic lua refs). In almost all cases, this should be left nil to preserve Springs default.

If nil then the env will be _G (global environment) If the optional env argument is provided any non-local variables and functions defined in filename.lua are then accessable via env or _G. Vise-versa, any variables defined in env prior to passing to VFS.Include are available to code in the included file. Code running in filename.lua will see the contents of env in place of the normal _G environment.

This loads and compiles the lua code from a file in the VFS. The path is relative to the main Spring directory, e.g. VFS.Include('LuaUI/includes/filename.lua', nil, [vfsmode])

VFS modes are single char strings and can be concatenated; doing specifies an order of preference for the mode (=location) from which to include files.

VFS.LoadFile

 ( string "filename" [, number mode ] ) -> nil | string data

This is used for loading raw text data, as it will not execute the file, like VFS.Include does, just returns a string of the file contents.

VFS.FileExists

 ( string "filename" [, number mode ] ) -> boolean

example usage: if VFS.FileExists("maps/Castles.sdz") then ... end

VFS.DirList

 ( string "directory" [, string "pattern" = "*"  [, number mode ] ] )
   -> { [1] = string filename, ... }

VFS.SubDirs

 ( string "directory", [, string "pattern" = "*"  [, number mode ] ] )
   -> { [1] = string subdir1, ... }

VFS.GetAvailableAIs

 ( [ string gameName] [, string mapName ] )
   -> { ai1, ai2, ... }

Gets a list of all Spring AIs. The optional gameName and mapName parameters can be used to include game/map specific LuaAIs in the list.

VFS.UseArchive unsynced only!

 ( string "filename" [, number mode ], lua_function [, arg1 [,arg2 ]] )
   -> result1,result2,... of the given lua_function

Loads an archive temporarely in the VFS and then runs the given lua_function,
which can make usage of the files in the archive.

VFS.MapArchive unsynced only!

 ( string "filename/modname of archive", [string checksum of archive] )
   -> bool

Permanently loads an archive into the VFS (to load zipped music collections etc.).
Does nothing if the archive is already loaded in the VFS (won't reload even if there are
changes made to the archive). If checksum is given it checks if the to be loaded file is correct,
if not then it won't load it and return false.

VFS.UnmapArchive unsynced only! available in 98.0

 ( string "filename/modname of archive")
   -> bool

Removes an already loaded archive (see VFS.MapArchive)

VFS.CompressFolder unsynced only!

 ( string "folderPath" [, string "archiveType" ] [, string "compressedFilePath" ] 
[, bool includeFolder ] [, number mode ] ) -> nil

Compresses the specified folder.
archiveType defines the compression type which can currently be only "zip"
includeFolder specifies whether the archive should have the specified folder as root (defaults to false)

VFS.GetFileAbsolutePath unsynced only! available in 105.0

 ( string "filename" [, number mode ] ) -> absPath | nil

VFS.GetArchiveContainingFile unsynced only! available in 105.0

 ( string "filename" [, number mode ] ) -> archiveNameWithVersion | nil

Generic hash

VFS.CalculateHash New in version 101.0

 ( string input, number hashType)
   -> string md5hash

Calculates hash (in base64 form) of a given string (with md5 support initially). Note supplying 0 (MD5) as hashType is mandatory

Archives

VFS.GetMaps New in version 98.0

 ( ) -> {
  [1] = string mapName, ...
}

VFS.GetGames New in version 98.0

 ( ) -> {
  [1] = string gameName, ...
}

VFS.GetAllArchives New in version 98.0

 ( ) -> {
  [1] = string archiveName, ...
}

VFS.GetLoadedArchives New in version 105.0

 ( ) -> {
  [1] = string archiveName, ...
}

VFS.GetArchivePath New in version 105.0

 ( string archiveName ) -> string archivePathOnDisk

VFS.GetNameFromRapidTag New in version 105.0

 ( string rapidTag ) -> string archiveName

VFS.HasArchive New in version 98.0

 ( string archiveName ) -> boolean

VFS.GetArchiveInfo New in version 98.0

 ( string archiveName ) - >
    {        
         name = string,
         shortname = string,
         version = string,
         mutator = string,
         game = string,
         shortgame = string,
         description = string,
         mapfile = string,
         modtype = number,     
     }

modtype values:1=primary, 0=hidden, 3=map

VFS.GetArchiveChecksum New in version 98.0

 ( string archiveName ) -> string singleArchiveChecksum, string completeArchiveChecksum

VFS.GetArchiveDependencies New in version 98.0

 ( string archiveName ) -> { 
   [1] = string archiveName, ... 
 }

VFS.GetArchiveReplaces New in version 98.0

 ( string archiveName ) -> { 
   [1] = string archiveName, ... 
 }

VFS.DownloadArchive New in version 101.0

 ( name, category ) -> nil

Category must be one of: map, game, engine.

VFS.AbortDownload New in version 104.0

 ( id ) -> boolean foundAndRemoved

Additional functions are available as part of the unsynced ctrl.

Packing

The Pack- and Unpack-functions are used to convert numbers->strings and strings->numbers. So you can read a binary file and then convert the received strings back to numbers and the other way around. Also you can use it in combination with the SendLuaXYZMsg-functions.


VFS.PackU8

VFS.PackU16

VFS.PackU32

VFS.PackS8

VFS.PackS16

VFS.PackS32

VFS.PackF32

arguments are:

VFS.PackX( number arg1, number arg2, number arg3, ... ) -> string

VFS.PackX( {[1]=number,[2]=number,..} ) -> string


VFS.UnpackU8

VFS.UnpackU16

VFS.UnpackU32

VFS.UnpackS8

VFS.UnpackS16

VFS.UnpackS32

VFS.UnpackF32

arguments are:

VFS.UnpackX( string "binary" [, number position = 1] [, number count] )

 -> number | table { [1]=number,[2]=number,.. }

Zlib

VFS.ZlibCompress unsynced only!

 ( string "uncompressedStr" )
   -> nil | string "compressedStr"

VFS.ZlibDecompress available in synced, too!

 ( string "compressedStr" [, number uncompressed size = 65500] )
   -> nil | string "uncompressedStr"