Skip to content

Creating a cook mod

takattowo edited this page Jun 3, 2026 · 6 revisions

PATCH_COOK modding guide

PATCH_COOK mods apply runtime tweaks while the game is open. You write a small text file of instructions, and the loader applies them during play. Use them for small offline adjustments, string swaps, value tests, or controlled practice-mode experiments.

Runtime mods are less stable than normal file mods. They can cause crashes, failed loads, or unexpected behavior. Use them only in offline or practice modes. Do not use them in online matches, ranked play, or public sessions.


1. Folder layout

Each mod is a folder under the game's Data\Addons\PATCH_COOK\ directory:

Data\Addons\PATCH_COOK\
  my_mod\
    info.json          (required, marks a valid mod)
    my_mod.cook        (your instructions, any *.cook or *.cooked name works)

Rules:

  • The folder must contain info.json, or it is ignored.
  • It must contain at least one .cook or .cooked file. You can have several, and each is parsed and applied.
  • Add an empty file named .disabled in the folder to skip the whole mod.

info.json

{
  "visible": true,
  "name": "my_mod",
  "type": "cook",
  "version": "1.0",
  "description": "What this mod does.",
  "author": "you"
}

type must be cook.

Also accepted:

patch_cook
bm

.cook vs .cooked

  • .cook: plain text. Easy to author and edit.
  • .cooked: protected format used for distribution when you do not want the instructions to be directly readable.

Both can live in the same folder.


2. When mods run

The game reloads its scripts at the start of every match. PATCH_COOK currently supports two stages:

  • Stage 1 runs when the game first loads.
  • Stage 2 runs after you have entered a game once.

Maximum stage is currently 2.

Put this line at the top of your file:

cook_stage 1

If omitted, the stage defaults to 1.

Why stages matter

Some targets are not available immediately when the game opens. If something only appears after entering a match, use:

cook_stage 2

When a rule reports not found, the target may not be loaded yet. Try a later stage or check your pattern.

Before any mod runs, the loader performs a one-time access self-test. If that fails, it logs:

access test failed

When this happens, no cook mod runs for that session.


2b. Code validation

This section is not updated yet.


3. The instruction language

Use one directive per line.

Blank lines are ignored.

Lines starting with either of these are comments:

#
--

PATCH_COOK supports these directive groups:

find
write
freeze

Use these only for offline testing and practice-mode experiments.


4. Values and patterns

Wherever a pattern or value is expected, use one of these forms:

Form Example Meaning
String "TEST_A" ASCII text
Hex bytes 90 90 90 or 0xDEADBEEF Raw bytes
Integer i8 5, i16 100, i32 1000, i64 5 Little-endian signed integer
Float f32 1.5, f64 2.0 Little-endian float

Numbers accept decimal or 0x hex.


5. find

find searches for a pattern and applies a replacement when a matching target is found.

find <pattern> [near <pattern> [within N]] [offset X] [all] [zend] -> <value>

Modifiers

All modifiers are optional.

near <pattern> [within N]

Only accepts matches that have the anchor pattern nearby.

N defaults to:

256

Use this to avoid changing the wrong copy of a common string.

offset X

Applies the replacement at match + X instead of directly at the match.

Use this when you find a stable anchor and need to adjust a nearby value.

all

Applies the rule to every qualifying match.

Without all, only the first qualifying match is changed.

zend

Requires a null byte after the match.

Use this when one name is a prefix of another.

Example:

NAME
NAME_X

Using zend helps match only the complete string.

Replacement behavior

For string replacements, the available slot is based on the length of the matched pattern.

A shorter replacement is padded cleanly.

A longer replacement can overflow past the original slot and may break nearby data. The parser logs a warning when this happens.

Prefer replacements that are equal length or shorter.

Examples

# simple swap
find "TEST_A" -> "TEST_B"

# only the TEST_A that sits near TEST_B, within 128 bytes
find "TEST_A" near "TEST_B" within 128 -> "TEST_B"

# whole-string swap, every occurrence, confirmed by a nearby anchor
find "STRINGTEST" near "OTHERSTRING" zend all -> "STRINGWOW"

# find an anchor, then adjust a nearby float
find "SPEED" offset 0x8 all -> f32 2.5

6. write

write applies a value at a specific target address.

write <addr> = <value>

Examples:

write 0x00ABCDEF = "HELLO"
write 0x00ABCDEF = 90 90 90
write 0x00ABCDEF = i32 100

Fixed addresses can change between runs. Prefer pattern-based rules for anything that is not stable.


7. freeze

freeze keeps a value steady through a resolved pointer chain.

freeze ["module"] [+] <baseOffset> chain <off> <off> ... as <type> (hold | = <value>)

This is intended for offline testing where a value changes during play and you want to keep it stable for controlled experiments.

Parts

"module"

Optional module name.

Example:

"GameApp.dll"

If omitted, the main executable is used.

<baseOffset>

Offset added to the module base.

chain <off> <off> ...

Pointer offsets applied in order.

as <type>

Value type.

Supported types:

i8
i16
i32
i64
f32
f64

hold

Captures the first value read and keeps restoring that value.

= <value>

Forces a specific value instead of capturing the first one.

Example

cook_stage 2

freeze "GameApp.dll" + 0x000000 chain 0000 0000 0000 as f32 hold

Freeze safety

Before every write, the loop checks that the resolved target is valid and writable. If a pointer resolves incorrectly during a scene change, it is skipped instead of being written.

Freezes stop automatically when the game closes.

Use this feature sparingly. Repeated runtime writes can cause instability or detection.


8. Reading the log

The loader streams the game's loader log silently.

Common messages:

  • CookLoader: access test passed: access OK, mods proceed.
  • CookLoader: access test failed (...): access blocked, so nothing runs.
  • CookLoader: my_mod applied 3/4: 3 of 4 directives applied successfully.
  • 1 not found: a directive matched nothing.
  • CookLoader: my_mod L8 error: ...: a rule failed, and line 8 is the problem.
  • CookLoader: my_mod N freeze active: N freeze rules are running.
  • CookScript[my_mod] L5: ...: a parse problem happened on line 5, and that line is skipped.
  • CookScript[my_mod] L8: replacement longer than match, will overflow ...: the replacement is longer than the matched slot.

9. Troubleshooting

Symptom Likely cause or fix
not found Target is not available at this stage. Try cook_stage 2.
not found on a near rule Anchor is too far away. Raise within, or choose a closer anchor.
Changes a string it should not Add zend, or use a tighter near rule.
Overflow warning Replacement is too long. Use an equal-length or shorter value.
access test failed Access was blocked. Mods cannot run this session.
Game crashes after a freeze The target changed, the chain is wrong, or repeated writes caused instability. Remove the freeze to confirm.
Wrong value frozen The chain captured at the wrong moment. Check the offsets and stage.

10. Best practices

Use PATCH_COOK only for offline or practice-mode testing.

Keep edits small and easy to reverse.

Prefer equal-length or shorter string replacements.

Use near and zend when matching common strings.

Avoid broad all rules unless you are sure every match should change.

Avoid fixed addresses when possible because they can change between runs.

Test one directive at a time when debugging.

Keep a backup of every mod folder.

Use .disabled to quickly turn off a mod without deleting it.


11. Minimal example

Folder:

Data\Addons\PATCH_COOK\
  test_swap\
    info.json
    test_swap.cook

info.json:

{
  "visible": true,
  "name": "test_swap",
  "type": "cook",
  "version": "1.0",
  "description": "Simple offline test swap.",
  "author": "you"
}

test_swap.cook:

cook_stage 1

find "TEST_A" -> "TEST_B"