Decode and encode ASUS Turbo VCore .OC overclock profile files
TurboVcoreSDK is a pure-Python toolkit that turns ASUS Turbo VCore save files into human-readable voltage and ratio reports (text + JSON), and can rebuild valid .OC XML from edited settings. It is intended for documentation, logging, archival, and careful offline editing of Turbo VCore profiles—not for live hardware control.
| Language | Python 3.11+ |
| Dependencies | Standard library only |
| Input | ASUS Turbo VCore .OC (XML) |
| Output | Text reports, JSON settings, re-encoded .OC |
| Entry points | turbo_decode.py, turbo_encode.py, python -m src |
- Motivation
- Important disclaimers
- How Turbo VCore stores values
- Requirements
- Repository layout
- Quick start
- CLI reference
- Decode workflow
- Encode workflow
- Output formats
- Conversion model
- Known voltage and ratio profiles
- Key registry and unknown settings
- Using the library in Python
- Extending profiles and key maps
- Testing
- Design principles
- Current limitations
- Roadmap
- Sample files
- Contributing / review checklist
- License
- Credits
ASUS Turbo VCore (and related motherboard utilities) can export overclock profiles as .OC files. Those files are XML documents filled with opaque integer fields, for example:
<item key="0x03020031">174</item>The integer 174 is not a voltage in volts, millivolts, or “1.74 V”. It is a raw step count: how many adjustment increments have been applied from a rail’s minimum. The real engineering value depends on:
- the rail’s minimum voltage (value at raw
0) - the rail’s step size (volts per raw increment)
- and, for ratios, a fixed offset from raw to multiplier
Without those formulas, .OC files are effectively opaque. This project exists so you can:
- Export BIOS / Turbo VCore settings into documentation and logs
- Inspect voltages and ratios in plain language
- Edit settings in JSON and write a new
.OC - Round-trip known values with deterministic encode/decode math
Overclocking and voltage changes can damage hardware, void warranties, cause instability, data loss, or failure to boot.
Please read carefully:
- This software does not talk to your BIOS, firmware, or hardware. It only reads and writes files on disk.
- Formulas and key maps are incomplete and partially experimental. Only a subset of item keys are mapped to named rails. Unmapped keys are preserved as raw integers only.
- PLL Termination Voltage uses a non-standard step size confirmed against experimental BIOS arrow-press data. Other rails use anchors documented in the project design notes; they may differ across CPU generations, motherboards, or Turbo VCore versions.
- BCLK Frequency conversion is provisional and marked as needing verification. Do not trust BCLK decode/encode for production profiles until validated on your hardware.
- Loading a hand-edited
.OCinto Turbo VCore or a motherboard utility is entirely at your own risk. Always keep backups of original profiles and know how to clear CMOS / restore defaults. - This project is not affiliated with ASUS or any motherboard vendor.
Original ASUS Turbo VCore saves are XML, often with a .OC extension. A minimal shape looks like:
<?xml version="1.0" encoding="utf-8"?>
<root>
<minor>
<item key="0x03020031">174</item>
</minor>
</root>Real profiles typically contain many more sections and items. The parser:
- Walks the entire tree
- Collects every
<item key="...">...</item> - Keeps the ElementTree so re-export can preserve structure
For a linear voltage rail:
voltage = minimum + raw × step
raw = round((voltage − minimum) / step)
Example (CPU Core Voltage in this project):
| Raw | Voltage |
|---|---|
| 0 | 1.00000 V |
| 80 | 1.25000 V (1.0 + 80 × 0.003125) |
Example (PLL Termination Voltage):
| Raw | Voltage |
|---|---|
| 20 | 0.33204 V |
| 40 | 0.46408 V |
| 60 | 0.59612 V |
| 174 | 1.34875 V |
For core ratios:
multiplier = raw + 12
raw 28 → 40×
Parsing never hardcodes voltage math. All conversions live in configurable profiles (src/profiles.py). The parser only preserves:
- XML structure
- Element / section tags
- Item keys
- Raw integer values
| Requirement | Detail |
|---|---|
| Python | 3.11 or newer (type hints use modern syntax; tested with 3.12) |
| OS | Windows, Linux, or macOS |
| Third-party packages | None |
| Standard library modules used | argparse, xml.etree.ElementTree, dataclasses, json, pathlib, unittest |
On some Windows setups the python command is not on PATH. If so, use the launcher or full shim name available on your machine, for example:
python3.12 turbo_decode.py examples/sample_profile.OC
# or
py -3.12 turbo_decode.py examples/sample_profile.OCNo pip install step is required to run from a clone of this repository.
TurboVcoreSDK/
├── README.md # This document
├── grok.md # Original design / mission notes
├── turbo_decode.py # CLI wrapper: decode .OC → reports
├── turbo_encode.py # CLI wrapper: JSON → .OC
├── src/
│ ├── __init__.py # Package version
│ ├── __main__.py # python -m src → CLI
│ ├── cli.py # argparse subcommands: decode | encode
│ ├── parser.py # OC XML parse, write, structure updates
│ ├── profiles.py # VoltageProfile, RatioProfile, key registry
│ ├── converter.py # decode_document / encode_settings_dict
│ └── reporter.py # Text + JSON report generation
├── tests/
│ ├── test_profiles.py # All known voltage/ratio anchors
│ ├── test_parser.py # XML parse / write / set_item_raw
│ ├── test_converter.py # End-to-end decode/encode
│ └── test_reporter.py # Report output shape
└── examples/
├── sample_profile.OC # Minimal sample input
├── profile_report.txt # Example decode text (generated)
└── profile.json # Example decode JSON (generated)
| Module | Responsibility |
|---|---|
parser.py |
Load/save XML; OcDocument / OcItem; update raw values without losing sibling keys |
profiles.py |
VoltageProfile, RatioProfile, BclkProfile; named constants; KNOWN_KEY_PROFILES |
converter.py |
Map items through profiles; build JSON-friendly structures; encode settings dicts |
reporter.py |
Human text layout; JSON dump; load settings JSON for encode |
cli.py |
User-facing commands and output path selection |
Clone or copy the repository, then from the repo root:
python turbo_decode.py examples/sample_profile.OCThis writes next to the input file (legacy names used by the wrapper):
| File | Description |
|---|---|
examples/profile_report.txt |
Human-readable report |
examples/profile.json |
Machine-readable settings |
python turbo_encode.py examples/profile.json examples/rebuilt.OCWhen you already have a full .OC from Turbo VCore, use it as a base so unmapped keys and XML structure are preserved:
python turbo_encode.py edited_settings.json output.OC --base-oc original.OCpython -m unittest discover -s tests -vThere are two layers of entry points. Both ultimately call src.cli.main.
python turbo_decode.py <profile.OC> [options...]
python turbo_decode.py --help
Behavior notes:
- Inserts the
decodesubcommand for you. - If you do not pass custom output paths (
-t/--text-out), it adds--legacy-names, so outputs are:profile_report.txtprofile.jsonin the same directory as the input file.
python turbo_encode.py <settings.json> <output.OC> [--base-oc base.OC]
python turbo_encode.py --help
Inserts the encode subcommand for you.
python -m src decode <oc_file> [options]
python -m src encode <settings_json> <output_oc> [options]
python -m src.cli decode ...Program name in help text: turbo_vcore.
| Argument | Description |
|---|---|
oc_file |
Path to the ASUS Turbo VCore .OC file (required) |
-t, --text-out |
Explicit path for the text report |
-j, --json-out |
Explicit path for the JSON report |
-o, --output-dir |
Write {stem}_report.txt and {stem}.json into this directory |
--legacy-names |
Write profile_report.txt and profile.json beside the input |
--title |
Title line for the text report (default: ASUS Turbo VCore Profile) |
--stdout |
Also print the text report to standard output |
Output path priority:
- If both
--text-outand--json-outare set → use those paths. - Else if
--output-diris set →{output_dir}/{stem}_report.txtand{stem}.json. - Else default →
{parent}/{stem}_report.txtand{parent}/{stem}.json. - If
--legacy-namesis set in the default branch →profile_report.txt/profile.jsonin the input’s parent directory.
turbo_decode.py always enables legacy names unless you override with -t/--text-out.
| Argument | Description |
|---|---|
settings_json |
Path to JSON object of settings (required) |
output_oc |
Path for the written .OC file (required) |
-b, --base-oc |
Optional existing .OC to update in place (structure-preserving) |
Without --base-oc, the encoder builds a minimal document:
<root>
<minor>
<item key="...">...</item>
...
</minor>
</root>That is enough for round-trips of known keys, but a full Turbo VCore profile may expect many more nodes. Prefer --base-oc when feeding files back into the utility.
profile.OC
│
▼
parser.parse_oc_file()
│ OcDocument (ElementTree + list[OcItem])
▼
converter.decode_document()
│ For each item:
│ look up KNOWN_KEY_PROFILES[key]
│ if found → profile.decode(raw)
│ else → raw only (known=False)
▼
reporter.write_reports()
│
├── profile_report.txt (or custom name)
└── profile.json
Console summary after a successful decode:
Wrote examples\profile_report.txt
Wrote examples\profile.json
Decoded 1/1 settings with known profiles
The Decoded N/M line tells you how many item keys had conversion profiles. On a real multi-key profile you will often see something like Decoded 1/87 until more keys are registered.
settings.json
│
▼
reporter.load_settings_json()
│
▼
converter.encode_settings_dict(settings, base_document?)
│ For each named setting:
│ resolve profile by name (or key)
│ encode voltage/multiplier/value → raw
│ or pass through explicit "raw"
│ If base_document: set_item_raw() on matching keys
│ Else: create_minimal_document()
▼
parser.write_oc_file()
│
▼
output.OC
Each top-level key is normally a human-readable setting name (or a hex key). Values may be:
{
"PLL Termination Voltage": { "voltage": 1.34875 },
"PLL Termination Voltage (raw form)": { "raw": 174, "key": "0x03020031" },
"Some Rail": { "value": 1.25 },
"Core 1": { "multiplier": 40 },
"Bare number means engineering value when name is known": 1.34875
}Field priority when encoding from an object:
| Field | Meaning |
|---|---|
voltage |
Engineering volts (for unit == "V" profiles) |
multiplier |
Core ratio multiplier (for ratio profiles) |
frequency_mhz |
BCLK-style frequency |
value |
Generic engineering value |
raw |
Skip conversion; write this integer (optionally with key) |
key |
Override which XML item key is written/updated |
Keys starting with _ are treated as metadata and skipped.
# 1. Decode
python turbo_decode.py myboard.OC
# 2. Edit profile.json voltages carefully
# 3. Encode back onto the original structure
python turbo_encode.py profile.json myboard_edited.OC --base-oc myboard.OCEncoding uses rounding to the nearest raw step:
raw = round((voltage − minimum) / step)
Slight floating-point drift is expected to snap back to the nearest discrete BIOS step.
Example (examples/profile_report.txt after decoding the sample):
ASUS Turbo VCore Profile
CPU Voltages
============
PLL Termination Voltage
Raw Value: 174
Voltage: 1.34875V
Source: examples\sample_profile.OC
Sections produced when present:
| Section | Contents |
|---|---|
| CPU Voltages | Settings with unit V (or names containing Voltage) |
| CPU Ratios | Settings with unit x / core names |
| Other Settings | Everything else (including unknown keys with raw only) |
Unknown items appear under “Other Settings” with key, raw value, and a note that no conversion profile exists.
Example:
{
"PLL Termination Voltage": {
"raw": 174,
"voltage": 1.34875,
"key": "0x03020031"
}
}Per-setting fields that may appear:
| Field | When |
|---|---|
raw |
Always |
voltage |
Decoded voltage rail |
multiplier |
Decoded core ratio |
frequency_mhz |
Decoded BCLK-style profile |
value |
Other decoded engineering units |
key |
XML item key when known |
unit |
Present in some intermediate dict helpers |
The top-level object is keyed by setting name (or raw hex key if the setting is unknown).
Implemented as VoltageProfile in src/profiles.py:
class VoltageProfile:
def decode(self, raw: int) -> float:
return round(self.minimum + raw * self.step, self.decimals)
def encode(self, voltage: float) -> int:
return int(round((voltage - self.minimum) / self.step))Attributes:
| Attribute | Role |
|---|---|
name |
Human-readable label |
minimum |
Voltage at raw 0 |
step |
ΔV per raw step |
unit |
Default "V" |
decimals |
Rounding for display / decode |
class RatioProfile:
def decode(self, raw: int) -> float:
return float(raw + self.offset) # default offset = 12
def encode(self, multiplier: float) -> int:
return int(round(multiplier - self.offset))Confirmed anchor: raw 28 → 40×.
frequency_mhz ≈ base_mhz + (raw − raw_base) × scale
Defaults currently:
| Parameter | Value |
|---|---|
base_mhz |
100.0 |
raw_base |
1000 |
scale |
0.1 |
This formula is a placeholder. Do not rely on it until validated against real Turbo VCore / BIOS exports for your platform.
These constants are defined in src/profiles.py and covered by unit tests.
| Profile constant | Display name | Formula | Anchor (raw → value) |
|---|---|---|---|
CPU_CORE_VOLTAGE |
CPU Core Voltage | 1.00000 + raw × 0.003125 |
80 → 1.25000 V |
DRAM_VOLTAGE |
DRAM Voltage | 0.800 + raw × 0.005 |
110 → 1.350 V |
CPU_CACHE_VOLTAGE |
CPU Cache Voltage | 1.000 + raw × 0.003125 |
64 → 1.20000 V |
CPU_SYSTEM_AGENT_VOLTAGE |
CPU System Agent Voltage | 0.80000 + raw × 0.003125 |
48 → 0.95000 V |
PLL_TERMINATION_VOLTAGE |
PLL Termination Voltage | 0.20000 + raw × 0.006602 |
174 → 1.34875 V |
CPU_INPUT_VOLTAGE |
CPU Input Voltage (VCCIN) | 0.80 + raw × 0.01 |
111 → 1.91 V |
PLL Termination is not a normal decimal step (not 0.005 / 0.00625). Experimental notes:
- Raw step count matches BIOS arrow presses
- Minimum: 0.20000 V
- Implementation step: 0.006602 (chosen so verification points hit cleanly)
Verification points:
| Raw | Expected voltage |
|---|---|
| 20 | 0.33204 V |
| 40 | 0.46408 V |
| 60 | 0.59612 V |
| 174 | 1.34875 V |
Note on the original design doc:
grok.mdlisted step ≈0.00660143678. That value is very close but does not land exactly on the same verification table (especially raw 174 → 1.34875). This codebase uses0.006602, which satisfies the tabulated checkpoints and still round-trips correctly.
| Name keys | Formula | Anchor |
|---|---|---|
Core 1 … Core 6 / core01 … core06 |
multiplier = raw + 12 |
raw 28 → 40× |
| Profile | Status |
|---|---|
BCLK_FREQUENCY |
Provisional; needs experimental verification |
XML items are identified by hexadecimal keys, not by English names:
<item key="0x03020031">174</item>Decoding to a named voltage requires a key → profile map.
| XML key | Profile |
|---|---|
0x03020031 |
PLL Termination Voltage |
Defined in KNOWN_KEY_PROFILES inside src/profiles.py.
If a key is not in the registry:
- It still appears in reports
- Only the raw integer is shown
knownisfalsein converter structures- Encode with
--base-ocpreserves those values unless you overwrite them
This is intentional: a partial map must never drop settings from a full profile.
PROFILES_BY_NAME maps human-readable names (and some short ids like core01) to profiles so JSON like:
{ "PLL Termination Voltage": { "voltage": 1.35 } }can be encoded without the user knowing the hex key—when that name is known and a key is registered for it. For PLL, encode resolves name → key 0x03020031.
Run from the repository root (or ensure the repo root is on PYTHONPATH).
from src.parser import parse_oc_file
from src.converter import decode_document
from src.reporter import format_text_report, format_json_report, write_reports
doc = parse_oc_file("examples/sample_profile.OC")
profile = decode_document(doc)
print(format_text_report(profile))
print(format_json_report(profile))
write_reports(profile, "out_report.txt", "out.json")from src.converter import encode_settings_dict
from src.parser import parse_oc_file, write_oc_file
settings = {
"PLL Termination Voltage": {"voltage": 1.34875},
}
# Minimal new document
doc = encode_settings_dict(settings)
write_oc_file(doc, "minimal.OC")
# Or patch an existing full profile
base = parse_oc_file("original.OC")
doc = encode_settings_dict(settings, base_document=base)
write_oc_file(doc, "patched.OC")from src.profiles import (
CPU_CORE_VOLTAGE,
PLL_TERMINATION_VOLTAGE,
RatioProfile,
)
assert CPU_CORE_VOLTAGE.decode(80) == 1.25
assert PLL_TERMINATION_VOLTAGE.encode(1.34875) == 174
ratio = RatioProfile(name="Core 1")
assert ratio.decode(28) == 40.0
assert ratio.encode(40) == 28from src.profiles import CPU_CORE_VOLTAGE, register_key
register_key("0xYOURKEYHERE", CPU_CORE_VOLTAGE)For permanent maps, edit KNOWN_KEY_PROFILES in src/profiles.py and add tests.
- Define a
VoltageProfileconstant insrc/profiles.pywith measuredminimumandstep. - Add it to
PROFILES_BY_NAME. - If you know the XML key, add it to
KNOWN_KEY_PROFILES. - Add unit tests in
tests/test_profiles.pyfor the known anchor(s). - Optionally add converter/parser tests with a tiny sample XML fragment.
Planned approach (not automated yet):
- Compare IFR / BIOS setup dumps with Turbo VCore exports
- Diff
.OCfiles after changing one BIOS option at a time - Map keys experimentally the same way PLL was mapped
Do not put voltage formulas in parser.py. New rails always go through profiles so decode and encode stay symmetric.
python -m unittest discover -s tests -v| Test module | Focus |
|---|---|
test_profiles.py |
CPU core, DRAM, cache, SA, PLL verification points, VCCIN, core ratio 28→40×, encode/decode round-trips |
test_parser.py |
Item collection, section paths, find, write/reread, set_item_raw, minimal document creation |
test_converter.py |
Known PLL key decode, unknown raw-only keys, JSON shape, encode by voltage/raw, base document patching |
test_reporter.py |
Text content, JSON validity, write_reports file creation |
As of the current tree, the suite is 35 tests, all expected to pass on Python 3.12.
python turbo_decode.py examples/sample_profile.OC
# Inspect examples/profile_report.txt and examples/profile.json
python turbo_encode.py examples/profile.json examples/roundtrip.OC
# Inspect examples/roundtrip.OC for key 0x03020031 / raw 174- Profiles are data, not parser logic — conversion formulas are swappable and testable in isolation.
- Structure preservation — encode-with-base updates raw values without discarding unknown siblings.
- Loss-aware partial knowledge — unknown keys remain visible as raw, never silently dropped.
- Standard library only — easy to run on air-gapped lab machines.
- Type hints everywhere — clearer public surfaces for future GUI or comparison tools.
- Round-trip honesty — encode uses rounding to discrete steps; floats that are not on a step boundary snap to the nearest raw.
Be aware of these before publishing or relying on the tool for full profiles:
| Limitation | Detail |
|---|---|
| Sparse key map | Only 0x03020031 (PLL Termination) is registered by default |
| Named profiles without keys | Core/DRAM/VCCIN math exists for encode-by-name and tests, but automatic decode from real OC keys needs those keys registered |
| BCLK formula | Provisional / unverified |
| Turbo VCore versions | No multi-version schema handling yet |
| Minimal encode documents | Without --base-oc, output is a tiny <root><minor>… skeleton, not a full utility-compatible dump |
| No GUI | CLI and library only |
| No profile diff | Comparison / Markdown export are roadmap items |
| Platform variance | Steps and mins may differ by board / CPU / BIOS; validate anchors on your system |
The original design (grok.md) calls for future work that the architecture is meant to accommodate without a rewrite:
- GUI front-end
- BIOS / profile comparison and difference reports
- Markdown report export
- Automatic BIOS variable mapping from IFR dumps
- Support multiple ASUS Turbo VCore versions
- Expand
KNOWN_KEY_PROFILESfor core, DRAM, cache, SA, VCCIN, and ratios - Validate and lock BCLK formula experimentally
- Optional packaging (
pyproject.toml) and console script entry points
| Path | Purpose |
|---|---|
examples/sample_profile.OC |
Minimal valid OC with PLL key 0x03020031 = 174 |
examples/profile_report.txt |
Generated text report from the sample |
examples/profile.json |
Generated JSON from the sample |
grok.md |
Mission brief, original formulas, and product requirements |
Regenerate sample outputs anytime:
python turbo_decode.py examples/sample_profile.OCBefore opening a PR or tagging a release, please verify:
-
python -m unittest discover -s tests -vis green - New voltage rails include encode and decode tests with at least one known anchor
- New XML keys are documented in this README’s key table
- Changes to PLL or other experimental steps include a short rationale (anchors used)
- No third-party dependencies added without discussion
- Encode paths that touch real profiles prefer documenting
--base-ocusage - README formulas match
src/profiles.pyconstants
You may want to add (not required by the code itself):
.gitignorefor__pycache__/,*.pyc, local.OCdumps you do not want public, IDE folders- A clear license file (MIT/Apache-2.0/etc.) if the project is public
- Redact any personal BIOS dumps that contain machine-identifying data
This README was written for local review; the repository is not automatically published.
Use and modify freely for personal overclock documentation and tooling unless a separate license file is added to the repository. If you publish a fork, please keep the safety disclaimers.
No warranty. See Important disclaimers.
- Mission and conversion anchors documented in
grok.md(experimental BIOS / Turbo VCore observations). - Implementation as a stdlib Python package under
src/, with CLI wrappersturbo_decode.pyandturbo_encode.py.
TurboVcoreSDK turns ASUS Turbo VCore raw step counts into real voltages and ratios—and can write them back—using explicit, testable conversion profiles and structure-preserving XML handling.