IS-NITRO-CAPTURE Video Adjustment Utility


Aug 21, 2020
United States

IS-NITRO-CAPTURE Video Adjustment Utility​


@famiac and I wanted to remove the aspect ratio "fix". The "fix" would apply uneven scaling which isn't pixel perfect...
Thus we created this utility to remove that "feature". This utility will patch the firmware, enabling the patch to persist and work without having a PC connected.

As always, lots of thanks to @Gericom for documenting the IS-NITRO-EMULATOR and the IS-NITRO-CAPTURE hardware registers.

Note this utility is only for the CAPTURE. We have made patches for the EMULATOR in another post.


This utility features removing the aspect ratio fix and adjusting the video offsets.
(Removing the aspect ratio fix will cause the image to be uncentered. Plus, we want fine adjustments for a CRT setup.)

For convenience, the utility auto-increments the firmware version so we don't have to reset the firmware everytime we wanted to adjust the video offsets. (ISNP.exe checks if the firmware version is higher than the version installed onto the machine.)

This utility relies on Cheat Engine. Our process patches the firmware stored inside RAM when ISNP.exe runs.


  • Use Windows 7 or earlier
  • Install Cheat Engine 7.1 or greater
  • Install IS-NITRO-CAPTURE_v2_15_07102300.exe
    • Under Win7, use XP SP3 compatibility mode to install


  1. Open ISNP.exe
  2. Open ISNP-Patcher.CT
  3. Press [Scan Firmware!] The signature, type, and version should be updated.
  4. Select any of the options you want to patch.
    • Patch Version
      • The firmware version should be 0x1DE.
      • Please enable it; The version number must be higher than the current version.
    • Remove Aspect Ratio Fix
      • There are 3 options for DIP2 configuration. When DIP2 is OFF, ON, or Always (Ignore DIP2).
      • (DIP2 allows user settings separate from the default.)
    • Adjust Video Offsets
      • You can adjust the settings for each AV out.
      • X and Y offsets for single screen mode.
      • X and Y offsets for dual screen mode.
  5. Finally, press [Apply Patch!]
  6. In ISNP.exe, proceed with a firmware update as usual.

Method for Centering the Image​

When the aspect ratio fix is removed, the bottom left corner of the image stays the same using the same offset values.
The image horizontally shrinks by 64 pixels (on a 640*480px grid).
To center the image, Famiac added 32 to the X offset. The Y offset is centered by eye according to the specific monitor.

Famiac's Values:
  • Single Screen X & Y: 0xE4, 0x2A
  • Dual Screen X & Y: 0x60, 0x26
  • Default Single: 0xC4, 0x2E (for reference)
Single Screen Ranges:
  • X: 0x87 (left), 0xF6 (right)
  • Y: 0x1C (top), 0x3E (bottom)
The ranges represent when the image touches the left, right, top, or bottom edges.

Based on the ranges, these units roughly correspond to a 640*480 resolution.
X: 0xF6-0x87 = 0x6F = 111px; 256*2 = 512px (NDS X Res * 2); 512+111 = 623px
Y: 0x3E-0x1C = 0x22 = 34px; 192*2 = 384px (NDS Y Res * 2); 384+34 = 418px

Technical Details​

The firmware is stored inside ISNP.exe and not in some DLL file.

We believe ISNP.exe is packed by ASProtect (ASProtect 2.0x as reported by PEiD). We did manage to unpack the program but we couldn't figure out a way to make it run after patching it... Thus, we gave up on that route... (If you want to give it a shot, I have tried using ImpREC 1.7e and Skylla to try "repacking" the program...)

Regardless, we figure using Cheat Engine might work... and indeed it does! We searched for the firmware header in memory and was able to patch it using Cheat Engine. Thus after a few lines of Lua and making a GUI inside the GUI editor for Cheat Engine, we had a working patcher.

The firmware is "encrypted" simply by decrementing/incrementing each byte... (Just like the firmware for EMULATOR.) Try searching for "HMSDKKHFDMS". (This is what our LUA script searches for to get the reference point for the firmware.)

Look at "script.lua" and "ISNP.CT" if you want to take a look at the code or adjust it. A majority of the code is GUI related while the rest references locations inside the firmware. (Various byte changes here and there...) GUI "code" is done via Cheat Engine's form design tool. Since Cheat Engine can edit memory, executables produced by Cheat Engine can be flagged as viruses... Which is why we opted to use the *.CT format. (Since it's just text afterall.)

<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="31">
    <Patcher Class="TCEForm" Encoding="Ascii85">*mvX_,r{BEL]Re=$N%Ca3xXo-nk[D{8S?Rv^/HxmJ9h2Vi#$tpqQ%8P%e2LsCKZv2=E(3{A.3[q#W0_4Rv;#zx3?e5$W!.2ry+{EkYPZuVnDhT@?d=vf+=W7-5q*SCmu.Lwwfxqn7,I3=*({jyYIe[I]?BzVal[U-^8vQ6O)FVjhhwpYE5i)(4_l7]?;XbN_aV:I.+AFvRY=TsXgbUCB4QnEH@IHbxBn,]:=81XZcB}v9;NWMBW.pXIz#/MYqNC:I:y0H)gbgk/*?MbpO1IVSWM(!:(K9^)[email protected],Y*0I+T/lgn0+WbmBY+[]12lq-pd?e],Wc9myJh^9@oR)vhl$mJ{@G}){%]ajvmGB(ok!=:8R$R%mf}TWq*NbhYaJ=sTXlyB(QEBMww(mG0?M.moMWnMwmr_sTjtWnHhsQX(6V}ILtA[xPAM!E(s^ipTT1gIk!Bs3H7fTrXEQDlO]6V/JGHIjW;V!rma7!Bl4)z[{Eu52gdy@!{#yVxb5!UySU$j2M[R1qzCGY3Th_v?4Wqv?wMZ{ZTX=U/=)z-p2)7N?7[H.4k{0v$zS:,6[0%HCpp^=wV@RkxpU^wwm!f85o6rAaRXySeb8JdPMk31AKf;8?OT!;U%!4G%Y6Fj72}tdBwPNEZ!=%rqyv)[[Wm]G^H5)Cu18}h@w+ED,{75X%@lI{*}#E)px?o^=]@,l[e1zmLGnmXRQF1]gy%+WCMHf{]ye*m,HY;)#ACSaV[^)d%c1hjok1*(]me7W$3Et5]y:wGAU+7Y+3?0#]Xc!k^[_}PWNX,X?Bbj1NBaD3]{bJpjqNuuKM3#m^qGqk^_)661/E6R$-GD((5a7eZ2a;txJ1dLg7iQIjX@j:1=gdNA1L3;hbt:MvZ^$)4!J?6xVKbO+Qb?TpskJx4KAzc1$kZA*R-*Ema}E*%K6E2*5?{npAQt2YE5mFq$Lsg(XI9d)HpAF=J2G,Vq=G43jL[B5j*%T5b=EdG04{Zhb(ino5;cMx;{NcEanEs*W)w7DAk3kq@yjXas%E/EbDc?Z!sSC3r8#qi$x?qW-yu:VNsNGC22)w@kze)VR7T#:j)$WxSXIh#@F3v!0r*D+wYs5bRR}YH(Lg+8-S_YK6COHY,YZ=(%$Fz)[email protected][:y2[h%DiQ}4G8}Tlptpl9KBnrrx?KaW[hLL@VdMia=y*guE,7C?c-+_$(_p6rLJU6^I4l_mSJ8^;n-.E-P,YkSmgatFc=fO1CRSI,*QtfgMpzwjWgMHN.2}7Sfs]kZ[F,gf7Nmuso!1;_qkAy}-kO]6P:rNUT+[vlqp1nXipU+o!Du$PHz$Jvvxy}1(s,V[GmVN=)x}fiLZ/N5Iu4CF)}L}!E*wPrV(:_R%,2NsGTbX]uqW563v.N.nWm.o%bS6m=)/gS^+YChKAW6Wwj_0WU1.cOCI:s9L{dle#_*Ik4Sh.d@hwpSEsm=oW3o[P@*!_AO/CkJ%i5SK!$;j+i5[c0R,R2iRZz@w-E*NTId*s.DB/]J:2*@ge$6:^f:qJATW1dFveaG#+pA=z#[email protected],e(u6znZ%b,M^XN+kdEVT[IH=h.PNL8ld4NZ:4d^!W3Ml5.nG5vMpe8T.JjOnEK8M[7eT8DSL)aKb7cjR[@*djh8}]VQbAd-ga$htyE-:i^EjtFKp{r=U:T[FblZS9fquTvIQJyg3a(C%^@0PaBS{8{h.laCAN8IblgsO;@T.PtStl?@Un6JX_E}+c*uWmz3u$QhpYj6z7}9EGqIQzg]m,9--ljlam3fp61_VNmuJR9_ajq0o4N$hG/8g*tmtH3zk!PJrv!5AKlp=lcs@oA^nSdjRwy]PeEt3CIDBmz/nnOT$G;8nDIY^tw})U1t4Z!h,TOCTnSCoW4p(;$979;(Hj%eImKgdPVq1mky8Yo!b8CX]I3#t-elupD#,?,!CqnMH5d[=B!vfiE,T0xxVnTny7W*m[j5z0qWnCZQ^}NxG+;^@f,N7sA;c1H*Mgz5XAASA*L@b/Fj@$h)y@U5mwn%}jA6=%w!*7!T6jhn.tKP8?s.wZ+Cc-JHHm-Fk3Ygp4l,{M6UfvI4sl0e1T}o]yUEg5FvxCe;BVsC4;y90sh9VBpKt=A3,?#s5quVEbv]V7==N4tZG9^n9.C,$h7Nm![;4u@i#rm.XNt/ihNN;ngbDPUi;jRs:-s=mqIj/*:e-inJ2g[Z=IF=19F1NX,sL!Ajhu1/x*-j{x@O$3i8+g?5JS^AIJSqNy]r%?[/TX1#{^zzdw_q:;N[VyQ0V=nvVq-Z.6pJ=Py^I4:O_q9DeUpJ({WaHZVGZV*5$i8e/3yrgxA{0%E3M+DrW0K$h@K/qWS8SvygamhLJ*?qs8A:bLUJCySXrpZAcWAd(mH2gU5N^/3n_vprV?^amhVIl]yw#;zmB#Z1FfyI($pwyHR*K:/[UT7_*gDJbTtM.Y%CO7;SO/V,mI+!6Df1CbcsMVFph5l2A^(v%5_4g!]Jj0@e]%YZeYh,1akCsHwf#CAqa#cEH+y=OEINUTd2oUv#u-Z4s75$n*4#dS]8[B;?Ft30AU2vhy!Cm5PyOw)}WBFV6),:b34Lo)UrJOhCs8d_PH:m-EaidY/VHLP1#A45Gqj?HvFdAS]SK:jn$$sO*}cf}JW_-9hry9h]q@Vw4[(v^R]$h*T@tC2^.]B1w2k+z!,%Jkmo+,_@jbpD{]nj1fFx#t6QE86$_w7)sZ)2s*lIf}brUL{ZMha}UIulQDuP%NW5@mob8lXN)_D#t5D*V,1;pF?i5r#t1r{F?o?n)bR}ZF?nxzj37E:1W%GNf+r@h!RM1PCa_[%htOTlB/:6F.dyO-udl]V.x@fZDU?u5FJHWcDS56=v3JOIpHeD8ves6F)LE,!66InOV(pQ5m)Hq,FS$G9$zGvC(b0#9@_TzimR!*L#0=tRd)9rF#j3QH*Jc2CFthnzFy8BD7Io:YBQ6s-+LKmMyrA):dHa?GuZ[od$(7Jd%Jb=I5[npMNRKS74yH9w5NQq}TlqesyrqK.V9:wQ4?AlhV!,%5b0YjN^DPsg$UCuoQ9I+HPP/tVPN;x/XnU}[DU4o-Ya;0F9e!Sro1$jFzp:=a([ZFr*kGKN]9i%eZ-$d=-oj7jmNL,x;P.:iS9k1O%IkV2Q=2[a:q4fM*^Vm$hG[ob*w8XP@{87GamBhL1^26]aO8IWaflj8dEyE7Ir3L$+O).NTaVx}k6,y6wW@;fun)]-M1GmvclCca]hN</Patcher>
  <LuaScript>function incBytes(bT)
  local b = {}
  --for i=1,#bT,1 do
  --  b[i] = bAnd(bT[i] + 1, 0xFF)
  for k,v in pairs(bT) do
    b[k] = v + 1
  return b

function decBytes(bT)
  local b = {}
  --for i=1,#bT,1 do
  --  b[i] = bAnd(bT[i] + 1, 0xFF)
  for k,v in pairs(bT) do
    b[k] = v - 1
  return b

-- read 2 bytes incremented by 1
function readIncWord(addr)
  return byteTableToWord(incBytes(readBytes(addr, 2, true)))

function writeDecWord(addr, val)

function ScanButtonClick(sender)

  base = AOBScanUnique(
    "48 4D 53 44 4B 4B 48 46 44 4D 53 1F 52 58 52 53 " ..
    "44 4C 52 FF FE FE FE FE FE FE FE FE FE FE FE FE " ..
    "4C 60 68 6D 1F 4F 71 6E 66 71 60 6C FF"

  if (base == nil) or (base == 0) then
    Patcher.Status.Caption = "Scanning Failed! :("

  local bIS = readBytes(base, 19, true)
  local bMain = readBytes(base+0x20, 12, true)
  local bVersion = readBytes(base+0x40, 4, true)

  local sIS = byteTableToString(incBytes(bIS))
  local sMain = byteTableToString(incBytes(bMain))
  local nVersion = byteTableToDword(incBytes(bVersion))

  Patcher.Signature.Caption = sIS
  Patcher.Type.Caption = sMain
  Patcher.Version.Caption = string.format("0x%08X",nVersion)
  Patcher.VersionEdit.Text = string.format("0x%X",nVersion+1)

  -- locations of instructions where offsets are stored
  --   they're in the form "movea val, r0, rn", val is stored in the
  --   second 2 bytes of the 4 byte instruction
  -- AV1X: 0x25DE, 0x25E2 (Jrev, Normal)
  -- AV1Y: 0x2650, 0x2654
  -- AV1XDual: 0x2618, 0x261C
  -- AV1YDual: 0x268E
  AV1X = readIncWord(base+0x25E4)
  AV1Y = readIncWord(base+0x2656)
  AV1XDual = readIncWord(base+0x261E)
  AV1YDual = readIncWord(base+0x2690)
  -- AV1Gap: 0x26B0, mov 8 r13
  AV1Gap = readBytes(base+0x26B0, 1) + 1

  -- AV2X: 0x26EA, 0x26EE
  -- AV2Y: 0x2756, 0x275A
  -- AV2XDual: 0x2720, 0x2724
  -- AV2YDual: 0x2790
  AV2X = readIncWord(base+0x26F0)
  AV2Y = readIncWord(base+0x275C)
  AV2XDual = readIncWord(base+0x2726)
  AV2YDual = readIncWord(base+0x2792)
  -- AV2Gap: 0x27B6, mov 8 r13
  AV2Gap = readBytes(base+0x27B6, 1) + 1

  Patcher.AV1XEdit.Text = AV1X
  Patcher.AV1YEdit.Text = AV1Y
  Patcher.AV1XDualEdit.Text = AV1XDual
  Patcher.AV1YDualEdit.Text = AV1YDual
  Patcher.AV1GapEdit.Text = AV1Gap

  Patcher.AV2XEdit.Text = AV2X
  Patcher.AV2YEdit.Text = AV2Y
  Patcher.AV2XDualEdit.Text = AV2XDual
  Patcher.AV2YDualEdit.Text = AV2YDual
  Patcher.AV2GapEdit.Text = AV2Gap

  -- Enable user to make changes
  Patcher.PatchVersionCheck.Enabled = true
  -- Patcher.VersionEdit.Enabled = true
  Patcher.AspectFixPanel.Enabled = true
  Patcher.OffsetPanel.Enabled = true
  Patcher.ApplyButton.Enabled = true

  Patcher.Status.Caption = "You can start selecting patches!"

function PatchVersionCheckChange(sender)
  Patcher.VersionEdit.Enabled = Patcher.PatchVersionCheck.Checked

function AspectFixCheckChange(sender)
  local state = Patcher.AspectFixCheck.Checked
  Patcher.DIP2Off.Enabled = state
  Patcher.DIP2On.Enabled = state
  Patcher.DIP2Both.Enabled = state

function AdjustOffsetCheckChange(sender)
  local state = Patcher.AdjustOffsetCheck.Checked
  Patcher.AV1.Enabled = state
  Patcher.AV2.Enabled = state
  Patcher.OffsetResetButton.Enabled = state

function OffsetResetButtonClick(sender)
  Patcher.AV1XEdit.Text = AV1X
  Patcher.AV1YEdit.Text = AV1Y
  Patcher.AV1XDualEdit.Text = AV1XDual
  Patcher.AV1YDualEdit.Text = AV1YDual
  Patcher.AV1GapEdit.Text = AV1Gap

  Patcher.AV2XEdit.Text = AV2X
  Patcher.AV2YEdit.Text = AV2Y
  Patcher.AV2XDualEdit.Text = AV2XDual
  Patcher.AV2YDualEdit.Text = AV2YDual
  Patcher.AV2GapEdit.Text = AV2Gap

function ApplyButtonClick(sender)
  local b = false

  if Patcher.PatchVersionCheck.Checked then
    b = true


  if Patcher.AspectFixCheck.Checked then
    b = true

    if Patcher.DIP2Off.Checked then
      -- 128CE bnh     loc_12900
      writeBytes(base+0x28CE, 0x94)
    elseif Patcher.DIP2On.Checked then
      -- 1379E bnh     loc_137D0
      writeBytes(base+0x379E, 0x94)
    else -- both
      writeBytes(base+0x28CE, 0x94)
      writeBytes(base+0x379E, 0x94)

  if Patcher.AdjustOffsetCheck.Checked then
    b = true
    -- AV1X: 0x25DE, 0x25E2
    writeDecWord(base+0x25E4, Patcher.AV1XEdit.Text)
    writeDecWord(base+0x25F0, Patcher.AV1XEdit.Text)
    -- AV1Y: 0x2650, 0x2654
    writeDecWord(base+0x2656, Patcher.AV1YEdit.Text)
    writeDecWord(base+0x2652, Patcher.AV1YEdit.Text)
    -- AV1XDual: 0x2618, 0x261C
    writeDecWord(base+0x261E, Patcher.AV1XDualEdit.Text)
    writeDecWord(base+0x261A, Patcher.AV1XDualEdit.Text)
    -- AV1YDual: 0x268E
    writeDecWord(base+0x2690, Patcher.AV1YDualEdit.Text)

    -- AV2X: 0x26EA, 0x26EE
    writeDecWord(base+0x25F0, Patcher.AV2XEdit.Text)
    writeDecWord(base+0x25EC, Patcher.AV2XEdit.Text)
    -- AV2Y: 0x2756, 0x275A
    writeDecWord(base+0x275C, Patcher.AV2YEdit.Text)
    writeDecWord(base+0x2758, Patcher.AV2YEdit.Text)
    -- AV2XDual: 0x2720, 0x2724
    writeDecWord(base+0x2726, Patcher.AV2XDualEdit.Text)
    writeDecWord(base+0x2722, Patcher.AV2XDualEdit.Text)
    -- AV2YDual: 0x2790
    writeDecWord(base+0x2792, Patcher.AV2YDualEdit.Text)

    -- AV1Gap: 0x26B0, mov 8 r13
    writeBytes(base+0x26B0, tonumber(Patcher.AV1GapEdit.Text)-1)
    -- AV2Gap: 0x27B6, mov 8 r13
    writeBytes(base+0x27B6, tonumber(Patcher.AV2GapEdit.Text)-1)

  if b then
    Patcher.Status.Caption = "Patches are applied! Scan to verify offsets!"
    Patcher.Status.Caption = "You haven't selected any patches to apply :/"

function ShowAsHexClick(sender)
  Patcher.AV1XEdit.Text = string.format("0x%X", Patcher.AV1XEdit.Text)
  Patcher.AV1YEdit.Text = string.format("0x%X", Patcher.AV1YEdit.Text)
  Patcher.AV1XDualEdit.Text = string.format("0x%X", Patcher.AV1XDualEdit.Text)
  Patcher.AV1YDualEdit.Text = string.format("0x%X", Patcher.AV1YDualEdit.Text)

  Patcher.AV2XEdit.Text = string.format("0x%X", Patcher.AV2XEdit.Text)
  Patcher.AV2YEdit.Text = string.format("0x%X", Patcher.AV2YEdit.Text)
  Patcher.AV2XDualEdit.Text = string.format("0x%X", Patcher.AV2XDualEdit.Text)
  Patcher.AV2YDualEdit.Text = string.format("0x%X", Patcher.AV2YDualEdit.Text)

  Patcher.AV1GapEdit.Text = string.format("0x%X", Patcher.AV1GapEdit.Text);
  Patcher.AV2GapEdit.Text = string.format("0x%X", Patcher.AV2GapEdit.Text);

function ShowAsDecimalClick(sender)
  Patcher.AV1XEdit.Text = string.format("%d", Patcher.AV1XEdit.Text)
  Patcher.AV1YEdit.Text = string.format("%d", Patcher.AV1YEdit.Text)
  Patcher.AV1XDualEdit.Text = string.format("%d", Patcher.AV1XDualEdit.Text)
  Patcher.AV1YDualEdit.Text = string.format("%d", Patcher.AV1YDualEdit.Text)

  Patcher.AV2XEdit.Text = string.format("%d", Patcher.AV2XEdit.Text)
  Patcher.AV2YEdit.Text = string.format("%d", Patcher.AV2YEdit.Text)
  Patcher.AV2XDualEdit.Text = string.format("%d", Patcher.AV2XDualEdit.Text)
  Patcher.AV2YDualEdit.Text = string.format("%d", Patcher.AV2YDualEdit.Text)

  Patcher.AV1GapEdit.Text = string.format("%d", Patcher.AV1GapEdit.Text);
  Patcher.AV2GapEdit.Text = string.format("%d", Patcher.AV2GapEdit.Text);

function FormClose(sender)
  ---- comment this out when editing the form
  return caFree --Possible options: caHide, caFree, caMinimize, caNone
  ---- uncomment this when editing the form
  --return caHide --Possible options: caHide, caFree, caMinimize, caNone

function clamp(low, high, n)
  local x = tonumber(n)

  if (x == nil) then
    x = 0
  elseif (x &gt; high) then
    x = high
  elseif (x &lt; low) then
    x = 0
    x = x

  return x

function AV1XEditExit(sender)
  Patcher.AV1XEdit.Text = clamp(0, 0xFF, Patcher.AV1XEdit.Text)
function AV1YEditExit(sender)
  Patcher.AV1YEdit.Text = clamp(0, 0xFF, Patcher.AV1YEdit.Text)
function AV1XDualEditExit(sender)
  Patcher.AV1XDualEdit.Text = clamp(0, 0xFFFF, Patcher.AV1XDualEdit.Text)
function AV1YDualEditExit(sender)
  Patcher.AV1YDualEdit.Text = clamp(0, 0xFF, Patcher.AV1YDualEdit.Text)
function AV1GapEditExit(sender)
  Patcher.AV1GapEdit.Text = clamp(0, 31, Patcher.AV1GapEdit.Text)

function AV2XEditExit(sender)
  Patcher.AV2XEdit.Text = clamp(0, 0xFF, Patcher.AV2XEdit.Text)
function AV2YEditExit(sender)
  Patcher.AV2YEdit.Text = clamp(0, 0xFF, Patcher.AV2YEdit.Text)
function AV2XDualEditExit(sender)
  Patcher.AV2XDualEdit.Text = clamp(0, 0xFFFF, Patcher.AV2XDualEdit.Text)
function AV2YDualEditExit(sender)
  Patcher.AV2YDualEdit.Text = clamp(0, 0xFF, Patcher.AV2YDualEdit.Text)
function AV2GapEditExit(sender)
  Patcher.AV2GapEdit.Text = clamp(0, 31, Patcher.AV2GapEdit.Text)

strings_add(getAutoAttachList(), "ISNP.exe")



    90.6 KB · Views: 56

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    AncientBoi @ AncientBoi: I shave :D +1