I started work on this, and here's the design so far:
Virtual Control File
The virtual control file is named "HrCtlAPI". The Hugo engine does not allow more than 8 characters in a file name, and enforces alphanumeric letters. That's unfortunate, but "HrCtlAPI", with *exactly* that capitalization (it's case-sensitive) is safe and very unlikely to be ever used in any other game.
Handshake
For detecting Hugor, the game must read exactly one value from the control file before writing anything in it. The magic value is 12121. So:
Code: Select all
readfile "HrCtlAPI"
{
hugor_test = readval
}
if hugor_test = 12121
{
print "We are running in Hugor."
}
The magic value is an additional safety measure. In the very unlikely event that a file named "HrCtlAPI" actually exists on disk, and the game is not running in Hugor, when you don't get 12121 as the only value from that file, then you know you're not running in Hugor. If you want to be completely paranoid, you might also test whether 12121 is actually the *only* value in the file. If you're able to read more values from the file, even if the first value was 12121 by some crazy chance, then you're not running in Hugor.
When trying to read more values, the read will fail (just like when trying to read past the end of a real file.)
This handshake is only possible when reading from the control file for the first time, and without having previously written to it. So the first access to the control file is special and should probably be done at game startup.
However, from Hugor's perspective, this is not mandatory. You can write opcodes to the control file before doing this handshake and just assume you're running in Hugor. But that's obviously not portable; other interpreters (but also older versions of Hugor) will actually create a file named "HrCtlAPI" on disk and your opcodes will be written there and obviously have no effect.
Long story short: do the handshake *first* if you care about other interpreters or older versions of Hugor.
Executing Opcodes
To execute a Hugor opcode, you write the value of the opcode, plus parameter (if any) to the control file, in *one* write operation. So each opcode should be in its own writefile { } block. The opcode is *only* executed when the block exits. You cannot write multiple opcodes in the same writefile block.
This works:
Code: Select all
writefile "HrCtlAPI"
{
writeval opcode, param1, param2, param3
! The opcode is NOT executed yet.
}!<-- The opcode is executed here.
This also works:
Code: Select all
writefile "HrCtlAPI"
{
writeval opcode, param1, param2
! Do something else
! ...
writeval param3
}
However, this does not work:
Code: Select all
writefile "HrCtlAPI"
{
writeval opcode, param1, param2
writeval some_other_opcode
}
Or at least, you should not expect it to work. It might, or it might not. Let's call it "undefined behavior."
So every writefile block should write exactly one opcode and the parameters (if any) for that one opcode.
The reason for this is two-fold:
First, Hugo does not allow you to read values inside a writefile block, so there's no way to get the return value of an opcode from inside the writefile block. To get the return value (if the opcode has a return value), you need to a readfile.
Second, if the opcode was executed immediately, you wouldn't get the chance to calculate parameters and write them later on inside the writefile block. This way, you can writeval the opcode, do something else, and then writeval parameters. It is guaranteed that the opcode will only be executed when the writefile block closes ("}").
Getting Opcode Return Values
Since you cannot read values inside a writefile block, to get the return values of an opcode, you need a readfile block and read the results:
Code: Select all
writefile "HrCtlAPI"
{
writeval opcode, param1, param2, param3
}
readfile "HrCtlAPI"
{
readval result
}
If an opcode has multiple return values, then you readval multiple times.