What's new in v1.4.7
Here is a summary of the main changes in v1.4.7. You can see the full changelog file here.
Input editor
Undo
Most input editor operations are now stored, and can be undone. This can be performed using contextual menu, shortcuts, or using the undo window which shows past actions and those which can be undone. Indeed, undo operations cannot modify inputs before the current frame.
The only operations that are not registered are when loading a savestate with its inputs. In that case, the undo history is reset.
Factor analog inputs
This feature has been asked a lot. It is now possible to multiply the values of a single analog input (like mouse coordinates) by a factor. This will help porting inputs when the game window resolution has changed.
Add input columns
There is now a unified way to add input columns, especially analog inputs, using the “inputs” menu. You don’t to set a new value to an analog input to add it to the editor. As a consequence, using the mouse wheel to change its value has been removed.
Ram watch
It has a few new features:
- ram watches can be frozen
- new types: string and byte array
- ram watches can be reordered
Hex viewer
A minimal hex viewer has been added, so that you can visualize the game memory. You can seek an address from the ram watch or ram search tools. Selecting bytes in the hex viewer will display the converted integer or float values. Also, modified bytes are highlighted.
Savestates
As a critical part of the tool, savestating feature has received some improvements:
Savefiles
Files that are created or modified by the game (aka savefiles) were incorrectly left out of savestates. This is now fixed, but will cause an increase in savestate size. To mitigate this, a few tricks have been developed:
We map the savefile in memory. If the original file is present, we map it also. We compare the both memory regions, and we only store the memory pages that are different. When loading the savestate, we also map both files to memory, and we copy either from the original file or from the savestate into the savefile.
If the original file does not exist, we save the whole file into the savestate.
Mapped files can have holes in them. We can detect them using lseek()
with
SEEK_DATA
and SEEK_HOLE
arguments, and skip pages which are inside a hole.
Calling lseek()
requires that we have a file descriptor associated with the
mapped file. In the special case of shared files that were mapped and then
closed, we can still have access to a file descriptor inside /proc/self/map_files/
.
Getting access to this directory requires CAP_CHECKPOINT_RESTORE
since
Linux 5.9. Before that, it requires CAP_SYS_ADMIN
. libTAS program now asks at
startup to add CAP_CHECKPOINT_RESTORE
to the executable.
In the case that the game manipulates a file using only the file stream given
by fopen()
, without ever needing the underlying file descriptor, we have a
strong optimization! Instead of opening the file, we map the file in memory
with private mapping, and we create a stream that gives access to the memory
mapping directly (using fopencookie()
). The advantages are:
- Because the mapping is private, the original file is never modified
- The file content is automatically included in savestates
- We benefit from the copy-on-write mechanism: no memory is allocated until the
file mapping is modified. We can detect if a memory page from the mapping has
been modified using the
file-page
flag from/proc/pid/pagemap
file. This way, we are sure to only save memory pages that have been modified from the original file.
This feature is crucial for programs like pcem
, which modifies the image disk
of the virtual machine, possibly taking several gigabytes of memory.
PID manipulation
When a savestate needs to be recreated, we are attempting to give it the same pid it has before. This is possible using two recent features:
- the “/proc/sys/kernel/ns_last_pid” file which can be modified to manipulate the next pid value
- the more recent
clone3
syscall which has aset_tid
option
Having the same pid everytime can mitigate some game crashes.