import { AABB } from "./Geometry" ; import { Color , Magenta , colorToCSS , Red , Green , Blue , OpaqueBlack } from "./Color" ; import { divideByW , ScreenSpaceProjection } from "./Camera" ; import { vec4 , vec3 , mat4 , ReadonlyMat4 , ReadonlyVec3 , ReadonlyVec4 } from "gl-matrix" ; import { nArray , assert , assertExists , hexzero } from "./util" ; import { UI , Slider } from "./ui" ; import { getMatrixTranslation , getMatrixAxisX , getMatrixAxisY , getMatrixAxisZ , MathConstants , transformVec3Mat4w0 , Vec3UnitX , lerp } from "./MathHelpers" ; import ArrayBufferSlice from "./ArrayBufferSlice" ; import { downloadBufferSlice , downloadBuffer } from "./DownloadUtils" ; import { GfxClipSpaceNearZ } from "./gfx/platform/GfxPlatform" ; export function stepF ( f , maxt , step , callback ) { for ( let t = 0 ; t < maxt ; t += step ) { callback ( t , f ( t ) ) ; } } export type F = ( t : number ) => number ; export class Graph { public minv : number | undefined = undefined ; public maxv : number | undefined = undefined ; public ctx : CanvasRenderingContext2D ; public mx = 0 ; public my = 0 ; constructor ( ctx ) { this . ctx = ctx ; this . ctx . canvas . onmousemove = ( e ) => { this . mx = e . offsetX ; this . my = e . offsetY ; } ; } public clear ( ) { const ctx = this . ctx ; ctx . fillStyle = 'white' ; ctx . fillRect ( 0 , 0 , ctx . canvas . width , ctx . canvas . height ) ; ctx . strokeStyle = '#aaaaaa' ; ctx . lineWidth = 1 ; ctx . beginPath ( ) ; ctx . moveTo ( 0 , this . my + 0.5 ) ; ctx . lineTo ( ctx . canvas . width , this . my + 0.5 ) ; ctx . stroke ( ) ; } public hline ( color , v ) { const ctx = this . ctx ; const ya = ( v - this . minv ! ) / ( this . maxv ! - this . minv ! ) ; const y = ( 1 - ya ) * ctx . canvas . height ; ctx . strokeStyle = color ; ctx . lineWidth = 2 ; ctx . beginPath ( ) ; ctx . moveTo ( 0 , y ) ; ctx . lineTo ( ctx . canvas . width , y ) ; ctx . stroke ( ) ; } public dot ( color , t , v , radius = 5 ) { assert ( t >= 0 && t <= 1 ) ; const ctx = this . ctx ; ctx . fillStyle = color ; ctx . beginPath ( ) ; const ya = ( v - this . minv ! ) / ( this . maxv ! - this . minv ! ) ; const x = t * ctx . canvas . width ; const y = ( 1 - ya ) * ctx . canvas . height ; ctx . arc ( x , y , radius , 0 , Math . PI * 2 ) ; ctx . closePath ( ) ; ctx . fill ( ) ; } public marker ( color , t ) { assert ( t >= 0 && t <= 1 ) ; const ctx = this . ctx ; ctx . strokeStyle = color ; ctx . lineWidth = 1 ; ctx . beginPath ( ) ; const x = t * ctx . canvas . width ; ctx . moveTo ( x , 0 ) ; ctx . lineTo ( x , ctx . canvas . height ) ; ctx . stroke ( ) ; } public graphF ( color , f , range , step = 1 ) { stepF ( f , range , step , ( t , v ) => { if ( this . minv === undefined ) this . minv = v ; if ( this . maxv === undefined ) this . maxv = v ; this . minv = Math . min ( this . minv , v ) ; this . maxv = Math . max ( this . maxv , v ) ; } ) ; const ctx = this . ctx ; ctx . strokeStyle = color ; ctx . lineWidth = 2 ; ctx . beginPath ( ) ; stepF ( f , range , step , ( t , v ) => { const xa = ( t / range ) ; const ya = ( v - this . minv ! ) / ( this . maxv ! - this . minv ! ) ; const x = xa * ctx . canvas . width ; const y = ( 1 - ya ) * ctx . canvas . height ; ctx . lineTo ( x , y ) ; } ) ; ctx . stroke ( ) ; } } export function cv ( ) { const canvas = document . createElement ( 'canvas' ) ; canvas . width = 800 ; canvas . height = 400 ; const ctx = assertExists ( canvas . getContext ( '2d' ) ) ; ctx . fillStyle = 'white' ; ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ; canvas . style . position = 'absolute' ; canvas . style . top = '0' ; canvas . style . left = '0' ; [ ] . forEach . call ( document . querySelectorAll ( 'canvas.cv' ) , ( e ) => document . body . removeChild ( e ) ) ; canvas . classList . add ( 'cv' ) ; document . body . appendChild ( canvas ) ; return ctx ; } let _debugOverlayCanvas = null ; export function getDebugOverlayCanvas2D ( ) { if ( _debugOverlayCanvas === null ) { const canvas = document . createElement ( 'canvas' ) ; const ctx = assertExists ( canvas . getContext ( '2d' ) ) ; canvas . style . position = 'absolute' ; canvas . style . top = '0' ; canvas . style . left = '0' ; canvas . style . pointerEvents = 'none' ; document . body . appendChild ( canvas ) ; _debugOverlayCanvas = ctx ; prepareFrameDebugOverlayCanvas2D ( ) ; } return _debugOverlayCanvas ! ; } export function prepareFrameDebugOverlayCanvas2D ( ) { if ( _debugOverlayCanvas !== null ) { _debugOverlayCanvas . canvas . style . width = `${ window . innerWidth }px` ; _debugOverlayCanvas . canvas . style . height = `${ window . innerHeight }px` ; _debugOverlayCanvas . canvas . width = window . innerWidth * window . devicePixelRatio ; _debugOverlayCanvas . canvas . height = window . innerHeight * window . devicePixelRatio ; } } const p = nArray ( 10 , ( ) => vec4 . create ( ) ) ; function transformToClipSpace ( m , p , nPoints ) { for ( let i = 0 ; i < nPoints ; i ++ ) vec4 . transformMat4 ( p [ i ] , p [ i ] , m ) ; } function clipLineToPlane ( da , db , a , b , plane ) { const dotA = vec4 . dot ( a , plane ) ; const dotB = vec4 . dot ( b , plane ) ; if ( dotA < 0.0 && dotB < 0.0 ) { return false ; } const t = dotA / ( dotA - dotB ) ; if ( dotA < 0.0 ) vec4 . lerp ( da , a , b , t ) ; else vec4 . copy ( da , a ) ; if ( dotB < 0.0 ) vec4 . lerp ( db , a , b , t ) ; else vec4 . copy ( db , b ) ; return true ; } const nearPlane = vec4 . fromValues ( 0 , 0 , - 1 , 1 ) ; function clipLineAndDivide ( da , db , a , b ) { if ( ! clipLineToPlane ( da , db , a , b , nearPlane ) ) return false ; divideByW ( da , da ) ; divideByW ( db , db ) ; return true ; } function drawClipSpaceLine ( ctx , p0 , p1 , s0 , s1 ) { if ( ! clipLineAndDivide ( s0 , s1 , p0 , p1 ) ) return ; const cw = ctx . canvas . width ; const ch = ctx . canvas . height ; ctx . moveTo ( ( s0 [ 0 ] + 1 ) * cw / 2 , ( ( - s0 [ 1 ] + 1 ) * ch / 2 ) ) ; ctx . lineTo ( ( s1 [ 0 ] + 1 ) * cw / 2 , ( ( - s1 [ 1 ] + 1 ) * ch / 2 ) ) ; } export function drawWorldSpaceLine ( ctx , clipFromWorldMatrix , v0 , v1 , color = Magenta , thickness = 2 ) { vec4 . set ( p [ 0 ] , v0 [ 0 ] , v0 [ 1 ] , v0 [ 2 ] , 1.0 ) ; vec4 . set ( p [ 1 ] , v1 [ 0 ] , v1 [ 1 ] , v1 [ 2 ] , 1.0 ) ; transformToClipSpace ( clipFromWorldMatrix , p , 2 ) ; ctx . beginPath ( ) ; drawClipSpaceLine ( ctx , p [ 0 ] , p [ 1 ] , p [ 8 ] , p [ 9 ] ) ; ctx . closePath ( ) ; ctx . lineWidth = thickness ; ctx . strokeStyle = colorToCSS ( color ) ; ctx . stroke ( ) ; } const scratchVec3v = vec3 . create ( ) ; export function drawWorldSpaceVector ( ctx , clipFromWorldMatrix , pos , dir , mag , color = Magenta , thickness = 2 ) { vec3 . scaleAndAdd ( scratchVec3v , pos , dir , mag ) ; drawWorldSpaceLine ( ctx , clipFromWorldMatrix , pos , scratchVec3v , color , thickness ) ; } const scratchMatrix = mat4 . create ( ) ; const scratchVec3a = vec3 . create ( ) ; const scratchVec3b = vec3 . create ( ) ; export function drawWorldSpaceBasis ( ctx , clipFromWorldMatrix , m , mag = 100 , thickness = 2 ) { getMatrixTranslation ( scratchVec3a , m ) ; getMatrixAxisX ( scratchVec3b , m ) ; drawWorldSpaceVector ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b , mag , Red , thickness ) ; getMatrixAxisY ( scratchVec3b , m ) ; drawWorldSpaceVector ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b , mag , Green , thickness ) ; getMatrixAxisZ ( scratchVec3b , m ) ; drawWorldSpaceVector ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b , mag , Blue , thickness ) ; } export function drawWorldSpaceAABB ( ctx , clipFromWorldMatrix , aabb , m = null , color = Magenta ) { vec4 . set ( p [ 0 ] , aabb . minX , aabb . minY , aabb . minZ , 1.0 ) ; vec4 . set ( p [ 1 ] , aabb . maxX , aabb . minY , aabb . minZ , 1.0 ) ; vec4 . set ( p [ 2 ] , aabb . minX , aabb . maxY , aabb . minZ , 1.0 ) ; vec4 . set ( p [ 3 ] , aabb . maxX , aabb . maxY , aabb . minZ , 1.0 ) ; vec4 . set ( p [ 4 ] , aabb . minX , aabb . minY , aabb . maxZ , 1.0 ) ; vec4 . set ( p [ 5 ] , aabb . maxX , aabb . minY , aabb . maxZ , 1.0 ) ; vec4 . set ( p [ 6 ] , aabb . minX , aabb . maxY , aabb . maxZ , 1.0 ) ; vec4 . set ( p [ 7 ] , aabb . maxX , aabb . maxY , aabb . maxZ , 1.0 ) ; if ( m !== null ) for ( let i = 0 ; i < 8 ; i ++ ) vec4 . transformMat4 ( p [ i ] , p [ i ] , m ) ; transformToClipSpace ( clipFromWorldMatrix , p , 8 ) ; ctx . beginPath ( ) ; drawClipSpaceLine ( ctx , p [ 0 ] , p [ 1 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 1 ] , p [ 3 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 3 ] , p [ 2 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 2 ] , p [ 0 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 4 ] , p [ 5 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 5 ] , p [ 7 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 7 ] , p [ 6 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 6 ] , p [ 4 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 0 ] , p [ 4 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 1 ] , p [ 5 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 2 ] , p [ 6 ] , p [ 8 ] , p [ 9 ] ) ; drawClipSpaceLine ( ctx , p [ 3 ] , p [ 7 ] , p [ 8 ] , p [ 9 ] ) ; ctx . closePath ( ) ; ctx . lineWidth = 2 ; ctx . strokeStyle = colorToCSS ( color ) ; ctx . stroke ( ) ; } export function drawViewportSpacePoint ( ctx , x , y , color = Magenta , size = 4 ) { const rad = size >>> 1 ; ctx . fillStyle = colorToCSS ( color ) ; ctx . fillRect ( x - rad , ctx . canvas . height - y - rad , size , size ) ; } function shouldCull ( p , clipSpaceNearZ = window . main . viewer . gfxDevice . queryVendorInfo ( ) . clipSpaceNearZ ) { const nearZ = clipSpaceNearZ === GfxClipSpaceNearZ . Zero ? 0 : - 1 ; return p [ 0 ] < - 1 || p [ 0 ] > 1 || p [ 1 ] < - 1 || p [ 1 ] > 1 || p [ 2 ] < nearZ || p [ 2 ] > 1 ; } export function drawWorldSpacePoint ( ctx , clipFromWorldMatrix , v , color = Magenta , size = 4 ) { const cw = ctx . canvas . width ; const ch = ctx . canvas . height ; vec4 . set ( p [ 0 ] , v [ 0 ] , v [ 1 ] , v [ 2 ] , 1.0 ) ; transformToClipSpace ( clipFromWorldMatrix , p , 1 ) ; divideByW ( p [ 0 ] , p [ 0 ] ) ; if ( shouldCull ( p [ 0 ] ) ) return ; const x = ( p [ 0 ] [ 0 ] + 1 ) * cw / 2 ; const y = ( p [ 0 ] [ 1 ] + 1 ) * ch / 2 ; drawViewportSpacePoint ( ctx , x , y , color , size ) ; } export function drawWorldSpaceCircle ( ctx , clipFromWorldMatrix , center , radius , axis , nPoints = 32 ) { for ( let i = 0 ; i < nPoints ; i ++ ) { const t0 = ( ( i + 0 ) / nPoints ) * MathConstants . TAU ; mat4 . fromRotation ( scratchMatrix , t0 , axis ) ; transformVec3Mat4w0 ( scratchVec3a , scratchMatrix , Vec3UnitX ) ; vec3 . scaleAndAdd ( scratchVec3a , center , scratchVec3a , radius ) ; const t1 = ( ( i + 1 ) / nPoints ) * MathConstants . TAU ; mat4 . fromRotation ( scratchMatrix , t1 , axis ) ; transformVec3Mat4w0 ( scratchVec3b , scratchMatrix , Vec3UnitX ) ; vec3 . scaleAndAdd ( scratchVec3b , center , scratchVec3b , radius ) ; drawWorldSpaceLine ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b ) ; } } export function drawWorldSpaceFan ( ctx , clipFromWorldMatrix , center , radius , front , angle , axis ) { const nPoints = 32 ; for ( let i = 0 ; i < nPoints ; i ++ ) { const t0 = lerp ( - angle , angle , ( i + 0 ) / ( nPoints - 1 ) ) ; mat4 . fromRotation ( scratchMatrix , t0 , axis ) ; transformVec3Mat4w0 ( scratchVec3a , scratchMatrix , front ) ; vec3 . scaleAndAdd ( scratchVec3a , center , scratchVec3a , radius ) ; if ( i === 0 ) drawWorldSpaceLine ( ctx , clipFromWorldMatrix , center , scratchVec3a ) ; const t1 = lerp ( - angle , angle , ( i + 1 ) / ( nPoints - 1 ) ) ; mat4 . fromRotation ( scratchMatrix , t1 , axis ) ; transformVec3Mat4w0 ( scratchVec3b , scratchMatrix , front ) ; vec3 . scaleAndAdd ( scratchVec3b , center , scratchVec3b , radius ) ; if ( i === nPoints - 1 ) drawWorldSpaceLine ( ctx , clipFromWorldMatrix , center , scratchVec3b ) ; drawWorldSpaceLine ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b ) ; } } export function drawWorldSpaceCylinder ( ctx , clipFromWorldMatrix , center , radius , height , axis , nPoints = 32 ) { for ( let i = 0 ; i < nPoints ; i ++ ) { const t0 = ( ( i + 0 ) / nPoints ) * MathConstants . TAU ; mat4 . fromRotation ( scratchMatrix , t0 , axis ) ; transformVec3Mat4w0 ( scratchVec3a , scratchMatrix , Vec3UnitX ) ; vec3 . scaleAndAdd ( scratchVec3a , center , scratchVec3a , radius ) ; const t1 = ( ( i + 1 ) / nPoints ) * MathConstants . TAU ; mat4 . fromRotation ( scratchMatrix , t1 , axis ) ; transformVec3Mat4w0 ( scratchVec3b , scratchMatrix , Vec3UnitX ) ; vec3 . scaleAndAdd ( scratchVec3b , center , scratchVec3b , radius ) ; drawWorldSpaceLine ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b ) ; vec3 . scaleAndAdd ( scratchVec3b , scratchVec3a , axis , height ) ; drawWorldSpaceLine ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b ) ; vec3 . scaleAndAdd ( scratchVec3a , scratchVec3a , axis , height ) ; mat4 . fromRotation ( scratchMatrix , t1 , axis ) ; transformVec3Mat4w0 ( scratchVec3b , scratchMatrix , Vec3UnitX ) ; vec3 . scaleAndAdd ( scratchVec3b , center , scratchVec3b , radius ) ; vec3 . scaleAndAdd ( scratchVec3b , scratchVec3b , axis , height ) ; drawWorldSpaceLine ( ctx , clipFromWorldMatrix , scratchVec3a , scratchVec3b ) ; } } interface TextOptions { font ? : string ; shadowColor ? : string ; shadowBlur ? : number ; outline ? : number ; align ? : CanvasTextAlign ; } export function drawScreenSpaceText ( ctx , x , y , text , color = Magenta , options = { } ) { ctx . fillStyle = colorToCSS ( color ) ; ctx . textBaseline = 'bottom' ; ctx . textAlign = options . align ?? 'start' ; ctx . font = options . font ?? '14pt monospace' ; if ( options . outline ) { const oldLineWidth = ctx . lineWidth ; ctx . lineWidth = options . outline ; ctx . strokeStyle = colorToCSS ( OpaqueBlack , color . a ) ; ctx . strokeText ( text , x , y ) ; ctx . lineWidth = oldLineWidth ; } ctx . shadowColor = options . shadowColor ?? colorToCSS ( OpaqueBlack , color . a ) ; ctx . shadowBlur = options . shadowBlur ?? 0 ; ctx . fillText ( text , x , y ) ; ctx . shadowColor = 'black' ; ctx . shadowBlur = 0 ; } export function drawWorldSpaceText ( ctx , clipFromWorldMatrix , v , text , offsY = 0 , color = Magenta , options = { } ) { const cw = ctx . canvas . width ; const ch = ctx . canvas . height ; vec4 . set ( p [ 0 ] , v [ 0 ] , v [ 1 ] , v [ 2 ] , 1.0 ) ; transformToClipSpace ( clipFromWorldMatrix , p , 1 ) ; divideByW ( p [ 0 ] , p [ 0 ] ) ; if ( shouldCull ( p [ 0 ] ) ) return ; const x = ( p [ 0 ] [ 0 ] + 1 ) * cw / 2 ; const y = ( - p [ 0 ] [ 1 ] + 1 ) * ch / 2 + offsY ; drawScreenSpaceText ( ctx , x , y , text , color , options ) ; } export function drawScreenSpaceProjection ( ctx , proj , color = Magenta ) { const cw = ctx . canvas . width ; const ch = ctx . canvas . height ; const x1 = ( proj . projectedMinX + 1 ) * cw / 2 ; const x2 = ( proj . projectedMaxX + 1 ) * cw / 2 ; const y1 = ( - proj . projectedMinY + 1 ) * ch / 2 ; const y2 = ( - proj . projectedMaxY + 1 ) * ch / 2 ; ctx . beginPath ( ) ; ctx . rect ( x1 , y1 , x2 - x1 , y2 - y1 ) ; ctx . closePath ( ) ; ctx . lineWidth = 2 ; ctx . strokeStyle = colorToCSS ( color ) ; ctx . stroke ( ) ; } function flashItem ( item , fieldName , step = 0 ) { item [ fieldName ] = step % 2 === 1 ; if ( step < 7 ) setTimeout ( ( ) => { flashItem ( item , fieldName , step + 1 ) ; } , 200 ) ; } export function interactiveSliderSelect ( items , testItem , done ) { const ui = ( window as any ) . main . ui ; const debugFloater = ui . debugFloaterHolder . makeFloatingPanel ( 'SliderSelect' ) ; const slider = new Slider ( ) ; slider . elem . querySelector ( 'input' ) ! . classList . remove ( 'Slider' ) ; debugFloater . contents . append ( slider . elem ) ; const textLabel = document . createElement ( 'div' ) ; textLabel . style . padding = '1em' ; debugFloater . contents . append ( textLabel ) ; const doneButton = document . createElement ( 'div' ) ; doneButton . textContent = 'Select' ; doneButton . style . background = '#333' ; doneButton . style . cursor = 'pointer' ; doneButton . style . padding = '1em' ; debugFloater . contents . append ( doneButton ) ; slider . setRange ( - 1 , items . length - 1 , 1 ) ; slider . onvalue = ( v ) => { slider . setLabel ( '' + v ) ; for ( let i = 0 ; i < items . length ; i ++ ) { const label = testItem ( i , ( i <= v ) ) ; if ( i === v ) textLabel . textContent = label ? label : '' ; } if ( v < 0 ) textLabel . textContent = '' ; } ; slider . setValue ( items . length - 1 , true ) ; doneButton . onclick = ( ) => { const index = slider . getValue ( ) ; debugFloater . close ( ) ; done ( index ) ; } ; debugFloater . onclose = ( ) => { done ( - 1 ) ; } ; } export function interactiveVizSliderSelect ( items , fieldName = 'visible' , callback = null ) { const visibleItems = items . filter ( ( v ) => v [ fieldName ] ) ; interactiveSliderSelect ( visibleItems , ( i , v ) => { const item = visibleItems [ i ] ; item [ fieldName ] = v ; return item . name || item . constructor . name ; } , ( index ) => { visibleItems . forEach ( ( v ) => v [ fieldName ] = true ) ; if ( index >= visibleItems . length || index < 0 ) return ; const item = visibleItems [ index ] ; const origIndex = items . indexOf ( item ) ; flashItem ( item , fieldName ) ; console . log ( `Found item @ ${ origIndex }:` , item ) ; if ( callback !== null ) callback ( item , origIndex ) ; } ) ; } function downloadBufferAny ( name , buffer ) { if ( name . name && name . arrayBuffer ) downloadBufferSlice ( name . name , name ) ; else if ( buffer instanceof ArrayBufferSlice ) downloadBufferSlice ( name , buffer ) ; else if ( name . name && name . buffer ) downloadBuffer ( name . name , name . buffer ) ; else if ( buffer instanceof ArrayBuffer ) downloadBuffer ( name , buffer ) ; } export function ghidraDecode ( s , encoding = 'sjis' ) { const hex = new Uint8Array ( [ ... s . matchAll ( /[0-9A-Fa-f]{2}h/g ) ] . map ( ( g ) => parseInt ( g [ 0 ] . slice ( 0 , 2 ) , 16 ) ) ) ; console . log ( [ ... hex ] . map ( ( g ) => g . toString ( 16 ) ) ) ; return new TextDecoder ( encoding ) . decode ( hex ) ; } export function hexdump ( b_ , offs = 0 , length = 0x100 ) { const buffer = b_ instanceof ArrayBufferSlice ? b_ : new ArrayBufferSlice ( b_ ) ; const groupSize_ = 16 ; let S = '' ; const arr = buffer . createTypedArray ( Uint8Array , offs ) ; length = Math . min ( length , arr . byteLength ) ; for ( let i = 0 ; i < length ; i += groupSize_ ) { let groupSize = Math . min ( length - i , groupSize_ ) ; const addr = offs + i ; S += `${ hexzero ( addr , 8 ) }    ` ; for ( let j = 0 ; j < groupSize ; j ++ ) { const b = arr [ i + j ] ; S += ` ${ hexzero ( b , 2 ) }` ; } for ( let j = groupSize ; j < groupSize_ ; j ++ ) S += `   ` ; S += '  ' ; for ( let j = 0 ; j < groupSize ; j ++ ) { const b = arr [ i + j ] ; const c = ( b >= 0x20 && b < 0x7F ) ? String . fromCharCode ( b ) : '.' ; S += `${ c }` ; } for ( let j = groupSize ; j < groupSize_ ; j ++ ) S += ` ` ; S += '\n' ; } console . log ( S ) ; } export function magicstr ( v ) { v = v & 0xFFFFFFFF ; const a0 = String . fromCharCode ( ( v >>> 24 ) & 0xFF ) ; const a1 = String . fromCharCode ( ( v >>> 16 ) & 0xFF ) ; const a2 = String . fromCharCode ( ( v >>> 8 ) & 0xFF ) ; const a3 = String . fromCharCode ( ( v >>> 0 ) & 0xFF ) ; return a0 + a1 + a2 + a3 ; } export const debugJunk = { interactiveVizSliderSelect , hexdump , magicstr , ghidraDecode , downloadBuffer : downloadBufferAny , } ;