From 9882984b825a8715df462f89b9c6ca5658b12ad9 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Sun, 30 Aug 2026 18:11:01 -0300
Subject: [PATCH 01/33] ci: migrate CodeQL to advanced setup and add CODEOWNERS
(#16)
The CodeQL default setup only analysed a pull request when it touched
files relevant to the configured languages, so a docs-only PR produced
no analysis while master still carried one per language, leaving the
code_scanning branch rule unable to diff the two sides.
Also add the CODEOWNERS file this repository was missing, so the branch
ruleset can require code owner review like the sibling repositories do.
---
.github/CODEOWNERS | 1 +
.github/workflows/codeql.yml | 55 ++++++++++++++++++++++++++++++++++++
2 files changed, 56 insertions(+)
create mode 100644 .github/CODEOWNERS
create mode 100644 .github/workflows/codeql.yml
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..d2d6bc1
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @NullSablex
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..f7ff48c
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,55 @@
+# CodeQL advanced setup.
+#
+# Replaces the repository's CodeQL *default* setup, which only analyses a pull
+# request when it touches files relevant to the configured languages. A PR that
+# changes only docs or dependency manifests produced no analysis at all, while
+# `master` still carried one per language — so the `code_scanning` branch rule
+# could not diff the two sides and reported "configurations not found".
+#
+# Running here, with no path filter, guarantees both configurations exist on
+# every pull request. The categories below must keep matching the ones recorded
+# on `master` (`/language:actions`, `/language:rust`) for that diff to work.
+name: CodeQL
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+ schedule:
+ # Weekly, to catch newly published queries against unchanged code.
+ - cron: '27 4 * * 1'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ security-events: write # upload the SARIF results
+ actions: read
+ strategy:
+ fail-fast: false
+ matrix:
+ # Keep in sync with the languages the previous default setup covered.
+ language: [ actions, rust ]
+
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
+ with:
+ languages: ${{ matrix.language }}
+ # Neither language needs a compiled build for CodeQL to extract it.
+ build-mode: none
+ queries: security-extended
+
+ - name: Perform CodeQL analysis
+ uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
+ with:
+ category: "/language:${{ matrix.language }}"
From c299a3bca947c4a1a0dbf9e1ba45b547c8440b6e Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 03:36:57 -0300
Subject: [PATCH 02/33] chore: re-pin rust-samp SDK to 51ba519 (v3.4.0)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Traz o lookup_function do bloco de debug (base para a pilha de chamadas),
Amx::exec_public_scope e o hardening de buffer/stack no FFI. Eventos do SDK
(#[event]) são x86-only e viram no-op no check aarch64.
---
Cargo.lock | 229 ++++++++++++++++++++++++++++++---
crates/dap-adapter/Cargo.toml | 2 +-
crates/debug-plugin/Cargo.toml | 2 +-
3 files changed, 213 insertions(+), 20 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 6c06076..500558f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,12 +2,24 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
[[package]]
name = "dap-adapter"
version = "0.1.0"
@@ -49,6 +61,25 @@ dependencies = [
"log",
]
+[[package]]
+name = "generic-array"
+version = "0.14.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "iced-x86"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "interprocess"
version = "2.4.3"
@@ -59,7 +90,7 @@ dependencies = [
"libc",
"recvmsg",
"widestring",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -68,6 +99,12 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
[[package]]
name = "libc"
version = "0.2.186"
@@ -80,12 +117,31 @@ version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+[[package]]
+name = "mach2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "memchr"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
+[[package]]
+name = "mmap-fixed-fixed"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "num-conv"
version = "0.2.2"
@@ -101,6 +157,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
[[package]]
name = "pawnpro-dbg-protocol"
version = "0.1.0"
@@ -140,13 +202,42 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
+[[package]]
+name = "region"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+ "mach2",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "retour"
+version = "0.4.0-alpha.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ead4bc8e12d553ff70769c5f5c21f5f4f0e73c0018068a6bb5a3d7d3b9e57ec7"
+dependencies = [
+ "cfg-if",
+ "generic-array",
+ "iced-x86",
+ "libc",
+ "mmap-fixed-fixed",
+ "once_cell",
+ "region",
+ "slice-pool2",
+]
+
[[package]]
name = "rust-samp"
-version = "3.2.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=e5b5fc1#e5b5fc10cd41351e684c28b0a7cc62071812f3c7"
+version = "3.3.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=51ba519#51ba519157e04258ef49515958491b9ba734e2db"
dependencies = [
"fern",
"log",
+ "retour",
"rust-samp-codegen",
"rust-samp-sdk",
"time",
@@ -154,20 +245,20 @@ dependencies = [
[[package]]
name = "rust-samp-codegen"
-version = "1.3.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=e5b5fc1#e5b5fc10cd41351e684c28b0a7cc62071812f3c7"
+version = "1.4.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=51ba519#51ba519157e04258ef49515958491b9ba734e2db"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.118",
+ "syn",
]
[[package]]
name = "rust-samp-sdk"
-version = "3.2.1"
-source = "git+https://github.com/NullSablex/rust-samp?rev=e5b5fc1#e5b5fc10cd41351e684c28b0a7cc62071812f3c7"
+version = "3.3.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=51ba519#51ba519157e04258ef49515958491b9ba734e2db"
dependencies = [
- "bitflags",
+ "bitflags 2.13.0",
]
[[package]]
@@ -197,7 +288,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.0",
+ "syn",
]
[[package]]
@@ -214,15 +305,10 @@ dependencies = [
]
[[package]]
-name = "syn"
-version = "2.0.118"
+name = "slice-pool2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
+checksum = "7a3d689654af89bdfeba29a914ab6ac0236d382eb3b764f7454dde052f2821f8"
[[package]]
name = "syn"
@@ -267,24 +353,67 @@ dependencies = [
"time-core",
]
+[[package]]
+name = "typenum"
+version = "1.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -294,6 +423,70 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
[[package]]
name = "zmij"
version = "1.0.21"
diff --git a/crates/dap-adapter/Cargo.toml b/crates/dap-adapter/Cargo.toml
index 4e5b1fb..77616c0 100644
--- a/crates/dap-adapter/Cargo.toml
+++ b/crates/dap-adapter/Cargo.toml
@@ -11,7 +11,7 @@ name = "dap-adapter"
path = "src/main.rs"
[dependencies]
-rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "e5b5fc1", default-features = false, features = ["debug"] }
+rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "51ba519", default-features = false, features = ["debug"] }
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
serde = { version = "1", features = ["derive"] }
diff --git a/crates/debug-plugin/Cargo.toml b/crates/debug-plugin/Cargo.toml
index b293b9b..620ea73 100644
--- a/crates/debug-plugin/Cargo.toml
+++ b/crates/debug-plugin/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
[dependencies]
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
-samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "e5b5fc1", features = ["debug"] }
+samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "51ba519", features = ["debug"] }
[package.metadata.samp]
uid = "0x0d9107bbd31c8d1b"
From fb86df3f0953ac5291a71935fdf15397dccd348c Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 03:37:45 -0300
Subject: [PATCH 03/33] feat(debugger): call stack multi-frame
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Caminha a cadeia de frames do AMX (FRM anterior + endereço de retorno,
parando no público de entrada) e expõe N frames ao editor. Cada frame traz
nome da função (lookup_function), linha e variáveis em escopo — reaproveitando
inspect::collect por frame.
- protocolo: Event::Paused passa a carregar frames: Vec; Command::
SetVariable ganha o índice do frame.
- plugin: novo módulo stack (walker puro e testável) + build_frames no on_pause;
PAUSE_CTX guarda o contexto de todos os frames para editar no frame certo.
- adaptador: stackTrace com N frames + source; scopes/variables/evaluate/
setVariable operam por frame (frameId/variablesReference).
Cobertura: 6 testes do walker + 2 do plumbing do adaptador. aarch64 ok.
---
README.md | 2 +-
crates/dap-adapter/src/plugin_client.rs | 61 +++++------
crates/dap-adapter/src/session.rs | 128 +++++++++++++++++-----
crates/debug-plugin/src/bridge.rs | 10 +-
crates/debug-plugin/src/hook.rs | 73 +++++++++----
crates/debug-plugin/src/lib.rs | 1 +
crates/debug-plugin/src/stack.rs | 138 ++++++++++++++++++++++++
crates/protocol/src/lib.rs | 54 +++++++---
8 files changed, 369 insertions(+), 98 deletions(-)
create mode 100644 crates/debug-plugin/src/stack.rs
diff --git a/README.md b/README.md
index 0e94f95..878c5cf 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ desenvolvimento.
| Editar variável | ✅ | Durante a pausa (`setVariable`). |
| Pausar em erro de runtime | ✅ | Divisão por zero e índice fora do limite; pausa na linha, antes do abort. SA-MP e open.mp. |
| Mensagens localizadas | ✅ | pt-BR, en, es, ro, ru (segue o idioma do editor). |
-| Call stack multi-frame | ⬜ | Hoje mostra um frame; caminhar a pilha está planejado. |
+| Call stack multi-frame | ✅ | Caminha a cadeia de frames (FRM→retorno); nome da função, linha e variáveis por frame. |
| Data breakpoints | ⬜ | Pausar quando uma variável muda — em avaliação. |
| Mais erros de runtime | ⬜ | STACKERR / MEMACCESS / HEAPLOW — em avaliação. |
diff --git a/crates/dap-adapter/src/plugin_client.rs b/crates/dap-adapter/src/plugin_client.rs
index e6b3cd9..ce59677 100644
--- a/crates/dap-adapter/src/plugin_client.rs
+++ b/crates/dap-adapter/src/plugin_client.rs
@@ -150,17 +150,14 @@ impl PluginClient {
match wire::from_line::(&line) {
Ok(Event::Paused {
reason,
- line,
- vars,
+ frames,
description,
}) => {
- store_vars(&vars);
- store_line(line);
+ store_frames(&frames);
let mut body = json!({
"reason": reason,
"threadId": 1,
"allThreadsStopped": true,
- "line": line,
});
// Runtime error: `description`/`text` show the cause in the
// editor's call-stack header (reason "exception").
@@ -204,41 +201,39 @@ impl PluginClient {
}
}
-/// Últimas variáveis recebidas num `Paused` — servidas ao `variables` do DAP.
-/// Global porque chegam pela thread leitora e são consultadas no loop principal.
-static LAST_VARS: Mutex> = Mutex::new(Vec::new());
+/// Pilha de chamadas da última pausa — servida ao `stackTrace`/`scopes`/
+/// `variables` do DAP. Global porque chega pela thread leitora e é consultada no
+/// loop principal. Índice 0 = topo (onde a VM parou).
+static LAST_FRAMES: Mutex> = Mutex::new(Vec::new());
-fn store_vars(vars: &[wire::Var]) {
- if let Ok(mut g) = LAST_VARS.lock() {
- *g = vars.to_vec();
+fn store_frames(frames: &[wire::Frame]) {
+ if let Ok(mut g) = LAST_FRAMES.lock() {
+ *g = frames.to_vec();
}
}
-/// Variáveis da última pausa (para o handler `variables` do DAP).
-pub fn last_vars() -> Vec {
- LAST_VARS.lock().map(|g| g.clone()).unwrap_or_default()
+/// Frames da última pausa (para o `stackTrace` do DAP).
+pub fn last_frames() -> Vec {
+ LAST_FRAMES.lock().map(|g| g.clone()).unwrap_or_default()
}
-/// Atualiza no cache o valor de uma variável editada via `setVariable`, para que
-/// o painel/watch reflitam o novo valor sem reler a VM (o plugin já a escreveu).
-pub fn update_var(name: &str, value: &str) {
- if let Ok(mut g) = LAST_VARS.lock()
- && let Some(v) = g.iter_mut().find(|v| v.name == name)
- {
- v.value = value.to_string();
- }
+/// Variáveis em escopo no frame dado (0 = topo) — para `variables`/`evaluate`.
+pub fn frame_vars(frame: usize) -> Vec {
+ LAST_FRAMES
+ .lock()
+ .ok()
+ .and_then(|g| g.get(frame).map(|f| f.vars.clone()))
+ .unwrap_or_default()
}
-/// Linha-fonte da última pausa (para o `stackTrace` do DAP).
-static LAST_LINE: Mutex
From 720bc0c2bd19cfd6c662f0622fc5567920dbdb10 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 20:28:51 -0300
Subject: [PATCH 27/33] docs(i18n): site em pt-BR e en-US com
mkdocs-static-i18n
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adiciona o plugin mkdocs-static-i18n (docs_structure: suffix), com pt-BR como
idioma padrão na raiz e en-US em /en-US/. As seis páginas foram traduzidas, e o
nav tem nav_translations; fallback_to_default cobre páginas futuras que ainda não
tenham versão traduzida.
O tema do locale en-US aponta para 'en': o Material só traz tabela de interface
para 'en', e 'en-US' quebra o build (TemplateNotFound em
partials/languages/en-US.html).
requirements.in/txt recompilados com hashes. Validado como no CI: venv limpo,
pip install --require-hashes e mkdocs build --strict.
---
README.md | 1 +
docs/architecture.en-US.md | 67 +++++++++++++++++++++++++
docs/features.en-US.md | 92 +++++++++++++++++++++++++++++++++++
docs/getting-started.en-US.md | 60 +++++++++++++++++++++++
docs/i18n.en-US.md | 86 ++++++++++++++++++++++++++++++++
docs/index.en-US.md | 20 ++++++++
docs/requirements.in | 2 +
docs/requirements.txt | 20 +++++---
docs/runtime-errors.en-US.md | 74 ++++++++++++++++++++++++++++
mkdocs.yml | 30 ++++++++++++
10 files changed, 446 insertions(+), 6 deletions(-)
create mode 100644 docs/architecture.en-US.md
create mode 100644 docs/features.en-US.md
create mode 100644 docs/getting-started.en-US.md
create mode 100644 docs/i18n.en-US.md
create mode 100644 docs/index.en-US.md
create mode 100644 docs/runtime-errors.en-US.md
diff --git a/README.md b/README.md
index ee4d9e6..fc0a328 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@
Documentação ·
+ English ·
Começando ·
Releases ·
Extensão PawnPro
diff --git a/docs/architecture.en-US.md b/docs/architecture.en-US.md
new file mode 100644
index 0000000..81ae2d9
--- /dev/null
+++ b/docs/architecture.en-US.md
@@ -0,0 +1,67 @@
+# Architecture
+
+The debugger is a Cargo workspace with three crates, plus the SDK as a
+dependency.
+
+```
+editor (VS Code, DAP)
+ │ Debug Adapter Protocol (stdio)
+ ▼
+dap-adapter ──launches──► SA-MP/open.mp server
+ │ own protocol (NDJSON / local socket) │ loads
+ └─────────────────────────────────────────────┤
+ ▼
+ debug-plugin (inside the VM)
+```
+
+## Crates
+
+| Crate | Kind | Role |
+|-------|------|------|
+| `protocol` | `lib` | Shared types for the plugin ↔ adapter IPC (commands and events as NDJSON over a local socket) and the [localized messages](i18n.md). |
+| `debug-plugin` | `cdylib` | Loaded by the server. Installs the debug hook, decides when to pause (breakpoint/step/data breakpoint/error), walks the stack, collects variables, reads and writes data memory, and blocks the VM until the editor says continue. |
+| `dap-adapter` | `bin` | Translates DAP ↔ the own protocol. Launches the server as a child process (which dies with it), relays breakpoints and events, and evaluates watch/console expressions. |
+
+## Shared SDK
+
+The parser for the `AMX_DBG` format (address ↔ line ↔ symbol ↔ function) and the
+VM primitives come from the [`rust-samp`](https://rust-samp.nullsablex.com/) SDK:
+
+- `samp::debug` / `samp_sdk::debug` — parser for the debug block.
+- `Amx::cip/frame/stack/heap/stp/pri/alt` — the VM registers.
+- `Amx::read_cell/write_cell` — data (inspecting and editing variables).
+- `Amx::read_code` / `Amx::opcode_table` — code (decoding opcodes; see
+ [Pausing on an error](runtime-errors.md)).
+
+Using the SDK as the single source avoids duplicating the parser between plugin
+and adapter (the adapter depends on `rust-samp-sdk` with `default-features =
+false, features = ["debug"]` — the pure logic only, no FFI).
+
+## The flow of a pause
+
+1. The VM calls the debug hook on every line (`.amx` compiled with `-d3`).
+2. The plugin decides whether to pause (breakpoint/condition/hit-count/step/data
+ breakpoint/runtime error).
+3. It collects the in-scope variables and the stack frames, and sends an event to
+ the adapter.
+4. It **blocks** the VM (the server freezes — expected in development) until the
+ editor says continue/step.
+
+## Direction of messages
+
+The protocol is asynchronous both ways: the adapter sends **commands**
+(breakpoints, step, continue, edit a variable) and the plugin sends **events**
+(pause, log, exit). Nothing waits for a reply — except one path:
+
+`Command::ReadMemory` ↔ `Event::MemoryData` is a **request/response** pair,
+correlated by a sequential `id`. The adapter registers the pending request, the
+socket reader thread hands the bytes to the caller waiting for them, and a
+**timeout** drops the pending entry if the plugin never answers — the session
+never gets stuck.
+
+## Compilation
+
+- **Plugin** → the server's architecture (SA-MP/open.mp are 32-bit →
+ `i686-unknown-linux-gnu`).
+- **Adapter** → the host's architecture (where the editor runs).
+- Edition **2024**, `resolver = "3"`.
diff --git a/docs/features.en-US.md b/docs/features.en-US.md
new file mode 100644
index 0000000..a04c795
--- /dev/null
+++ b/docs/features.en-US.md
@@ -0,0 +1,92 @@
+# Features
+
+| Feature | Status | Detail |
+|---------|:------:|--------|
+| Plain breakpoints | :material-check: | By line. |
+| Conditional breakpoints | :material-check: | `var OP value` (`==` `!=` `<` `>` `<=` `>=`); `int`/`Float:`/`bool:`/hex. |
+| Hit count | :material-check: | `N`, `==N`, `>=N`, `<=N`, `>N`, `=N`,
+`<=N`, `>N`, ` <= >=`.
+
+Anything that does not match returns "not evaluable" instead of an invented
+value.
+
+Editing supports both DAP routes: `setVariable` (typing a new value in the
+**Variables** panel) and `setExpression` (`arr[i] = 10` in the watch panel or the
+console). In `setExpression` the left-hand side is an **lvalue** — `name` or
+`name[expr]`; a whole array is not editable, only its elements.
+
+## Data breakpoints
+
+"Break on Value Change" in the **Variables** panel: the plugin keeps the last
+observed value and pauses when it changes. It covers:
+
+- **globals** — absolute address, the watch never expires;
+- **locals** — frame-relative address; the watch **expires** when the owning
+ frame returns (so it will not fire on another frame's leftovers in the same
+ slot);
+- **array elements** (`arr[3]`) — watched individually.
+
+If the address cannot be read, the plugin stays conservative: it does not invent
+a change.
+
+## Reading memory
+
+Every variable exposed to the editor carries a `memoryReference`, so the **hex
+view** opens from it and browses raw data memory (the region starting at the
+variable's or element's address, plus the requested `offset`). It is the only
+path in the protocol that does **request/response** with the plugin: the adapter
+correlates the request with its reply and gives up on a timeout instead of
+blocking the session.
+
+Reads honor the VM's 4-byte cell alignment and return only what could actually be
+read — at the end of the segment, fewer bytes than requested.
diff --git a/docs/getting-started.en-US.md b/docs/getting-started.en-US.md
new file mode 100644
index 0000000..19af2ee
--- /dev/null
+++ b/docs/getting-started.en-US.md
@@ -0,0 +1,60 @@
+# Getting started
+
+To debug you need two things: the **PawnPro** extension in your editor and the
+debugger **plugin** in your server. The extension handles everything when a
+session starts (recompiling, launching the server, connecting) — the only manual
+step is putting the plugin in the server, **once**.
+
+## 1. Download the plugin
+
+Grab the plugin binary from the [releases page][releases]:
+
+- **Linux:** `pawnpro_debug.so`
+- **Windows:** `pawnpro_debug.dll`
+
+[releases]: https://github.com/NullSablex/PawnPro-Debugger/releases
+
+## 2. Put it in the server
+
+Copy the file into the right folder of your server:
+
+- **SA-MP:** `plugins/pawnpro_debug.so`, and add `pawnpro_debug` to the `plugins`
+ line of `server.cfg`.
+- **open.mp:** `components/pawnpro_debug.so`.
+
+!!! warning "Do not rename the file"
+ The name must be **`pawnpro_debug`** (`.so` on Linux, `.dll` on Windows). The
+ extension looks for the plugin by exactly that name; under any other name,
+ debugging will not start.
+
+!!! note "The extension only checks"
+ On startup the extension verifies that the plugin is present and warns if it
+ is missing — it does **not** install anything into your server.
+
+## 3. Debug
+
+Open your gamemode's `.pwn` in the editor (with the PawnPro extension) and press
+**F5**. Set breakpoints in the gutter and debug as usual. The extension
+recompiles the gamemode with `-d3` (the debug block, carrying lines and symbols)
+before launching the server — without it there are no breakpoints and no
+inspection.
+
+While execution is paused the server is **frozen**: expected on a local
+development server.
+
+### Example `launch.json`
+
+```json
+{
+ "type": "pawn",
+ "request": "launch",
+ "name": "Debug gamemode",
+ "program": "${workspaceFolder}/gamemodes/mygm.amx"
+}
+```
+
+## Next step
+
+See [Features](features.md) for what you can do while paused — call stack,
+inspecting and editing variables, data breakpoints, the memory hex view and
+automatic pausing on [runtime errors](runtime-errors.md).
diff --git a/docs/i18n.en-US.md b/docs/i18n.en-US.md
new file mode 100644
index 0000000..d787b24
--- /dev/null
+++ b/docs/i18n.en-US.md
@@ -0,0 +1,86 @@
+# Localization (i18n)
+
+The debugger's user-facing messages (runtime errors and the adapter's replies to
+the editor) live in **one place**, shared by the plugin and the adapter:
+
+| Where | What |
+|-------|------|
+| `crates/protocol/src/messages/mod.rs` | `Locale`, `MsgKey` (the keys) and the routing (`msg`/`format`) |
+| `crates/protocol/src/messages/langs/.rs` | one `match` table per language: `get(MsgKey) -> &'static str` |
+
+The texts are **templates** with positional `{}` markers, filled in by
+`messages::format(locale, key, &[args])`. The language comes from the editor (the
+`locale` argument of `initialize`, in the adapter; the `PAWNPRO_DBG_LOCALE` env
+var, in the plugin) and is resolved by prefix (`Locale::from_tag`); an unknown one
+falls back to **English**.
+
+There are **11 message keys** (5 runtime errors + 6 from the adapter). Each
+language's `match` on `MsgKey` is **exhaustive** — a missing key does not compile.
+That is why a language marked ✅ necessarily covers all 11.
+
+**Legend:** ✅ complete · 🟡 partial · ⬜ missing.
+
+> Coverage goal: at least **50 languages**. The order prioritizes historical
+> relevance in the SA-MP / open.mp community. Today there are **5** implemented.
+
+## Status
+
+| # | Language | Code | Status |
+|---|----------|------|:------:|
+| 1 | English (source) | `en` | ✅ |
+| 2 | Portuguese (BR) | `pt-BR` | ✅ |
+| 3 | Spanish | `es` | ✅ |
+| 4 | Russian | `ru` | ✅ |
+| 5 | Romanian | `ro` | ✅ |
+| 6 | German | `de` | ⬜ |
+| 7 | French | `fr` | ⬜ |
+| 8 | Italian | `it` | ⬜ |
+| 9 | Polish | `pl` | ⬜ |
+| 10 | Turkish | `tr` | ⬜ |
+| 11 | Dutch | `nl` | ⬜ |
+| 12 | Ukrainian | `uk` | ⬜ |
+| 13 | Chinese (Simpl.) | `zh-CN` | ⬜ |
+| 14 | Chinese (Trad.) | `zh-TW` | ⬜ |
+| 15 | Indonesian | `id` | ⬜ |
+| 16 | Arabic | `ar` | ⬜ |
+| 17 | Portuguese (PT) | `pt-PT` | ⬜ |
+| 18 | Hungarian | `hu` | ⬜ |
+| 19 | Czech | `cs` | ⬜ |
+| 20 | Serbian | `sr` | ⬜ |
+| 21 | Bulgarian | `bg` | ⬜ |
+| 22 | Greek | `el` | ⬜ |
+| 23 | Swedish | `sv` | ⬜ |
+| 24 | Lithuanian | `lt` | ⬜ |
+| 25 | Croatian | `hr` | ⬜ |
+| 26 | Slovak | `sk` | ⬜ |
+| 27 | Hebrew | `he` | ⬜ |
+| 28 | Thai | `th` | ⬜ |
+| 29 | Vietnamese | `vi` | ⬜ |
+| 30 | Persian | `fa` | ⬜ |
+| 31 | Japanese | `ja` | ⬜ |
+| 32 | Korean | `ko` | ⬜ |
+| 33 | Finnish | `fi` | ⬜ |
+| 34 | Danish | `da` | ⬜ |
+| 35 | Norwegian | `nb` | ⬜ |
+| 36 | Hindi | `hi` | ⬜ |
+| 37 | Bengali | `bn` | ⬜ |
+| 38 | Filipino | `fil` | ⬜ |
+| 39 | Malay | `ms` | ⬜ |
+| 40 | Latvian | `lv` | ⬜ |
+| 41 | Estonian | `et` | ⬜ |
+| 42 | Slovenian | `sl` | ⬜ |
+| 43 | Belarusian | `be` | ⬜ |
+| 44 | Macedonian | `mk` | ⬜ |
+| 45 | Albanian | `sq` | ⬜ |
+| 46 | Bosnian | `bs` | ⬜ |
+| 47 | Catalan | `ca` | ⬜ |
+| 48 | Azerbaijani | `az` | ⬜ |
+| 49 | Kazakh | `kk` | ⬜ |
+| 50 | Georgian | `ka` | ⬜ |
+
+## Adding a language
+
+1. Create `crates/protocol/src/messages/langs/.rs` with `pub fn get(key: MsgKey) -> &'static str` covering **every** `MsgKey` (keeping the `{}` markers in the same logical position).
+2. Register the module in `langs/mod.rs` (`pub mod ;`).
+3. Add the variant to the `Locale` enum, to `Locale::from_tag` (by prefix) and to the `match` in `messages::msg`.
+4. Mark it ✅ in this table.
diff --git a/docs/index.en-US.md b/docs/index.en-US.md
new file mode 100644
index 0000000..6911232
--- /dev/null
+++ b/docs/index.en-US.md
@@ -0,0 +1,20 @@
+# PawnPro Debugger
+
+Visual debugger (DAP) for the **Pawn** language (SA-MP / open.mp), integrated
+with the [PawnPro](https://github.com/NullSablex/PawnPro) extension, for a local
+development server.
+
+It lets you debug Pawn code straight from the editor: breakpoints (plain,
+conditional, hit-count and function), logpoints, stepping, a multi-frame call
+stack, variable inspection and editing, data breakpoints, raw memory reads and
+**pausing on the line of a runtime error** before the VM aborts — on both SA-MP
+and open.mp.
+
+## Where to start
+
+- **[Getting started](getting-started.md)** — download the plugin, drop it into
+ your server and start a debugging session.
+- **[Features](features.md)** — what the debugger does.
+
+For the internals, see **[Architecture](architecture.md)**, **[How pausing on an
+error works](runtime-errors.md)** and **[Localization](i18n.md)**.
diff --git a/docs/requirements.in b/docs/requirements.in
index 5c812f1..1cde508 100644
--- a/docs/requirements.in
+++ b/docs/requirements.in
@@ -3,3 +3,5 @@
# with:
# pip-compile --generate-hashes --output-file=docs/requirements.txt docs/requirements.in
mkdocs-material==9.7.7
+# Multi-language docs (pt-BR default, en-US); see mkdocs.yml `plugins.i18n`.
+mkdocs-static-i18n==1.3.1
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 612805f..b5e3ba4 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,7 +1,9 @@
-# Build dependencies for the MkDocs Material documentation site, pinned by hash.
-# Regenerate when bumping:
-# pip-compile --generate-hashes --output-file=docs/requirements.txt docs/requirements.in
-# Install in CI: pip install --require-hashes -r docs/requirements.txt
+#
+# This file is autogenerated by pip-compile with Python 3.13
+# by the following command:
+#
+# pip-compile --generate-hashes --no-index --output-file=docs/requirements.txt docs/requirements.in
+#
babel==2.18.0 \
--hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \
--hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35
@@ -280,7 +282,9 @@ mergedeep==1.3.4 \
mkdocs==1.6.1 \
--hash=sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2 \
--hash=sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e
- # via mkdocs-material
+ # via
+ # mkdocs-material
+ # mkdocs-static-i18n
mkdocs-get-deps==0.2.2 \
--hash=sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1 \
--hash=sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650
@@ -288,11 +292,15 @@ mkdocs-get-deps==0.2.2 \
mkdocs-material==9.7.7 \
--hash=sha256:8ea9bb1737a5b524a5f9dcf2e1b4ebda8274ae3008aa7845720a97083bef708f \
--hash=sha256:c0649c065b1b0512d60aad8c10f947f8e455284475239b364b610f2deb4d0855
- # via -r requirements.in
+ # via -r docs/requirements.in
mkdocs-material-extensions==1.3.1 \
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
--hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31
# via mkdocs-material
+mkdocs-static-i18n==1.3.1 \
+ --hash=sha256:4036e24795a150c9c4d4b001ed24a43aec01335f76188dbe5a5d8fb4a27eba65 \
+ --hash=sha256:a6125ea7db6cc1a900d76a967f262535af09831160a93c56d7f0d522a79b5faf
+ # via -r docs/requirements.in
packaging==26.2 \
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
diff --git a/docs/runtime-errors.en-US.md b/docs/runtime-errors.en-US.md
new file mode 100644
index 0000000..00f24a9
--- /dev/null
+++ b/docs/runtime-errors.en-US.md
@@ -0,0 +1,74 @@
+# How pausing on an error works
+
+Debuggers for other languages pause on the exact line of an error (division by
+zero, index out of bounds). On the AMX VM that is not trivial, and it is the
+debugger's most technical feature. This page explains how it works.
+
+## The problem
+
+The VM aborts a runtime error through the `ABORT` macro, which **returns
+immediately** from `amx_Exec` without calling the debug hook and without
+preserving the exact `cip` of the instruction. In other words: when the error
+happens the hook is never called — there is no way to "catch" the error
+afterwards.
+
+The way out is to **predict**: the hook is called on every line; in it, we look at
+the upcoming instruction and check whether it will fail with the current
+registers, pausing **before** the VM aborts.
+
+## `OP_BREAK` is per line, not per instruction
+
+The compiler emits an `OP_BREAK` at the **start** of each source line — not before
+every instruction. The dangerous instruction (a division, an `OP_BOUNDS`) usually
+sits in the **middle** of the line, after several `load`/`push`/`pop` that change
+the registers.
+
+So looking at the opcode right after the break is not enough: the debugger
+**scans the line** (from the break to the next break), simulating the `pri`/`alt`
+registers from the real state and reading the data segment (`Amx::read_cell`) for
+operands that come from variables. When it reaches a risky instruction, it checks
+it with the correct values.
+
+## Relocation (computed goto)
+
+On servers built with computed goto (GCC/Clang — the SA-MP and open.mp builds),
+the loader **rewrites every opcode** in the code segment into the **address** of
+the label that handles it. So `Amx::read_code(cip)` returns a pointer, not the
+opcode number.
+
+To recover the opcode, the debugger inverts the VM's dispatch table, obtained at
+runtime through `Amx::opcode_table` (the same mechanism the loader uses). That
+makes detection **portable**: it works on SA-MP and open.mp without depending on
+either one's source, because both use the same AMX VM.
+
+## Detected errors
+
+| Error | Opcode / check | Condition (faithful to `amx.c`) |
+|-------|----------------|----------------------------------|
+| Division by zero | `OP_SDIV` / `OP_UDIV` | the divisor (`alt`) is zero |
+| Division by zero | `OP_SDIV_ALT` / `OP_UDIV_ALT` | the divisor (`pri`) is zero |
+| Index out of bounds | `OP_BOUNDS` | `(unsigned) pri > limit` |
+| Stack/heap collision (`STACKERR`) | `OP_STACK` / `OP_HEAP` / `OP_PROC` / `OP_CALL` / `OP_CALL_PRI` (`CHKMARGIN`) | `hea + STKMARGIN > stk` (on `CALL`, anticipating the callee's `PROC`) |
+| Heap underflow (`HEAPLOW`) | `OP_HEAP` (`CHKHEAP`) | `hea < hlw` |
+| Invalid memory access (`MEMACCESS`) | `OP_LOAD_I` / `OP_LODB_I` / `OP_STOR_I` / `OP_STRB_I` / `OP_LIDX` / `OP_LIDX_B` (`VERIFYADDRESS`) | address within `[hea, stk)` or `>= stp` |
+
+On detection the debugger pauses with `reason: "exception"` and the message in the
+editor's language, showing the line and the variables — just like a regular
+breakpoint.
+
+!!! note "Conservative by design"
+ The `STACKERR`/`HEAPLOW`/`MEMACCESS` checks track `stk`/`hea` along the line
+ and only fire while that tracking is exact; any branch or unmodeled opcode
+ (a jump, a `sysreq`, arithmetic) turns them off — **never** a false positive.
+ This is not "break on any exception".
+
+## SDK primitives used
+
+Every read from the VM comes from the
+[`rust-samp`](https://rust-samp.nullsablex.com/) SDK, with no hand-written FFI in
+the debugger:
+
+- `Amx::pri()` / `Amx::alt()` — the accumulator registers.
+- `Amx::read_code(offset)` — reads the code segment (the instruction).
+- `Amx::opcode_table(count)` — the dispatch table, to decode under relocation.
+- `Amx::read_cell(addr)` — reads a variable's real value during the simulation.
diff --git a/mkdocs.yml b/mkdocs.yml
index 4eaa81f..6046d79 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -48,6 +48,36 @@ theme:
edit: material/pencil
view: material/eye
+# `search` fica listado explicitamente: declarar `plugins` substitui o padrão do
+# MkDocs, que traz a busca embutida.
+plugins:
+ - search
+ - i18n:
+ docs_structure: suffix
+ fallback_to_default: true
+ languages:
+ - locale: pt-BR
+ name: Português (BR)
+ default: true
+ build: true
+ - locale: en-US
+ name: English (US)
+ build: true
+ site_description: Visual debugger (DAP) for Pawn (SA-MP / open.mp)
+ # O tema fica em `en`: o Material traduz a interface por idioma e não
+ # tem uma tabela para `en-US` (só `en`), o que quebraria o build.
+ theme:
+ language: en
+ nav_translations:
+ Início: Home
+ Uso: Usage
+ Começando: Getting started
+ Recursos: Features
+ Interno: Internals
+ Arquitetura: Architecture
+ Como funciona a pausa no erro: How pausing on an error works
+ Localização (i18n): Localization (i18n)
+
markdown_extensions:
- admonition
- attr_list
From 34d7ec7ddbf595f8a34374cede2e1753d935aac7 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 20:43:54 -0300
Subject: [PATCH 28/33] docs: registrar docs, i18n do site e Scorecard no
changelog
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A seção Não lançado parou nos recursos; faltavam o README com badges, o site
bilíngue, a padronização dos comentários, as duas correções de documentação e o
workflow do OpenSSF Scorecard.
---
CHANGELOG.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c167bfb..746692f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,7 +25,16 @@ Podem existir falhas ou itens não declarados, causados por falha humana ou por
### Alterado
- **Mensagens localizadas centralizadas** em `crates/protocol/src/messages` — `Locale`, as 11 `MsgKey` e uma tabela por idioma (pt-BR, en, es, ro, ru), compartilhadas pelo plugin e pelo adaptador. O `match` por chave é exaustivo: idioma incompleto não compila.
- **Primeiro canal request/response do protocolo** — `Command::ReadMemory` ↔ `Event::MemoryData`, correlacionados por `id` e com timeout, sem prender a sessão se o plugin não responder.
-- Documentação reescrita: `README.md`, `docs/features.md`, `docs/architecture.md`, `docs/index.md` e a nova página de [localização](docs/i18n.md), com a meta de 50 idiomas.
+- **Documentação reescrita** — `README.md` (com badges de CI, CodeQL, docs, OpenSSF Scorecard, release, downloads, stars e licença), `docs/features.md`, `docs/architecture.md`, `docs/index.md`, `docs/getting-started.md` e a nova página de [localização](docs/i18n.md), com a meta de 50 idiomas.
+- **Site da documentação bilíngue** — `mkdocs-static-i18n` com **pt-BR** na raiz e **en-US** em `/en-US/`, as seis páginas traduzidas e o nav por `nav_translations`. O tema do locale `en-US` aponta para `en`, porque o Material só traz tabela de interface para `en`.
+- **Comentários do código padronizados em português** — `hook.rs` estava inteiramente em inglês (mais pontos isolados em `plugin_client.rs`, `inspect.rs` e `langs/en.rs`); na passagem, cortado o que apenas repetia a assinatura.
+
+### Corrigido
+- Tabela de erros em `docs/runtime-errors.md`: faltavam `OP_CALL_PRI` no STACKERR e `OP_LODB_I`/`OP_STRB_I`/`OP_LIDX_B` no MEMACCESS, todos já checados no plugin.
+- Doc de `resolve_data_watches`, que ainda dizia que arrays não eram observáveis — elementos de array passaram a ser.
+
+### Infraestrutura
+- **OpenSSF Scorecard** (`.github/workflows/scorecard.yml`) — análise em push no `master`, semanal e manual, com actions pinadas por SHA e permissões mínimas. Publica na API pública do OpenSSF (o que sustenta o badge) e envia o SARIF ao code scanning.
## [0.1.0] - 04/07/2026
From 19f1430a8bb398b366454ea8b8440756b69c7844 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 20:55:37 -0300
Subject: [PATCH 29/33] chore(release): 0.2.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fecha o ciclo desde a v0.1.0: call stack multi-frame, data breakpoints,
breakpoints de função, três novos erros de runtime, inspeção de arrays/strings,
setExpression, readMemory, expressões no watch, autocomplete e a documentação em
inglês.
Sobe a versão do workspace e o marcador embutido no plugin (a extensão casa
apenas o prefixo PAWNPRO_DEBUG_MARKER, então a versão dentro dele é livre; o
tamanho segue 26 bytes).
O changelog passa a cobrir todas as mudanças desde o último release, incluindo os
re-pins do SDK e a infraestrutura do repositório.
---
CHANGELOG.md | 43 +++++++++++++++++++---------------
Cargo.lock | 6 ++---
Cargo.toml | 2 +-
crates/debug-plugin/src/lib.rs | 2 +-
4 files changed, 29 insertions(+), 24 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 746692f..0de4995 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,33 +8,38 @@ Podem existir falhas ou itens não declarados, causados por falha humana ou por
---
-## [Não lançado]
+## [0.2.0] - 31/08/2026
+
+Segundo pré-lançamento. Amplia o conjunto DAP suportado: a depuração deixa de ser
+"breakpoint e inspeção" e passa a cobrir call stack, data breakpoints, edição por
+expressão, leitura de memória e mais três classes de erro de runtime.
### Adicionado
-- **Call stack multi-frame** — o plugin caminha a cadeia de frames do AMX (FRM → endereço de retorno) e entrega nome da função, linha e variáveis de cada frame; o editor navega entre eles e a inspeção segue o frame selecionado.
-- **Breakpoints de função** — parar ao entrar numa função pelo nome. O adaptador resolve o nome no bloco `AMX_DBG` e envia ao plugin a união dos breakpoints de linha e de função; nome que não resolve volta como não verificado.
-- **Data breakpoints** — pausar quando um valor muda, em globais, locais e **elementos de array** (`arr[3]`). Watches de locais expiram quando o frame dono retorna, para não disparar com o lixo de outro frame no mesmo slot; endereço ilegível não gera disparo.
-- **Inspeção rica de arrays e strings** — arrays são expansíveis (cada elemento vira um filho) e arrays de char são resumidos como **string** quando o conteúdo parece texto terminado em zero.
-- **`setExpression`** — editar um lvalue (`x = 1`, `arr[i] = 10`) direto no watch ou no console, com o índice podendo ser uma subexpressão. O array inteiro não é editável, só os elementos.
-- **`readMemory`** — hex view da memória de dados crua a partir de qualquer variável: cada variável passa a expor um `memoryReference`, e o adaptador devolve os bytes em base64 (encoder próprio, sem dependência nova).
-- **Autocomplete** (`completions`) — sugestão de variáveis em escopo no watch e no console.
+- **Call stack multi-frame** — caminha a cadeia de frames do AMX (FRM → endereço de retorno) e entrega nome da função, linha e variáveis de cada frame. O editor navega entre os frames e a inspeção segue o frame selecionado.
+- **Data breakpoints** — pausar quando um valor muda, em globais, locais e **elementos de array** (`arr[3]`). Watches de locais expiram quando o frame dono retorna, para não dispararem com o conteúdo de outro frame no mesmo slot.
+- **Breakpoints de função** — parar ao entrar numa função pelo nome; um nome que não resolve volta ao editor como não verificado, sem quebrar a sessão.
+- **Três novos erros de runtime** — `STACKERR` (colisão pilha/heap), `HEAPLOW` (underflow de heap) e `MEMACCESS` (acesso inválido à memória), somando-se à divisão por zero e ao índice fora do limite. A simulação é fiel ao `amx.c` e conservadora: o rastreio dos registradores perde a confiança ao primeiro opcode não modelado, então não há falso-positivo.
- **Filtro de exceção** — o editor liga e desliga a pausa em erros de runtime pelo painel de breakpoints.
-- **Mais erros de runtime** — `STACKERR` (colisão pilha/heap), `HEAPLOW` (underflow de heap) e `MEMACCESS` (acesso inválido à memória), com simulação fiel ao `amx.c` e conservadora: o rastreio de `pri`/`alt`/`stk`/`hea` perde a confiança ao primeiro opcode não modelado, então nunca há falso-positivo.
-- **Avaliador de expressões** para watch/hover — um operador de topo com `+ - * / %` (semântica de truncamento do Pawn) e `== != < > <= >=`, sobre literais, variáveis e `arr[i]`.
+- **Inspeção de arrays e strings** — arrays são expansíveis (cada elemento vira um filho) e arrays de char são mostrados como **string** quando o conteúdo é texto terminado em zero.
+- **Edição por expressão** (`setExpression`) — `x = 1` ou `arr[i] = 10` direto no watch ou no console, com o índice podendo ser uma subexpressão. O array inteiro não é editável, só os elementos.
+- **Leitura de memória** (`readMemory`) — hex view da memória de dados crua a partir de qualquer variável, que agora expõe um `memoryReference`.
+- **Expressões no watch e no hover** — um operador de topo com `+ - * / %` (seguindo o truncamento do Pawn) e `== != < > <= >=`, sobre literais, variáveis e `arr[i]`.
+- **Autocomplete** — sugestão de variáveis em escopo no watch e no console.
+- **Documentação em inglês** — o site passa a ter **pt-BR** na raiz e **en-US** em `/en-US/`, com todas as páginas traduzidas e o menu localizado.
### Alterado
-- **Mensagens localizadas centralizadas** em `crates/protocol/src/messages` — `Locale`, as 11 `MsgKey` e uma tabela por idioma (pt-BR, en, es, ro, ru), compartilhadas pelo plugin e pelo adaptador. O `match` por chave é exaustivo: idioma incompleto não compila.
-- **Primeiro canal request/response do protocolo** — `Command::ReadMemory` ↔ `Event::MemoryData`, correlacionados por `id` e com timeout, sem prender a sessão se o plugin não responder.
-- **Documentação reescrita** — `README.md` (com badges de CI, CodeQL, docs, OpenSSF Scorecard, release, downloads, stars e licença), `docs/features.md`, `docs/architecture.md`, `docs/index.md`, `docs/getting-started.md` e a nova página de [localização](docs/i18n.md), com a meta de 50 idiomas.
-- **Site da documentação bilíngue** — `mkdocs-static-i18n` com **pt-BR** na raiz e **en-US** em `/en-US/`, as seis páginas traduzidas e o nav por `nav_translations`. O tema do locale `en-US` aponta para `en`, porque o Material só traz tabela de interface para `en`.
-- **Comentários do código padronizados em português** — `hook.rs` estava inteiramente em inglês (mais pontos isolados em `plugin_client.rs`, `inspect.rs` e `langs/en.rs`); na passagem, cortado o que apenas repetia a assinatura.
+- **Mensagens do adaptador agora são localizadas** — antes só os erros de runtime seguiam o idioma do editor. As mensagens dos dois lados foram unificadas em `crates/protocol/src/messages`, com 11 chaves em pt-BR, en, es, ro e ru; a tabela de cada idioma é exaustiva, então um idioma incompleto não compila.
+- **O protocolo ganhou um canal request/response** — `ReadMemory` ↔ `MemoryData`, correlacionados por `id` e com timeout, para a sessão não ficar presa se o plugin não responder. O restante do protocolo continua assíncrono nos dois sentidos.
+- **README e documentação reescritos** — recursos, arquitetura e a página de localização atualizados para o que esta versão entrega.
-### Corrigido
-- Tabela de erros em `docs/runtime-errors.md`: faltavam `OP_CALL_PRI` no STACKERR e `OP_LODB_I`/`OP_STRB_I`/`OP_LIDX_B` no MEMACCESS, todos já checados no plugin.
-- Doc de `resolve_data_watches`, que ainda dizia que arrays não eram observáveis — elementos de array passaram a ser.
+### Dependências
+- **SDK `rust-samp`** atualizado ao longo do ciclo, acompanhando o que cada versão liberou: `v3.3.1` (correção), `v3.4.0`, o acessor `Amx::hlw` (exigido pelo HEAPLOW) e `AmxDbg::function_address` (exigido pelos breakpoints de função).
+- Atualizações de `serde` (1.0.229), `serde_json` (1.0.151), das GitHub Actions e das dependências da documentação.
### Infraestrutura
-- **OpenSSF Scorecard** (`.github/workflows/scorecard.yml`) — análise em push no `master`, semanal e manual, com actions pinadas por SHA e permissões mínimas. Publica na API pública do OpenSSF (o que sustenta o badge) e envia o SARIF ao code scanning.
+- **CodeQL** migrado para advanced setup, garantindo as duas análises (`actions`, `rust`) em todo pull request, com `CODEOWNERS`.
+- **OpenSSF Scorecard** — análise semanal e em push no `master`, publicando o resultado na API pública do OpenSSF e o SARIF no code scanning.
+- **Dependabot** para `github-actions`, `cargo` e `pip`, agrupado em um PR por ecossistema.
## [0.1.0] - 04/07/2026
diff --git a/Cargo.lock b/Cargo.lock
index df62c68..459d832 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22,7 +22,7 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "dap-adapter"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"interprocess",
"pawnpro-dbg-protocol",
@@ -33,7 +33,7 @@ dependencies = [
[[package]]
name = "debug-plugin"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"interprocess",
"pawnpro-dbg-protocol",
@@ -165,7 +165,7 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pawnpro-dbg-protocol"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"interprocess",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index a1815cc..fbed91b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,7 @@ members = [
]
[workspace.package]
-version = "0.1.0"
+version = "0.2.0"
edition = "2024"
license = "AGPL-3.0-or-later"
repository = "https://github.com/NullSablex/PawnPro-Debugger"
diff --git a/crates/debug-plugin/src/lib.rs b/crates/debug-plugin/src/lib.rs
index 1f8d3e2..56a5fb2 100644
--- a/crates/debug-plugin/src/lib.rs
+++ b/crates/debug-plugin/src/lib.rs
@@ -40,7 +40,7 @@ static FIRST_AMX_SEEN: AtomicBool = AtomicBool::new(false);
/// (`src/core/server.ts`). NÃO renomear o valor — é contrato com a extensão.
#[used]
#[unsafe(no_mangle)]
-pub static PAWNPRO_DEBUG_MARKER: [u8; 26] = *b"PAWNPRO_DEBUG_MARKER:0.1.0";
+pub static PAWNPRO_DEBUG_MARKER: [u8; 26] = *b"PAWNPRO_DEBUG_MARKER:0.2.0";
/// Mantém o marcador vivo até o link final. `#[used]` sozinho não basta para um
/// cdylib: o linker ainda pode descartar o dado por não ser referenciado nem
From 2f38f57710c2ff092657d88dae5fc3d999522858 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Mon, 31 Aug 2026 21:07:57 -0300
Subject: [PATCH 30/33] docs: mover a data do release 0.2.0 para 01/09/2026
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0de4995..5255142 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,7 @@ Podem existir falhas ou itens não declarados, causados por falha humana ou por
---
-## [0.2.0] - 31/08/2026
+## [0.2.0] - 01/09/2026
Segundo pré-lançamento. Amplia o conjunto DAP suportado: a depuração deixa de ser
"breakpoint e inspeção" e passa a cobrir call stack, data breakpoints, edição por
From f041dde9e2394eebcd73f776dabaac6afa0ec163 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Tue, 1 Sep 2026 00:41:17 -0300
Subject: [PATCH 31/33] refactor: usar as primitivas de VM do SDK
(rust-samp-sdk 3.4.0)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A numeração de opcodes, o tamanho das instruções, o OpcodeMap, a caminhada da
pilha e a leitura de faixa de memória eram fatos genéricos da VM AMX que viviam
no plugin. Passaram para o SDK e voltam como API:
- samp::debug::opcode substitui as 44 constantes locais, a tabela OP_PARAMS e
STK_MARGIN; operand_cells troca o índice cru por uma consulta checada.
- samp::debug::stack substitui crates/debug-plugin/src/stack.rs, removido com
seus 6 testes (reescritos no SDK).
- Amx::read_bytes substitui o alinhamento e o fatiamento manuais do readMemory.
- Amx::data_only substitui Amx::new(ptr, 0) nos três pontos da pausa.
- Amx::opcode_map substitui OpcodeMap::new(amx.opcode_table(OP_NUM_OPCODES)).
O plugin perde ~250 linhas sem mudança de comportamento: 88 testes (os 6 que
saíram vivem agora no SDK), clippy pedantic e fmt verdes.
---
CHANGELOG.md | 1 +
Cargo.lock | 10 +-
crates/dap-adapter/Cargo.toml | 2 +-
crates/debug-plugin/Cargo.toml | 2 +-
crates/debug-plugin/src/hook.rs | 36 ++----
crates/debug-plugin/src/lib.rs | 1 -
crates/debug-plugin/src/runtime_error.rs | 122 +++-----------------
crates/debug-plugin/src/stack.rs | 138 -----------------------
docs/architecture.en-US.md | 12 +-
docs/architecture.md | 12 +-
10 files changed, 49 insertions(+), 287 deletions(-)
delete mode 100644 crates/debug-plugin/src/stack.rs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5255142..be09646 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,7 @@ expressão, leitura de memória e mais três classes de erro de runtime.
### Dependências
- **SDK `rust-samp`** atualizado ao longo do ciclo, acompanhando o que cada versão liberou: `v3.3.1` (correção), `v3.4.0`, o acessor `Amx::hlw` (exigido pelo HEAPLOW) e `AmxDbg::function_address` (exigido pelos breakpoints de função).
+- **Lógica genérica de VM devolvida ao SDK** (`rust-samp-sdk` 3.4.0) — a numeração dos opcodes, o tamanho das instruções, o decodificador da relocação por computed-goto (`OpcodeMap`), a caminhada da cadeia de frames e a leitura de faixas de memória eram fatos da VM AMX que viviam aqui. Agora vêm de `samp::debug::opcode`, `samp::debug::stack` e `Amx::read_bytes`/`call_stack`/`data_only`, e o plugin ficou ~250 linhas menor sem perder comportamento.
- Atualizações de `serde` (1.0.229), `serde_json` (1.0.151), das GitHub Actions e das dependências da documentação.
### Infraestrutura
diff --git a/Cargo.lock b/Cargo.lock
index 459d832..4afac02 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -232,8 +232,8 @@ dependencies = [
[[package]]
name = "rust-samp"
-version = "3.3.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=900ebd0#900ebd0f73ec5351a17428d1ea77032413383b1a"
+version = "3.4.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=9524c09#9524c09808576b42d4f4df36d2b9ffa6078075d4"
dependencies = [
"fern",
"log",
@@ -246,7 +246,7 @@ dependencies = [
[[package]]
name = "rust-samp-codegen"
version = "1.4.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=900ebd0#900ebd0f73ec5351a17428d1ea77032413383b1a"
+source = "git+https://github.com/NullSablex/rust-samp?rev=9524c09#9524c09808576b42d4f4df36d2b9ffa6078075d4"
dependencies = [
"proc-macro2",
"quote",
@@ -255,8 +255,8 @@ dependencies = [
[[package]]
name = "rust-samp-sdk"
-version = "3.3.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=900ebd0#900ebd0f73ec5351a17428d1ea77032413383b1a"
+version = "3.4.0"
+source = "git+https://github.com/NullSablex/rust-samp?rev=9524c09#9524c09808576b42d4f4df36d2b9ffa6078075d4"
dependencies = [
"bitflags 2.13.0",
]
diff --git a/crates/dap-adapter/Cargo.toml b/crates/dap-adapter/Cargo.toml
index cd617a6..8155b16 100644
--- a/crates/dap-adapter/Cargo.toml
+++ b/crates/dap-adapter/Cargo.toml
@@ -11,7 +11,7 @@ name = "dap-adapter"
path = "src/main.rs"
[dependencies]
-rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "900ebd0", default-features = false, features = ["debug"] }
+rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "9524c09", default-features = false, features = ["debug"] }
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
serde = { version = "1", features = ["derive"] }
diff --git a/crates/debug-plugin/Cargo.toml b/crates/debug-plugin/Cargo.toml
index 03a93ae..efc5761 100644
--- a/crates/debug-plugin/Cargo.toml
+++ b/crates/debug-plugin/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
[dependencies]
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
-samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "900ebd0", features = ["debug"] }
+samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "9524c09", features = ["debug"] }
[package.metadata.samp]
uid = "0x0d9107bbd31c8d1b"
diff --git a/crates/debug-plugin/src/hook.rs b/crates/debug-plugin/src/hook.rs
index e9e2295..5869f9b 100644
--- a/crates/debug-plugin/src/hook.rs
+++ b/crates/debug-plugin/src/hook.rs
@@ -20,10 +20,11 @@ use crate::control::{
};
use crate::gate::Resume;
use crate::inspect::{self, CellReader};
-use crate::runtime_error::{self, Locale, OP_NUM_OPCODES, OpcodeMap};
-use crate::stack;
+use crate::runtime_error::{self, Locale};
use pawnpro_dbg_protocol::{Breakpoint, Event, Frame};
+use samp::debug::OpcodeMap;
use samp::debug::VClass;
+use samp::debug::stack;
/// Tamanho (bytes) de uma instrução AMX. No hook, o `cip` aponta para a célula
/// SEGUINTE ao `OP_BREAK`; voltamos isto para chegar ao endereço da linha.
@@ -255,9 +256,8 @@ pub fn load_debug(dbg: AmxDbg) {
/// Monta o mapa de opcodes desta VM (inverso de `amx_opcodelist` numa imagem
/// relocada), para a detecção de erro de runtime. Uma vez por VM, no `on_amx_load`.
pub fn load_opcode_map(amx: &Amx) {
- let map = OpcodeMap::new(amx.opcode_table(OP_NUM_OPCODES));
if let Ok(mut guard) = OPCODE_MAP.lock() {
- *guard = Some(map);
+ *guard = Some(amx.opcode_map());
}
}
@@ -336,7 +336,7 @@ fn read_memory_inner(
count: usize,
) -> Option> {
let (amx_usize, frames) = PAUSE_CTX.lock().ok().and_then(|g| g.clone())?;
- let amx = Amx::new(amx_usize as *mut samp::raw::types::AMX, 0);
+ let amx = Amx::data_only(amx_usize as *mut samp::raw::types::AMX);
let (cip, frm) = *frames.get(frame)?;
let guard = DBG.lock().ok()?;
let dbg = guard.as_ref()?;
@@ -353,21 +353,9 @@ fn read_memory_inner(
}
let start = i32::try_from(i64::from(base) + offset).ok()?;
- // `read_cell` lê cells de 4 bytes alinhadas; alinha para baixo e pula o resto.
- let aligned = start & !3;
- let skip = usize::try_from(start - aligned).ok()?;
- let mut out = Vec::with_capacity(skip + count);
- let mut addr = aligned;
- while out.len() < skip + count {
- let Some(cell) = amx.read_cell(addr) else {
- break; // endereço inacessível: devolve o que leu até aqui
- };
- out.extend_from_slice(&cell.to_le_bytes());
- addr = addr.wrapping_add(4);
- }
- // Fatia [skip, skip+count) do que foi lido (pode ser menor no fim do segmento).
- let end = (skip + count).min(out.len());
- Some(out.get(skip..end).unwrap_or(&[]).to_vec())
+ // O SDK cuida do alinhamento de cells e para no primeiro endereço
+ // inacessível — no fim do segmento devolve menos que `count`.
+ amx.read_bytes(start, count)
}
/// Arma os data breakpoints pedidos pelo adaptador. Resolve cada `(frame, name)`
@@ -389,8 +377,7 @@ fn resolve_data_watches(reqs: Vec) -> Vec, value: i32)
let (cip, frm) = *frames.get(frame)?;
(*amx_usize, cip, frm)
};
- // Reconstrói um `Amx` sobre o ponteiro da VM pausada. `write_cell` lê o
- // base/data segment direto da struct AMX, então a tabela de funções não é
- // necessária aqui (0 serve).
- let amx = Amx::new(amx_usize as *mut samp::raw::types::AMX, 0);
+ let amx = Amx::data_only(amx_usize as *mut samp::raw::types::AMX);
let guard = DBG.lock().ok()?;
let dbg = guard.as_ref()?;
diff --git a/crates/debug-plugin/src/lib.rs b/crates/debug-plugin/src/lib.rs
index 56a5fb2..4adbc38 100644
--- a/crates/debug-plugin/src/lib.rs
+++ b/crates/debug-plugin/src/lib.rs
@@ -15,7 +15,6 @@ mod gate;
mod hook;
mod inspect;
mod runtime_error;
-mod stack;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
diff --git a/crates/debug-plugin/src/runtime_error.rs b/crates/debug-plugin/src/runtime_error.rs
index ef9c63b..68034d0 100644
--- a/crates/debug-plugin/src/runtime_error.rs
+++ b/crates/debug-plugin/src/runtime_error.rs
@@ -20,72 +20,16 @@
//! essa tabela (ponteiro → opcode). Em imagens não-relocadas, o valor já é o
//! número.
-use std::collections::HashMap;
-
-/// Números de opcode da VM AMX (ordem do enum em `amx.c`). Só os que o simulador
-/// de linha consome (efeito em `pri`/`alt`) ou detecta.
-pub const OP_LOAD_PRI: i32 = 1; // pri = data[offs]
-pub const OP_LOAD_ALT: i32 = 2; // alt = data[offs]
-pub const OP_LOAD_S_PRI: i32 = 3; // pri = data[frm+offs]
-pub const OP_LOAD_S_ALT: i32 = 4; // alt = data[frm+offs]
-pub const OP_CONST_PRI: i32 = 11;
-pub const OP_CONST_ALT: i32 = 12;
-pub const OP_MOVE_PRI: i32 = 33; // pri = alt
-pub const OP_MOVE_ALT: i32 = 34; // alt = pri
-pub const OP_XCHG: i32 = 35;
-pub const OP_PUSH_PRI: i32 = 36;
-pub const OP_PUSH_ALT: i32 = 37;
-pub const OP_PUSH_C: i32 = 39;
-pub const OP_POP_PRI: i32 = 42;
-pub const OP_POP_ALT: i32 = 43;
-pub const OP_SDIV: i32 = 73;
-pub const OP_SDIV_ALT: i32 = 74;
-pub const OP_UDIV: i32 = 76;
-pub const OP_UDIV_ALT: i32 = 77;
-pub const OP_ZERO_PRI: i32 = 89;
-pub const OP_ZERO_ALT: i32 = 90;
-pub const OP_BOUNDS: i32 = 121;
-pub const OP_BREAK: i32 = 137;
-// Opcodes de endereço/memória e pilha/heap, para os erros STACKERR/HEAPLOW/
-// MEMACCESS (números conferidos na `amx_opcodelist` do interpretador).
-pub const OP_LOAD_I: i32 = 9; // pri = data[pri]
-pub const OP_LODB_I: i32 = 10; // pri = data[pri] (byte/word)
-pub const OP_ADDR_PRI: i32 = 13; // pri = frm + offs
-pub const OP_ADDR_ALT: i32 = 14; // alt = frm + offs
-pub const OP_STOR_I: i32 = 23; // data[alt] = pri
-pub const OP_STRB_I: i32 = 24; // data[alt] = pri (byte/word)
-pub const OP_LIDX: i32 = 25; // pri = data[pri*4 + alt]
-pub const OP_LIDX_B: i32 = 26; // pri = data[(pri< bool {
(addr >= hea && addr < stk) || addr.cast_unsigned() >= stp.cast_unsigned()
}
-/// Traduz o valor cru lido do code segment (via `read_code`) no número do opcode.
-pub struct OpcodeMap {
- /// `endereço do label → número do opcode`. Vazio = imagem não relocada.
- inverse: HashMap,
-}
-
-impl OpcodeMap {
- /// Constrói o mapa a partir da `amx_opcodelist` (de `Amx::opcode_table`).
- #[must_use]
- pub fn new(opcode_table: Option>) -> Self {
- let inverse = opcode_table
- .map(|table| {
- table
- .into_iter()
- .enumerate()
- .map(|(op, addr)| (addr, i32::try_from(op).unwrap_or(-1)))
- .collect()
- })
- .unwrap_or_default();
- Self { inverse }
- }
-
- /// Opcode real a partir do valor cru em `code[cip]`. Resolve o endereço de
- /// label (computed-goto) ou aceita um número de opcode pequeno (não relocado).
- /// `None` se não for nenhum dos dois.
- #[must_use]
- pub fn decode(&self, raw: i32) -> Option {
- if self.inverse.is_empty() {
- return Some(raw);
- }
- if let Some(&op) = self
- .inverse
- .get(&usize::try_from(raw.cast_unsigned()).ok()?)
- {
- return Some(op);
- }
- (0..i32::try_from(OP_NUM_OPCODES).ok()?)
- .contains(&raw)
- .then_some(raw)
- }
-}
-
/// Estado simulado dos registradores durante a varredura de uma linha.
struct Regs {
pri: i32,
@@ -246,7 +148,7 @@ pub fn scan_line(
if op == OP_BREAK {
return None;
}
- let nparams = u32::from(*OP_PARAMS.get(usize::try_from(op).ok()?)?);
+ let nparams = u32::from(operand_cells(op)?);
if nparams == 99 {
return None; // tamanho variável → não dá para avançar com segurança
}
@@ -495,6 +397,8 @@ pub fn scan_line(
#[cfg(test)]
mod tests {
use super::*;
+ use samp::debug::OpcodeMap;
+ use samp::debug::opcode::OP_NUM_OPCODES;
/// Monta um "code segment" a partir de uma lista de (opcode, params...).
fn code(instrs: &[&[i32]]) -> Vec {
diff --git a/crates/debug-plugin/src/stack.rs b/crates/debug-plugin/src/stack.rs
deleted file mode 100644
index 66ec4cf..0000000
--- a/crates/debug-plugin/src/stack.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-//! Caminhada da pilha de chamadas (call stack) do AMX — a lógica pura, separada
-//! da leitura de memória real (`Amx::read_cell`), via um leitor injetável. Assim
-//! a caminhada é testável com um mapa de memória falso, sem servidor.
-//!
-//! # Layout de frame do AMX
-//!
-//! A pilha do AMX cresce para BAIXO (endereços menores = mais recente), então os
-//! frames dos chamadores ficam em endereços MAIORES. O prólogo `OP_PROC` empilha o
-//! `FRM` anterior e aponta `FRM` para o topo; a instrução `OP_CALL` empilhou antes
-//! o endereço de retorno. Relativo ao `frm` corrente:
-//!
-//! ```text
-//! [frm] = FRM do chamador (salvo pelo PROC)
-//! [frm + CELL] = endereço de retorno no chamador (empilhado pelo CALL)
-//! ```
-//!
-//! O `amx_Exec` empilha um endereço de retorno `0` antes de entrar no público de
-//! entrada; ao chegar nele, `[frm + CELL] == 0` encerra a caminhada.
-
-/// Tamanho de uma cell do AMX (32 bits). O `cip`/`OP_BREAK` do resto do plugin já
-/// assume 4 (ver `hook::BREAK_OP_SIZE`).
-const CELL: i32 = 4;
-
-/// Teto de profundidade da caminhada — guarda contra uma pilha corrompida (frame
-/// que não sobe, ciclo) para não girar sem fim no hook de debug.
-const MAX_DEPTH: usize = 128;
-
-/// Caminha a pilha a partir do frame do topo `(top_cip, top_frm)` e devolve os
-/// frames `(cip, frm)` do topo (índice 0, onde a VM parou) até o público de
-/// entrada. `stp` é o topo da pilha (`Amx::stp`), o limite superior válido de um
-/// endereço de dados; `read_cell` lê uma cell do segmento de dados (`None` se
-/// inacessível).
-///
-/// Para cada chamador, o `cip` é o endereço de retorno salvo — um offset de
-/// código dentro da função chamadora, que mapeia à linha do ponto de chamada.
-#[must_use]
-pub fn walk(
- top_cip: u32,
- top_frm: i32,
- stp: i32,
- read_cell: impl Fn(i32) -> Option,
-) -> Vec<(u32, i32)> {
- let mut frames = vec![(top_cip, top_frm)];
- let mut frm = top_frm;
-
- for _ in 0..MAX_DEPTH {
- // Frame precisa caber na pilha para ler os dois slots do cabeçalho.
- if frm <= 0 || frm + CELL >= stp {
- break;
- }
- let (Some(ret), Some(prev)) = (read_cell(frm + CELL), read_cell(frm)) else {
- break;
- };
- // `amx_Exec` empurra retorno 0 antes do público de entrada: sem chamador.
- if ret <= 0 {
- break;
- }
- frames.push((ret.cast_unsigned(), prev));
- // O frame do chamador deve estar ACIMA (endereço maior) e dentro da pilha;
- // caso contrário a cadeia é inválida e paramos após registrar a linha.
- if prev <= frm || prev >= stp {
- break;
- }
- frm = prev;
- }
-
- frames
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::collections::HashMap;
-
- /// Monta um leitor de memória falso a partir de pares (endereço, valor).
- fn mem(pairs: &[(i32, i32)]) -> impl Fn(i32) -> Option {
- let map: HashMap = pairs.iter().copied().collect();
- move |addr| map.get(&addr).copied()
- }
-
- #[test]
- fn single_frame_when_return_is_zero() {
- // Público de entrada: [frm+4] = 0 (retorno sentinela do amx_Exec).
- let read = mem(&[(1000, 0), (1004, 0)]);
- let frames = walk(40, 1000, 2000, read);
- assert_eq!(frames, vec![(40, 1000)]);
- }
-
- #[test]
- fn walks_two_levels() {
- // foo (frm=1000) chamado por main (frm=1500), main é o público de entrada.
- // foo: [1000]=1500 (FRM de main), [1004]=800 (retorno em main)
- // main: [1500]=1900 (FRM anterior), [1504]=0 (entrada → para)
- let read = mem(&[(1000, 1500), (1004, 800), (1500, 1900), (1504, 0)]);
- let frames = walk(40, 1000, 2000, read);
- assert_eq!(frames, vec![(40, 1000), (800, 1500)]);
- }
-
- #[test]
- fn walks_three_levels() {
- // bar(1000) ← foo(1400) ← main(1800, entrada).
- let read = mem(&[
- (1000, 1400),
- (1004, 600), // retorno em foo
- (1400, 1800),
- (1404, 300), // retorno em main
- (1800, 1950),
- (1804, 0), // entrada
- ]);
- let frames = walk(64, 1000, 2000, read);
- assert_eq!(frames, vec![(64, 1000), (600, 1400), (300, 1800)]);
- }
-
- #[test]
- fn stops_on_unreadable_cell() {
- // Sem dados para [1000]/[1004]: só o frame do topo.
- let read = mem(&[]);
- let frames = walk(40, 1000, 2000, read);
- assert_eq!(frames, vec![(40, 1000)]);
- }
-
- #[test]
- fn stops_when_frame_does_not_climb() {
- // prev (1000) não sobe em relação a frm (1000): registra a linha do
- // chamador e para, sem laço infinito.
- let read = mem(&[(1000, 1000), (1004, 800)]);
- let frames = walk(40, 1000, 2000, read);
- assert_eq!(frames, vec![(40, 1000), (800, 1000)]);
- }
-
- #[test]
- fn stops_when_frame_out_of_stack() {
- // frm no limite de stp: não há espaço para o cabeçalho do frame.
- let read = mem(&[(1996, 100), (2000, 0)]);
- let frames = walk(40, 1998, 2000, read);
- assert_eq!(frames, vec![(40, 1998)]);
- }
-}
diff --git a/docs/architecture.en-US.md b/docs/architecture.en-US.md
index 81ae2d9..76bf79f 100644
--- a/docs/architecture.en-US.md
+++ b/docs/architecture.en-US.md
@@ -28,10 +28,16 @@ The parser for the `AMX_DBG` format (address ↔ line ↔ symbol ↔ function) a
VM primitives come from the [`rust-samp`](https://rust-samp.nullsablex.com/) SDK:
- `samp::debug` / `samp_sdk::debug` — parser for the debug block.
-- `Amx::cip/frame/stack/heap/stp/pri/alt` — the VM registers.
+- `Amx::cip/frame/stack/heap/hlw/stp/pri/alt` — the VM registers.
- `Amx::read_cell/write_cell` — data (inspecting and editing variables).
-- `Amx::read_code` / `Amx::opcode_table` — code (decoding opcodes; see
- [Pausing on an error](runtime-errors.md)).
+- `Amx::read_bytes` — raw memory ranges (the `readMemory` hex view).
+- `Amx::read_code` / `Amx::opcode_map` — code and opcode decoding under
+ relocation (see [Pausing on an error](runtime-errors.md)).
+- `samp::debug::opcode` — opcode numbering, `STK_MARGIN` and each instruction's
+ size (`operand_cells`), which the line scan uses to step forward.
+- `samp::debug::stack::walk` / `Amx::call_stack` — walking the frame chain.
+- `Amx::data_only` — wraps a paused VM for data-side access with no function
+ table (the socket thread's situation during a pause).
Using the SDK as the single source avoids duplicating the parser between plugin
and adapter (the adapter depends on `rust-samp-sdk` with `default-features =
diff --git a/docs/architecture.md b/docs/architecture.md
index f97e92d..fb4c8d6 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -27,10 +27,16 @@ O parser do formato `AMX_DBG` (endereço ↔ linha ↔ símbolo ↔ função) e
primitivas de VM vêm do SDK [`rust-samp`](https://rust-samp.nullsablex.com/):
- `samp::debug` / `samp_sdk::debug` — parser do bloco de debug.
-- `Amx::cip/frame/stack/heap/stp/pri/alt` — registradores da VM.
+- `Amx::cip/frame/stack/heap/hlw/stp/pri/alt` — registradores da VM.
- `Amx::read_cell/write_cell` — dados (inspecionar/editar variáveis).
-- `Amx::read_code` / `Amx::opcode_table` — código (decodificar opcodes; ver
- [Pausa no erro](runtime-errors.md)).
+- `Amx::read_bytes` — faixa de memória crua (o hex view do `readMemory`).
+- `Amx::read_code` / `Amx::opcode_map` — código e a decodificação de opcodes sob
+ relocação (ver [Pausa no erro](runtime-errors.md)).
+- `samp::debug::opcode` — numeração dos opcodes, `STK_MARGIN` e o tamanho de
+ cada instrução (`operand_cells`), que a varredura da linha usa para avançar.
+- `samp::debug::stack::walk` / `Amx::call_stack` — caminhada da cadeia de frames.
+- `Amx::data_only` — embrulha a VM pausada para acesso só de dados, sem tabela
+ de funções (é a situação da thread do socket durante a pausa).
Usar o SDK como fonte única evita duplicar o parser entre o plugin e o adaptador
(o adaptador depende do `rust-samp-sdk` com `default-features = false, features =
From 5e4cc9fc46c74d036a67c4300f19958a41028e35 Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Tue, 1 Sep 2026 01:39:13 -0300
Subject: [PATCH 32/33] chore(deps): consumir rust-samp 3.4.0 do crates.io
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A dependência git + rev existia porque o debugger precisava de API do SDK que
ainda não estava publicada. Com o lançamento da 3.4.0 (rust-samp e
rust-samp-sdk), passa a ser dependência de versão: acaba o re-pin por SHA a
cada mudança no SDK e o build deixa de resolver nada pelo GitHub — não há mais
nenhuma dependência git no Cargo.lock.
---
CHANGELOG.md | 3 +-
Cargo.lock | 51 ++++++++++++++++++----------------
crates/dap-adapter/Cargo.toml | 2 +-
crates/debug-plugin/Cargo.toml | 2 +-
docs/architecture.en-US.md | 3 +-
docs/architecture.md | 3 +-
6 files changed, 35 insertions(+), 29 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be09646..846bfb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,7 +33,8 @@ expressão, leitura de memória e mais três classes de erro de runtime.
- **README e documentação reescritos** — recursos, arquitetura e a página de localização atualizados para o que esta versão entrega.
### Dependências
-- **SDK `rust-samp`** atualizado ao longo do ciclo, acompanhando o que cada versão liberou: `v3.3.1` (correção), `v3.4.0`, o acessor `Amx::hlw` (exigido pelo HEAPLOW) e `AmxDbg::function_address` (exigido pelos breakpoints de função).
+- **SDK `rust-samp` agora vem do crates.io** (`3.4.0`), no lugar da dependência `git` + `rev` que o projeto carregava desde o início — ela existia porque o debugger precisava de API ainda não publicada. O build deixa de depender do GitHub para resolver dependências.
+- Ao longo do ciclo o SDK acompanhou o que cada versão liberou: `v3.3.1` (correção), `v3.4.0`, o acessor `Amx::hlw` (exigido pelo HEAPLOW) e `AmxDbg::function_address` (exigido pelos breakpoints de função).
- **Lógica genérica de VM devolvida ao SDK** (`rust-samp-sdk` 3.4.0) — a numeração dos opcodes, o tamanho das instruções, o decodificador da relocação por computed-goto (`OpcodeMap`), a caminhada da cadeia de frames e a leitura de faixas de memória eram fatos da VM AMX que viviam aqui. Agora vêm de `samp::debug::opcode`, `samp::debug::stack` e `Amx::read_bytes`/`call_stack`/`data_only`, e o plugin ficou ~250 linhas menor sem perder comportamento.
- Atualizações de `serde` (1.0.229), `serde_json` (1.0.151), das GitHub Actions e das dependências da documentação.
diff --git a/Cargo.lock b/Cargo.lock
index 4afac02..930d1aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.13.0"
+version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "cfg-if"
@@ -107,15 +107,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.186"
+version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "log"
-version = "0.4.33"
+version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "mach2"
@@ -128,9 +128,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.8.2"
+version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "mmap-fixed-fixed"
@@ -180,18 +180,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
-version = "1.0.106"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.46"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
@@ -233,7 +233,8 @@ dependencies = [
[[package]]
name = "rust-samp"
version = "3.4.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=9524c09#9524c09808576b42d4f4df36d2b9ffa6078075d4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8dfaa4bbc0847a5f6f4ccb77694a02cedc2cf11d61722d41f6ba6ae80c8f2887"
dependencies = [
"fern",
"log",
@@ -246,7 +247,8 @@ dependencies = [
[[package]]
name = "rust-samp-codegen"
version = "1.4.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=9524c09#9524c09808576b42d4f4df36d2b9ffa6078075d4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73c2a29e2ef6737ca3afe059e0006e1bc8fd88f017a38d6f2bc46e8f348eac5a"
dependencies = [
"proc-macro2",
"quote",
@@ -256,9 +258,10 @@ dependencies = [
[[package]]
name = "rust-samp-sdk"
version = "3.4.0"
-source = "git+https://github.com/NullSablex/rust-samp?rev=9524c09#9524c09808576b42d4f4df36d2b9ffa6078075d4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92de2d90ad83ece4e7ce5911cbe55f5bbce2f2099f9807cfed8b7980bf89f855"
dependencies = [
- "bitflags 2.13.0",
+ "bitflags 2.13.1",
]
[[package]]
@@ -312,9 +315,9 @@ checksum = "7a3d689654af89bdfeba29a914ab6ac0236d382eb3b764f7454dde052f2821f8"
[[package]]
name = "syn"
-version = "3.0.0"
+version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967"
+checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [
"proc-macro2",
"quote",
@@ -323,9 +326,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.53"
+version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50"
+checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
dependencies = [
"deranged",
"libc",
@@ -345,9 +348,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "time-macros"
-version = "0.2.31"
+version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f"
+checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
dependencies = [
"num-conv",
"time-core",
@@ -489,6 +492,6 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zmij"
-version = "1.0.21"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
diff --git a/crates/dap-adapter/Cargo.toml b/crates/dap-adapter/Cargo.toml
index 8155b16..0f0c357 100644
--- a/crates/dap-adapter/Cargo.toml
+++ b/crates/dap-adapter/Cargo.toml
@@ -11,7 +11,7 @@ name = "dap-adapter"
path = "src/main.rs"
[dependencies]
-rust-samp-sdk = { git = "https://github.com/NullSablex/rust-samp", rev = "9524c09", default-features = false, features = ["debug"] }
+rust-samp-sdk = { version = "3.4.0", default-features = false, features = ["debug"] }
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
serde = { version = "1", features = ["derive"] }
diff --git a/crates/debug-plugin/Cargo.toml b/crates/debug-plugin/Cargo.toml
index efc5761..9075077 100644
--- a/crates/debug-plugin/Cargo.toml
+++ b/crates/debug-plugin/Cargo.toml
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
[dependencies]
pawnpro-dbg-protocol = { path = "../protocol" }
interprocess = "2"
-samp = { package = "rust-samp", git = "https://github.com/NullSablex/rust-samp", rev = "9524c09", features = ["debug"] }
+samp = { package = "rust-samp", version = "3.4.0", features = ["debug"] }
[package.metadata.samp]
uid = "0x0d9107bbd31c8d1b"
diff --git a/docs/architecture.en-US.md b/docs/architecture.en-US.md
index 76bf79f..e88d421 100644
--- a/docs/architecture.en-US.md
+++ b/docs/architecture.en-US.md
@@ -41,7 +41,8 @@ VM primitives come from the [`rust-samp`](https://rust-samp.nullsablex.com/) SDK
Using the SDK as the single source avoids duplicating the parser between plugin
and adapter (the adapter depends on `rust-samp-sdk` with `default-features =
-false, features = ["debug"]` — the pure logic only, no FFI).
+false, features = ["debug"]` — the pure logic only, no FFI). Both come from
+crates.io at version **3.4.0**; the build has no `git` dependency.
## The flow of a pause
diff --git a/docs/architecture.md b/docs/architecture.md
index fb4c8d6..8171e94 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -40,7 +40,8 @@ primitivas de VM vêm do SDK [`rust-samp`](https://rust-samp.nullsablex.com/):
Usar o SDK como fonte única evita duplicar o parser entre o plugin e o adaptador
(o adaptador depende do `rust-samp-sdk` com `default-features = false, features =
-["debug"]`, só a lógica pura, sem FFI).
+["debug"]`, só a lógica pura, sem FFI). Ambos vêm do crates.io na versão
+**3.4.0** — o build não depende de nenhuma dependência `git`.
## Fluxo de uma pausa
From a664a9d751a79a084a990597325214e0c2506c7b Mon Sep 17 00:00:00 2001
From: NullSablex <244216261+NullSablex@users.noreply.github.com>
Date: Tue, 1 Sep 2026 01:44:04 -0300
Subject: [PATCH 33/33] docs(changelog): completar a 0.2.0 com tudo desde a
v0.1.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Faltavam a fileira de badges do README, o marcador do plugin subindo para
0.2.0, a correção da tabela de opcodes em runtime-errors.md e as atualizações
de dependência do Dependabot nos três ecossistemas.
Conferido item a item contra os 50 commits desde a tag.
---
CHANGELOG.md | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 846bfb3..7cdf16c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,11 +30,16 @@ expressão, leitura de memória e mais três classes de erro de runtime.
### Alterado
- **Mensagens do adaptador agora são localizadas** — antes só os erros de runtime seguiam o idioma do editor. As mensagens dos dois lados foram unificadas em `crates/protocol/src/messages`, com 11 chaves em pt-BR, en, es, ro e ru; a tabela de cada idioma é exaustiva, então um idioma incompleto não compila.
- **O protocolo ganhou um canal request/response** — `ReadMemory` ↔ `MemoryData`, correlacionados por `id` e com timeout, para a sessão não ficar presa se o plugin não responder. O restante do protocolo continua assíncrono nos dois sentidos.
-- **README e documentação reescritos** — recursos, arquitetura e a página de localização atualizados para o que esta versão entrega.
+- **README e documentação reescritos** — recursos, arquitetura e a página de localização atualizados para o que esta versão entrega. O README ganhou a fileira de badges (CI, CodeQL, docs, OpenSSF Scorecard, release, downloads, estrelas e licença) e uma linha de navegação para a documentação, os releases e a extensão.
+- **Marcador do plugin atualizado para `PAWNPRO_DEBUG_MARKER:0.2.0`** — a extensão casa apenas o prefixo `PAWNPRO_DEBUG_MARKER`, então o reconhecimento do plugin oficial não muda; o tamanho segue 26 bytes.
+
+### Corrigido
+- Tabela de erros em [Como funciona a pausa no erro](docs/runtime-errors.md): faltavam `OP_CALL_PRI` no STACKERR e `OP_LODB_I`/`OP_STRB_I`/`OP_LIDX_B` no MEMACCESS, todos já checados pelo plugin.
### Dependências
- **SDK `rust-samp` agora vem do crates.io** (`3.4.0`), no lugar da dependência `git` + `rev` que o projeto carregava desde o início — ela existia porque o debugger precisava de API ainda não publicada. O build deixa de depender do GitHub para resolver dependências.
- Ao longo do ciclo o SDK acompanhou o que cada versão liberou: `v3.3.1` (correção), `v3.4.0`, o acessor `Amx::hlw` (exigido pelo HEAPLOW) e `AmxDbg::function_address` (exigido pelos breakpoints de função).
+- Atualizações do **Dependabot** nos três ecossistemas, agrupadas por ecossistema a partir de #13: `cargo` (`serde` 1.0.228 → 1.0.229 e o grupo cargo com 2 atualizações), `github-actions` (`checkout`, `upload-artifact`, `download-artifact`, `setup-python`, `rust-cache`, `action-gh-release`) e `pip` (`mkdocs-material`, `pymdown-extensions`). Nenhuma altera comportamento do debugger.
- **Lógica genérica de VM devolvida ao SDK** (`rust-samp-sdk` 3.4.0) — a numeração dos opcodes, o tamanho das instruções, o decodificador da relocação por computed-goto (`OpcodeMap`), a caminhada da cadeia de frames e a leitura de faixas de memória eram fatos da VM AMX que viviam aqui. Agora vêm de `samp::debug::opcode`, `samp::debug::stack` e `Amx::read_bytes`/`call_stack`/`data_only`, e o plugin ficou ~250 linhas menor sem perder comportamento.
- Atualizações de `serde` (1.0.229), `serde_json` (1.0.151), das GitHub Actions e das dependências da documentação.