<!-- Generated by KoalaBox::sync tool. Manual changes to this file will be lost. -->

# SmokeAPI



_Legit DLC ownership emulation for Steamworks._

## ✨ Features

* `🔓` Emulate DLC ownership in legitimately owned games
* `🛅` Emulate Inventory item ownership
* `📄` Optional configuration
* `🐧` Support for 32-bit and 64-bit Windows and Linux systems

## 🔗 Links

* `📥` [Download the latest release](https://github.com/acidicoala/SmokeAPI/releases/latest)
* `🪧` [SmokeAPI forum topic](https://cs.rin.ru/forum/viewtopic.php?p=2597932#p2597932)
* `🗂️` [DLC Database](https://steamdb.info/)

## 📖 Introduction

### ❓ What is SmokeAPI?

SmokeAPI is a tool for Steamworks DLC ownership emulation in games that are _legitimately_ owned in Steam.
It attempts to fool games that use Steamworks SDK (Steamworks) into thinking that you own the game's DLCs.
However, SmokeAPI does not modify the rest of the Steamworks, hence features like multiplayer, achievements, and so on remain fully functional.

### ❔ Which games are supported?

Only games that use Steamworks SDK for the DLC ownership verification are supported.
Hence, if a game's installation directory does not contain any `steam_api.dll` or `steam_api64.dll` files then it's definitely not supported.
Even if a game uses Steamworks DLL, it's not guaranteed to be supported because each game might implement additional custom verification checks.
Therefore, **you have to first research the game's topic**, to see if it supports unlocking.

Additionally, there are several points to bear in mind when it comes to unlocking DLCs with SmokeAPI:


* SmokeAPI most definitely will not work with games that use 3rd party DRM, such as games from Ubisoft, Rockstar, etc.
* SmokeAPI most likely will not work with games that use Denuvo SecureDLC.
* SmokeAPI is unlikely to unlock anything in Free-To-Play games since they typically store all player data on the corresponding game server and hence all the checks are server-side.
* SmokeAPI will not work with games that employ additional ownership protection or if the game is using alternative DLC verification mechanism.
* SmokeAPI is unlikely to work with games that use an anti-cheat, since they typically detect any DLL/EXE that has been tampered with. Sometimes it is possible to disable an anti-cheat, but that typically entails the loss of online capabilities. Search in the respective game topic for more information about how to disable anti-cheat.
* Some games include DLC files in their base game, regardless of whether you own the DLC or not. However, some games download additional DLC files only after a user has bought the corresponding DLC. In this case, not only will you need to install SmokeAPI, but you also have to get the additional DLC files from somewhere else and put them into the game folder. Up-to-date DLC files often can be found in corresponding game topics.
* Some games don't use any DRM at all, in which case SmokeAPI is useless. All you need to do is to download the DLC files and place them in the game folder.

## :shipit: Usage

> [!WARNING]
> Please proceed with usage at your own risk.
> Usage of this unlocker entails breaking one or more terms of service,
> which might result in a permanent loss of your account.

To use SmokeAPI, you need to install it into the game folder following the instructions below.
Once installed, SmokeAPI is automatically loaded by a game every time you launch the game.
SmokeAPI supports **2** installation modes: _Hook mode_ and _Proxy mode_.

### ↔️ Mode comparison

#### 🪝 Hook mode

- **Advantages**:
    - Persists after game updates
    - Can be loaded by other injectors
    - Can sometimes bypass DLL integrity checks
- **Disadvantages**:
    - Might need an additional DLL to get injected (like [Koaloader])

#### 🔀 Proxy mode

- **Advantages**:
    - Guaranteed to load
- **Disadvantages**:
    - Might need reinstallation after a game update

My advice is to try installing the unlocker in hook mode first.
If it doesn't work, try installing it in proxy mode instead.
If that didn't work, refer to the _Troubleshooting_ section below.

## 🛠 Installation instructions (🪟 Windows)

> [!NOTE]
> To determine the bitness of a game you can open _Task Manager_, navigate to _Details_ tab,
> right-click on the column headers, click _Select columns_, tick checkbox next to _Platform_ and click _OK_.
> This will allow you to see a game's bitness in the _Details_ tab while a game is running.

### 🪝 Hook mode

Hook mode itself has two sub-modes: Self-Hook and Hook with injector.

#### 🪝 Self-Hook mode

In self-hook mode SmokeAPI is injected automatically without the help of third-party injectors.
It works best when a game doesn't have any protections against DLL injection.
The main advantage of this mode is its minimal setup, which adds only 1 new DLL to the game folder.

- Download the [latest SmokeAPI release zip].
- From this downloaded zip extract `smoke_api32.dll` or `smoke_api64.dll`, depending on a game's bitness.
- Rename the unzipped DLL to `version.dll` or `winhttp.dll` or `winmm.dll`.
- Place this `version.dll` or `winhttp.dll` or `winmm.dll` next to the game's `.exe` file.

#### 🪝 Hook mode with Koaloader

If a game doesn't load `version.dll` or `winhttp.dll` or `winmm.dll`, you can use an alternative injector to load
SmokeAPI into the game process.
One such injector is [Koaloader], which supports different DLLs that a typical game might load.
For example, assuming that the game loads `d3d11.dll`:

- Install Koaloader:
    - Download the [latest Koaloader release zip].
    - From this downloaded zip extract `d3d11.dll` from `d3d11-32` or `d3d11-64`, depending on a game's bitness.
    - Place `d3d11.dll` next to the game's `.exe` file.
- Install SmokeAPI
    - Download the [latest SmokeAPI release zip].
    - From this downloaded zip extract `smoke_api32.dll` or `smoke_api64.dll`, depending on a game's bitness.
    - Place `smoke_api32.dll` or `smoke_api64.dll` next to the game's `.exe` file.

#### 🪝 Hook mode with Special K

There are games which have extra protections that break hook mode.
In such cases, it might be worth trying [Special K], which can inject SmokeAPI as a [custom plugin].

---

### 🔀 Proxy mode

- Find a `steam_api.dll` or `steam_api64.dll` file in game directory, and rename it to `steam_api_o.dll` or `steam_api64_o.dll`.
- Download the [latest SmokeAPI release zip].
- From this downloaded zip extract `smoke_api32.dll` or `smoke_api64.dll`, depending on a game's bitness.
- Rename this extracted DLL to `steam_api.dll` or `steam_api64.dll`, depending on a game's bitness.
- Place this renamed unlocker DLL next to the `steam_api_o.dll` or `steam_api64_o.dll` file.

[latest SmokeAPI release zip]: https://github.com/acidicoala/SmokeAPI/releases/latest
[latest Koaloader release zip]: https://github.com/acidicoala/Koaloader/releases/latest
[Special K]: https://www.special-k.info
[custom plugin]: https://wiki.special-k.info/en/SpecialK/Tools#custom-plugin

## 🛠️ Installation instructions (🐧 Linux)

> [!NOTE]
> Linux support in SmokeAPI is highly experimental/unstable and has known issues.
> If none of the methods below work for you, then consider running a Windows version of a game
> via Proton compatibility layer and follow the instructions in the Windows section.

### ✔️ Requirements

Linux builds of SmokeAPI depend on several libraries. These libraries are usually included in the 
[Steam Linux Runtime 3.0 (sniper)](https://github.com/ValveSoftware/steam-runtime#readme),
so nothing is required to be installed on the system. But when launching a game directly via its executable
(such as in case of hook mode) your system needs to have the following required libraries installed.

The following list features links in Arch Linux repositories, but if you are using a different distribution,
you should use the equivalent package for your distro.

Required libraries:
- [gcc-libs](https://archlinux.org/packages/core/x86_64/gcc-libs/)
  [[32-bit](https://archlinux.org/packages/core/x86_64/lib32-gcc-libs/)]

- [glibc](https://archlinux.org/packages/core/x86_64/glibc/)
  [[32-bit](https://archlinux.org/packages/core/x86_64/lib32-glibc/)]

- [libidn2](https://archlinux.org/packages/core/x86_64/libidn2/)
  [[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-libidn2/)]

- [libnghttp2](https://archlinux.org/packages/core/x86_64/libnghttp2/)
  [[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-libnghttp2/)]

- [zlib](https://archlinux.org/packages/core/x86_64/zlib/)
  [[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-zlib/)]

- [zstd](https://archlinux.org/packages/core/x86_64/zstd/)
  [[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-zstd/)]


Optional libraries:
- [gtk3](https://archlinux.org/packages/extra/x86_64/gtk3/)
  [[32-bit](https://archlinux.org/packages/multilib/x86_64/lib32-gtk3/)]

---

Just like on Windows, SmokeAPI features 32 and 64-bit Linux builds.
In release archive they are named as `libsmoke_api32.so` or `libsmoke_api64.so` respectively.
However, unlike Windows, it is recommended to use proxy mode, rather than hook mode.
This is because the current hook mode installation method has to directly launch game
executable. However, by default Steam doesn't do that, instead it launches certain wrappers
that setup game environment with optimal parameters. Hence, launching a game without those
wrappers might cause issues in theory. However, in practice real tests show that hook mode has higher
chance of success compared to proxy mode. So, at the end of the day, try both modes to see which one works
best for you.

### 🔀 Proxy mode (🐧 Linux)

Same as on Windows:
1. Rename the original `libsteam_api.so` to `libsteam_api_o.so`
2. Extract and paste the `libsmoke_api32.so` or `libsmoke_api64.so` to the same directory, renaming it to `libsteam_api.so`.

### 🪝 Hook mode (🐧 Linux)

Linux doesn't have the same easily exploitable library injection mechanism that Windows has.
However, it is possible to force any Linux executable to load any library by using the
`LD_PRELOAD` environment variable. In fact, Steam itself already uses that to inject its overlay,
hence we can use it as well. But we have to include that overlay library as well when specifying
`LD_PRELOAD`, otherwise the game will be launched with SmokeAPI, but without Steam overlay.

For example:

1. Extract and paste the `libsmoke_api32.so` or `libsmoke_api64.so` in the root of game's installation directory.
2. In Steam _Library_ open game's _Properties_, switch to the _General_ tab, and set the following _LAUNCH OPTIONS_:

| Bitness | Launch Options                                                                                                                  |
|---------|---------------------------------------------------------------------------------------------------------------------------------|
| 32-bit  | `LD_PRELOAD="./libsmoke_api32.so $HOME/.local/share/Steam/ubuntu12_32/gameoverlayrenderer.so" ./<GameExe32> ; exit ; %command%` |
| 64-bit  | `LD_PRELOAD="./libsmoke_api64.so $HOME/.local/share/Steam/ubuntu12_64/gameoverlayrenderer.so" ./<GameExe64> ; exit ; %command%` |

Where `<GameExe32>` and `<GameExe64>` correspond to the actual filename of the game executable. For example:
- `TheEscapists2.x86` (32-bit)
- `TheEscapists2.x86_64` (64-bit)
- `_linux/darkest.bin.x86` (32-bit)
- `_linux/darkest.bin.x86_64` (64-bit)
- `eurotrucks2` (64-bit)
- `binaries/victoria3` (64-bit)

And so on. Notice that Linux executables do not have `.exe` extension like on Windows, so make sure to copy the entire
file name as it is. Naturally, the exact options might change depending on where files are located on your filesystem
and other environment variables you might have specified previously.
If you have other environment variables, and you don't know how to correctly combine them,
then please make extensive use of search engines and LLMs for guidance and examples
before seeking help the official forum topic.

> [!NOTE]
> The `; exit ; %command%` at the end of launch options
> is a trick used to force Steam to directly run the game executable.

## ⚙ Configuration

SmokeAPI does not require any manual configuration.
By default, it uses the most reasonable options and tries to unlock all DLCs that it can.
However, there might be circumstances in which you need more custom-tailored behaviour, such as disabling certain DLCs, or selectively enabling just a few of them.
In this case you can use a configuration file [SmokeAPI.config.json](res/SmokeAPI.config.json) that you can find here in this repository or in the release zip.
To use it, simply place it next to the SmokeAPI DLL.
It will be read upon each launch of a game.

The config file is expected to conform to the JSON standard. It is recommended to use a text editor with JSON schema supports. This greatly assists with editing since it warns not just about syntax errors, but also about invalid values. One such editor is [Visual Studio Code](https://code.visualstudio.com/).
<details><summary>VS Code demo</summary>

This example showcases how VS code highlights an invalid value and displays a list of valid values that are accepted.
![VS Code demo](https://i.ibb.co/0y45qgtQ/schema-config-demo.jpg)
</details>

Below you can find the detailed description of each available config option. In the absence of the config file, default values specified below will be used.

| Option | Description | Type | Default | Valid values |
|--------|-------------|------|---------|--------------|
| `logging` | Enables logging to SmokeAPI.log.log file. | Boolean | `false` | `true` or `false`. |
| `log_steam_http` | Toggles logging of SteamHTTP traffic | Boolean | `false` | `true` or `false`. |
| `default_app_status` | Specifies default DLC status. | String | `"unlocked"` | `"unlocked"` or `"locked"` or `"original"`. |
| `override_app_status` | Overrides the status of all DLCs that belong to a specified app ID. | Object | `{}` | An object with `"key": "value"` pairs, where key is App ID and value is App status. |
| `override_dlc_status` | Overrides the status of individual DLCs, regardless of the corresponding app status. | Object | `{}` | An object with `"key": "value"` pairs, where key is DLC ID and value is DLC status. |
| `auto_inject_inventory` | Specifies whether SmokeAPI should automatically inject a list of all registered inventory items, when a game queries user inventory | Boolean | `true` | `true` or `false`. |
| `extra_inventory_items` | A list of inventory item IDs that will be added in addition to the automatically injected items. | Array | `[]` | An array of integer App IDs. |
| `extra_dlcs` | See [Extra info](#-how-smokeapi-works-in-games-with-large-number-of-dlcs) to understand the use case for this option. | Object | `{}` | An object with `"key": "value"`, where the key is App ID and value is an object with `"dlcs"` property. See the complete example for more. |


<details><summary>Advanced options</summary>

[//]: # (Admonitions don't work inside collapsible spoilers on GitHub :/)

**NOTE**: These options do not affect the unlocker, and should be left unmodified. They serve as utilities for text or GUI editors.

| Option | Description | Type | Default | Valid values |
|--------|-------------|------|---------|--------------|
| `$schema` | URL of a JSON Schema that corresponds to this config. | String | [SmokeAPI.schema.json](res/SmokeAPI.schema.json) | URL to a valid SmokeAPI config JSON schema. |
| `$version` | Reserved for use by tools like GUI config editors. | Integer | `4` | Integer numbers from 1 and beyond. |

</details>

---

Below you can find an example config where nearly every option has been customized.

<details><summary>Complete example</summary>

```json
{
  "$schema": "https://raw.githubusercontent.com/acidicoala/SmokeAPI/refs/tags/v4.0.0/res/SmokeAPI.schema.json",
  "$version": 4,
  "logging": true,
  "log_steam_http": true,
  "default_app_status": "unlocked",
  "override_app_status": {
    "1234": "original",
    "4321": "unlocked"
  },
  "override_dlc_status": {
    "1234": "original",
    "4321": "unlocked",
    "5678": "locked"
  },
  "auto_inject_inventory": true,
  "extra_inventory_items": [
    9876,
    8765,
    7654
  ],
  "extra_dlcs": {
    "1234": {
      "dlcs": {
        "56789": "Example DLC 1"
      }
    },
    "4321": {
      "dlcs": {
        "98765": "Example DLC 2",
        "98766": "Example DLC 3"
      }
    }
  }
}
```
</details>


## 🎓 Extra info

### 🔑 How SmokeAPI works in games with large number of DLCs

Some the games that have a large number of DLCs begin ownership verification by querying the Steamworks API for a list of all available DLCs.
Once the game receives the list, it will go over each item and check the ownership.
The issue arises from the fact that response from Steamworks SDK may max out at 64, depending on how much unowned DLCs the user has.
To alleviate this issue, SmokeAPI will make a web request to Steam API for a full list of DLCs, which works well most of the time.
Unfortunately, even the web API does not solve all of our problems, because it will return only DLCs that are available in Steam store.
This means that DLCs without a dedicated store offer, such as pre-order DLCs will be left out.
That's where the `extra_dlcs` config option comes into play.
You can specify those missing DLC IDs there, and SmokeAPI will make them available to the game.
However, this introduces the need for manual configuration, which goes against the ideals of this project.
To remedy this issue SmokeAPI will also fetch a manually maintained list of [extra DLCs] stored in a GitHub repository.
The purpose of that JSON file is to contain all the DLC IDs that are lacking a Steam store page.
This enables SmokeAPI to unlock all DLCs without any config file at all.
Feel free to report in the {forum-topic} games that have more than 64 DLCs,
_and_ have DLCs without a dedicated store page.
They will be added to the list of missing DLC IDs to facilitate config-less operation.

[extra DLCs]: https://github.com/acidicoala/public-entitlements/blob/main/steam/v2/dlc.json

### 🔢 Supported Steamworks SDK versions

SmokeAPI aims to support all Steamworks SDK version.
At the time of writing, the latest supported version is **1.63**.

## 🔧 Troubleshooting

### 🔐 DLCs are not unlocked

There are many reasons why the DLCs remain locked. In some games DLC unlocking is inherently impossible because of online-only state, profile, etc. In other cases it may be possible, but only after dealing with custom game checks. To learn about the specifics, consult the corresponding game topic in [the forum].

If you are sure that DLC unlocking in a targeted game is inherently possible, then you have to verify that installation was successful. To do that, add the unlocker's config file next to the unlocker DLL and enable logging in it. You should see a `*.log` file being generated upon the game launch, which could provide insight into what went wrong. Use this log file when requesting support in the forum. If after launching the game no `*.log` file was generated, then it means that installation was not performed correctly.

If you installed the unlocker via proxy mode, then make sure that you have renamed the unlocker DLL exactly like the original DLL and placed it exactly in its place. Also verify that the original DLL was renamed by adding `_o` at the end of the filename. Notice that the second symbol is a literal `o` (short for original), not a numeral zero `0`.

If you installed the unlocker via hook mode, then make sure that you have picked a compatible Koaloader DLL. Not all games will try to load `version.dll`, hence you need will need to try another. You can use [Process Monitor] to find out which Koaloader DLL is supported by a game, and where to place it. Click on the cyan funnel icon on the top to open filter editor, and add 3 filters (Process name, Result, and Path), as shown in the screenshot below. Launch the game with the Process Monitor active, and you should see DLLs that a game was trying to load from its directory.

<details><summary>Process Monitor screenshot</summary>

![Process Monitor](https://i.ibb.co/VmdVWLN/image.png)
</details>

[the forum]: https://cs.rin.ru/forum/viewforum.php?f=10
[Process Monitor]: https://docs.microsoft.com/en-us/sysinternals/downloads/procmon

If you have made sure that you picked the right DLL for Koaloader, then try adding Koaloader's config file and enable logging in it. The log file from Koaloader can show if it was able to successfully load the unlocker DLL.

### 💥 The game is crashing

If the game is crashing or not opening as expected after installing an unlocker, then try to download and install the [Latest supported Visual C++ Redistributable version](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-supported-redistributable-version)
<details><summary>Download page screenshot</summary>

![Download page](https://i.ibb.co/PZBXqHpB/vc-redist.jpg)
</details>

## 🏗️ Building from source

### ✔️ Requirements

- [CMake] v3.24 or newer (Make sure that cmake is available from powershell)
- [Visual Studio Build Tools 2022] with `Desktop Development for C++` workload installed
    - Tested on Windows 11 SDK (10.0.26100.4188)

### 👨‍💻 Commands

Build the project

```powershell
.\build.ps1 $arch $config
```

where

| Variable | Valid values         |
|----------|----------------------|
| $arch    | `32` or `64`         |
| $config  | `Debug` or `Release` |

For example:

```powershell
.\build.ps1 64 Release
```

[Visual Studio Build Tools 2022]: https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022

[CMake]: https://cmake.org/

### 🆕 Adding support for new Steamworks SDK

- Run `steamworks_downloader [prompt]` providing directory which contains Steamworks SDK zip files.
- Run `steamworks_parser`
- Run `linux_exports_generator [32]`
- Run `linux_exports_generator [64]`


## 📚 Acknowledgments

This project makes use of the following open source projects:

- [richgel999/miniz](https://github.com/richgel999/miniz)
- [libcpr/cpr](https://github.com/libcpr/cpr)
- [nlohmann/json](https://github.com/nlohmann/json)
- [stevemk14ebr/PolyHook_2_0](https://github.com/stevemk14ebr/PolyHook_2_0)
- [gabime/spdlog](https://github.com/gabime/spdlog)
- [nsumner/cpp-tree-sitter](https://github.com/nsumner/cpp-tree-sitter)
- [tree-sitter/tree-sitter-cpp](https://github.com/tree-sitter/tree-sitter-cpp)
- [nemtrif/utfcpp](https://github.com/nemtrif/utfcpp)
- [microsoft/wil](https://github.com/microsoft/wil)
- [p-ranav/glob](https://github.com/p-ranav/glob)
- [pantor/inja](https://github.com/pantor/inja)
- [jarro2783/cxxopts](https://github.com/jarro2783/cxxopts)
- [serge1/ELFIO](https://github.com/serge1/ELFIO)
- [Mbed-TLS/mbedtls](https://github.com/Mbed-TLS/mbedtls) 
- [bshoshany/thread-pool](https://github.com/bshoshany/thread-pool)
- [batterycenter/embed](https://github.com/batterycenter/embed)



## 📄 License

This software is licensed under the [Unlicense](https://unlicense.org),
terms of which are available in [UNLICENSE.txt](UNLICENSE.txt).


[Koaloader]: https://github.com/acidicoala/Koaloader
