QSP (Quest Soft Player)
Status: Planned — No implementation started. Open-source reference engine available.
Background
QSP is a Russian interactive fiction engine widely used for text adventures and eroge in Russian-speaking communities. Games are written in QSP-lang, a BASIC-like scripting language. The engine is open source under GPLv2.
Format
.qsp— compiled binary format. Essentially the source text XOR-scrambled with a key, stored in a simple binary envelope with per-location headers. Not true bytecode — the runtime parses text at load time..qsps— plain text format (UTF-8). Functionally identical to.qspbut unobfuscated. Development files are typically.qsps; distributed games are usually.qsp.
Both formats contain locations — named blocks analogous to rooms/passages in other IF engines. Each location has optional act (action) blocks and an onuse (default action) block.
The @qsp/converters npm package provides documented read/write functions for both formats. The text format is fully documented in the QSP documentation.
Games may embed images, sounds, and HTML in the location text.
QSP-lang
BASIC-like scripting language with:
- Variables (strings and numbers — no type declarations;
$varnamefor string,varnamefor numeric) IF ... THEN ... ELSEIF ... ELSE ... END/act 'text': ...goto 'location'/gosub 'location'/xgoto 'location'(inter-location navigation)msg 'text'/p 'text'/pl 'text'— text output*pl/*p— clear output / append to outputaddobj 'name'/delobj 'name'/killobj— inventory managementsetact 'text', 'code'— add action buttonmenu 'title'— display menuplay 'file'— audio playbackshowimage 'file'— display imagecopyarr/sortarr— array operationsinput 'prompt'— text input
Lifting Strategy
Full recompilation (Tier 2). The goal is emitted TypeScript (or Rust) per location — not bundling the libqsp interpreter.
- Decode the
.qspbinary (XOR key is known;@qsp/convertershandles this) or read.qspsdirectly - Parse QSP-lang source text per location
- Emit IR — each location is a function,
gotobecomes a call/jump,actbecomes a choice registration - Run the standard transform pipeline
- Emit TypeScript with a thin QSP runtime shim
Note: WASM alternative existslibqsp (GPLv2 C engine) can be compiled to WASM via Emscripten. This works for quick deployment but ships an interpreter loop — not emitted code, not backend-agnostic, opaque to analysis.
What Needs Building
Format Parser (new crate: reincarnate-frontend-qsp)
- [ ]
.qspdecoder — XOR descramble + binary envelope parser (reference:@qsp/converters) - [ ] QSP-lang text parser:
- Location declarations
- Statement list:
IF,act,goto/gosub/xgoto, output (msg,p,pl,*pl,*p),addobj/delobj,setact,play,showimage,input,menu - Expressions: arithmetic, string concatenation (
&), comparison,and/or/no - Built-in functions:
len($str),mid($str, pos, len),ucase($str),lcase($str),str(num),val($str),rand(min, max),max(a, b),min(a, b),arrsize(arr),loc($name), etc.
IR Mapping
- Location → function
goto 'loc'→Op::Bror tail call to location functiongosub 'loc'→Op::Callact 'text': code end→SystemCall("QSP.RegisterAction", text, function)inlinemsg 'text'→SystemCall("QSP.Output", text)+ implicit yieldaddobj / delobj→SystemCall("QSP.Inventory.*")showimage 'file'→SystemCall("QSP.ShowImage", file)input 'prompt'→SystemCall("QSP.Input", prompt)+Yield
Replacement Runtime (runtime/qsp/ts/)
- [ ] Text output buffer (HTML-formatted)
- [ ] Action list (buttons/links)
- [ ] Inventory panel
- [ ] Image display
- [ ] Menu dialog
- [ ] Text input dialog
- [ ] Audio playback
- [ ] Save/load (variable state + current location)
- [ ]
gosubcall stack