A CLI tool to help you port SOC (Sonic Object Configuration) files.
Given valid SOC, emits valid SOC (or Lua!).
- Port mode. Automatically migrate old SOC files that use hard-coded slot numbers to freeslots, making them portable.
- Generate Lua. Migrate SOC scripts to Lua.
- Dependency info. Generates a listing of all external references made by the SOC, as well as required asset files.
- Selection and extraction. Recursively extract only specific entities (by ID), as well as their local dependencies.
- Updates property names. Any old-style SOC property names are updated (e.g.
SpriteSubNumber
=>SpriteFrame
).
All SOC input first must pass the selectors to be further processed.
Selectors (--<entity>--ids
) filter the input SOC to only the desired entities (and by default, their local dependencies and freeslot declarations). soctool
discards entities that don't match one of the selectors (they will not be processed, and will not appear in the output).
Selectors are useful for extracting specific entities from an otherwise unwieldy SOC.
They are additive filters: the more IDs you specify, the more you will select. You can combine them to select groups of unrelated entities, or omit them entirely to read and process the entire SOC.
To achieve subtractive filtering, chain multiple invocations of soctool
together (pipe I/O).
In port mode (--make-portable
), any selected entity definitions that install themselves to hard-coded slots (e.g. Object 230
, State 1041
, Sound 150
) will be migrated to generated freeslots. Further, any hard-coded sprite IDs referenced by a selected state will also be allocated freeslots, as long as at least one referencing state will migrate to a freeslot as well.
Freeslot names (constants) are generated by combining the appropriate prefix for the resource type with as much of the user-provided freeslot prefix (--freeslot-prefix
) as possible, followed by the original hard-coded slot number. The original slot number is included to guarantee uniqueness within the selection, and to help porters understand what's been migrated from where.
For example, if --freeslot-prefix
is GHOUL
:
type | slot | freeslot |
---|---|---|
Object | 123 | MT_GHOUL123 |
State | 2443 | S_GHOUL2443 |
Sound | 433 | sfx_gho433 |
Sprite | 231 | SPR_GH6F |
Note that for spites, the slot number is base-36 encoded, due its 4 character limit.
After porting SOC entities, be sure to examine the dependency listing to determine which asset files are expected by the ported code.
By default, the generated output (SOC or Lua code) includes a listing of all external references** and required assets. If your output is missing dependencies from the original input SOC file, they will be listed as external references here, along with any other external references.
** There are some limitations to soctool
's dependency analysis that prevent certain dependencies from being recognized. See the Limitations section for details.
Any references to entities not declared in the output. This includes hard-coded slot IDs (unless they have been overridden within the selection).
Mathematical expressions used in an entity reference context will appear here as well, even if the expression would resolve to a locally declared entity. This is because soctool
does not evaluate expressions. You can use this information to manually augment your selectors to include these references.
Any sound lump names expected by locally* declared sounds.
* Currently, this does not include hard-coded slot numbers, but it should.
Any sprite lump names expected by locally declared sprites, excluding the rotation*.
A sprite is considered local if the selection contains a matching freeslot declaration. For example, if a State references a sprite by a hard-coded slot number and port mode is off, this slot number is considered to be external. If the same state is processed with port mode on, the sprite would be migrated to a freeslot**, making it local.
* Rotation is omitted because not all sprites need files for all rotations. For more information on this, see Sprites.
** Sprites are only migrated to freeslots if at least one corresponding state in the same selection is also migrated.
The IDs of any Linedef executors called.
This section describes the available command-line arguments. They're separated into two categories below only to help you find what you need.
These arguments filter the input SOC to only the listed entities (and by default, their local dependencies).
They are additive filters: the more IDs you specify, the more you will select.
arg | short | type | description |
---|---|---|---|
--no-recurse |
-R | flag | Select only the entities identified, without recursively selecting their dependencies. Only applicable if at least one entity selector argument is specified. |
--object-ids <id>[,<id>]... |
-o | csv | Select these objects. |
--state-ids <id>[,<id>]... |
-s | csv | Select these states. |
--sound-ids <id>[,<id>]... |
-d | csv | Select these sounds. |
--level-ids <id>[,<id>]... |
-l | csv | Select these levels. |
These arguments specify the processing behavior to apply to the selection.
arg | short | type | description |
---|---|---|---|
--input-soc |
-i | path | When provided, the input SOC's contents will be read from this file instead of STDIN . |
--make-portable |
-p | flag | When provided, all selected SOC entity definitions using hard-coded slots will be upgraded to freeslots. Any local references (within the selection) will also be upgraded to point to the new freeslot locations. |
--freeslot-prefix |
-f | string | When in port mode (--make-portable ), specifies a friendly name prefix to use for generated freeslot names. As much of this prefix will be used as possible, though due to length restrictions for certain slot types, it will likely be truncated. For best results, provide at most 4 characters that will help you to identify related entities. |
--to-lua |
-S | flag | Emit results as Lua code. Keep in mind that this tool cannot read Lua, so if you plan to do multiple passes by chaining invocations, save this parameter for your final invocation. |
--from-old-srb2 |
-u | flag | [Not very useful at the moment] Attempt to upgrade old versions of actions (e.g. if they've been renamed or had parameter changes). Currently, this just changes A_RingExplode calls to A_OldRingExplode if the state's var1 contains an object ID. |
--no-describe |
-D | flag | Omit the default block comment with dependency information from the output. |
--no-inline-comments |
-I | flag | Suppress the generation of warnings as inline comments. Inline comments are generated by default with information specific to particular entities that soctool thinks may be helpful to porters. Once you're done porting, this flag might be a good idea for a final pass to clean things up. |
--no-attribution |
-A | flag | Suppress the generation of the attribution message. |
--help |
-? | flag | Print the help text. |
Slot number references cannot be ported unless they are numeric constants. This means that soctool
won't port slot numbers that are involved in mathematical expressions.
This is because soctool
does not evaluate expressions. To do this, it would need to know all constants defined in SRB2 (some of which may only be known at runtime). This just isn't worth doing, since old SOC code that hard-codes slot numbers is unlikely to be using mathematical expressions anyway.
Because soctool
does not evaluate expressions, it cannot follow references calculated by them. This means that selectors cannot recursively include entities that are referenced within a mathematical expression.
However, these expressions will be included in the dependency listing, even if the referenced entities are declared locally. You can use this information to manually augment your selectors to include these references.
As of SRB2 2.2, soctool
is aware of all documented built-in Actions
which accept an entity reference through either Var1
or Var2
. Almost all of these references are recognized, and will be recursively expanded by selectors (if possible) and included in the dependency listing.
However, some Actions
are not supported due to non-standard implementations.
Partially supported:
A_CheckThingCount
andA_MultiShot
use the upper 16 bits ofVar1
to store object references. Recursive selections will never follow these references, and they will never be ported. TheirVar1
expression will however always be included in the dependency reference listing.
No support:
A_DropMine
andA_ShootBullet
use theRaiseState
field on their actor's object to store an object reference. This is not supported bysoctool
's current design. In fact, if an object'sRaiseState
contains an object reference, there's a possibility the object will be interpreted as a state reference (but only if there exists a locally declared State with the same name).A_RandomStateRange
is not supported, since it would require runtime evaluation ofVar1
andVar2
.A_CusValAction
,A_RelayCustomValue
,A_SetCustomValue
,A_UseCusValMemo
would require runtime evaluation to properly support.
If soctool
encounters one of these completely unsupported actions, it will generate an appropriate warning.
soctool
is written in Scala (and thus can run on any system with a JVM), but is also cross-compiled to native 64-bit executables for macOS, Windows, and Linux (thanks, GraalVM!).
Get the latest version for your system in the Releases tab.