- Introduced a comprehensive markdown document outlining the API surface of RSNet.dll related to downloading video files. - Documented initialization, connection, record querying, and download processes. - Provided insights into the exported functions, their parameters, and expected behaviors. - Included practical implications and recommendations for implementing a downloader script using C# interop. - Highlighted the necessary struct layouts and callback mechanisms for effective integration with the DLL.
29 KiB
RSNet Download Analysis
Date: 2026-04-17
Target binary: RSNet.dll
Related binaries:
Surveillance_client.exeRSPlay.dll
Analysis method: Ghidra MCP against the loaded RSNet.dll, using read-only inspection plus function renames/comments only.
Objective
Determine enough of the RSNet.dll API and behavior to build a simple script that can:
- initialize the DLL
- connect/login to a device
- query recorded segments for a camera
- request download of a chosen interval or file segment
- wait for completion and retrieve the saved file
Bottom Line
At this point, the DLL has been analyzed far enough to outline a realistic first-pass downloader script that calls RSNet.dll directly with ctypes or similar FFI.
What is established:
RSNet.dllowns the real connection, login, record-query, playback, and record-download logic.RSNet.dllperforms its own socket I/O and background worker management.- the EXE is mainly a UI and adapter layer that fills structs and hands them to
RSNet.dll - the download worker inside
RSNet.dllwrites data locally through an internal record-file layer rather than requiring the caller to manually process frame packets - a simple standalone script is plausible without reversing the entire wire protocol, because the DLL already encapsulates the transport
What is not fully nailed down yet:
- exact C struct definitions for all exported APIs
- the full callback signature set and all event codes
- exact output container selection logic between AVI, MP4, or the vendor private format
Even with those gaps, there is now enough to define the likely call sequence and the minimum reverse-engineering targets for a first prototype.
Exported API Surface Relevant To Downloading
Confirmed exports in RSNet.dll:
RSNetInitat0x1001ae30RSNetSetEncriptionat0x1001b570RSNetConnectionStartat0x1001c350RSNetConnectionStartExat0x1001c3a0RSNetQueryRecordat0x1001c640RSNetAsyncQueryRecordat0x1001c660RSNetQueryRecordExat0x1001c680RSNetAsyncQueryRecordExat0x1001c6a0RSNetAsyncQueryRecordStopat0x1001c6c0RSNetStartDownloadRecordat0x1001c780RSNetStartDownloadRecordExat0x1001c810RSNetStartRecordPlayat0x1001c870RSNetStartRecordPlayExat0x1001c9a0RSNetReqRecordDataat0x1001ca00RSNetReqRecordPlayCtrolat0x1001ca40RSNetReleaseat0x1001b4e0
Interpretation:
- a standalone downloader should use the DLL exports directly
- there is no need to reproduce the underlying socket protocol if the goal is just to automate download on Windows where the vendor DLL is available
Initialization
RSNetInit
RSNetInit:
- initializes logging from
RSNet.ini - calls
WSAStartup(0x202, ...) - prepares global DLL state
Practical implication:
- any standalone script must call
RSNetInit()before any connection or query function
RSNetSetEncription
RSNetSetEncription(value) simply stores a global value.
Practical implication:
- there is some configurable encryption mode or toggle
- it may need to be set explicitly for some device families, but this has not yet been proven necessary for the basic flow
Connection / Login
Public entry points
RSNetConnectionStartRSNetConnectionStartEx
Both functions:
- allocate a per-device/session object of size
0xe98 - initialize internal locks, events, strings, and worker state
- copy caller-provided login parameters into the session object
- queue a background login/connection worker
- return a session handle pointer on success, or
NULLon failure
RSNetConnectionStart
This is the simpler path and likely the better first target for a prototype.
From decompilation, the input struct used by RSNetConnectionStart appears to have this rough layout:
struct RSNET_CONNECT_REQ_BASE {
char *address_or_id; // offset 0x00
uint16_t port; // offset 0x04
char *username; // offset 0x08
char *password; // offset 0x0c
uint32_t field10; // offset 0x10
uint32_t field14; // offset 0x14
uint32_t field18; // offset 0x18
uint32_t field1c; // offset 0x1c
uint32_t field20; // offset 0x20
uint32_t field24; // offset 0x24
uint32_t field28; // offset 0x28
};
The DLL copies these into its internal session object as:
- destination string at session
+0x04 - port at session
+0x20 - username at session
+0x28 - password at session
+0x44 - several mode or callback related fields at
+0x68 .. +0x80
RSNetConnectionStartEx
The extended path supports more than direct IP login. Decompiled behavior shows:
- a connection mode field at request index
0x0b - if that mode is zero, the DLL treats the first string as the device address directly
- if non-zero, it stores the first string in an alternate field and forces the target address to
127.0.0.1 - when mode is
1or10, it copies an additional string from request index0x0c
Interpretation:
RSNetConnectionStartExlikely supports P2P / relay / localhost-tunneled connection modes- the simple downloader should avoid the
Expath initially unless the target system requires P2P rather than direct device access
Login worker behavior
The session worker at 0x100074b0:
- attempts login / session setup
- posts state changes through callback or message mechanisms
- sends heartbeats every ~5 seconds after login
- dispatches status notifications with integer codes
Observed status/event codes from the worker:
2on one failure path3on disconnect/connection-lost path0x650x790x7a
Strings in the DLL map login-related status messages to names such as:
RSNetMsgLoginRequestRSNetMsgLoginSuccessRSNetMsgLoginUserLoginedRSNetMsgLoginNoUserNameRSNetMsgLoginPasswordErrorRSNetMsgLoginNoRightRSNetMsgOverMaxUserRSNetMsgLoginUserDisableRSNetMsgLoginFail
Practical implication:
- a script should assume login is asynchronous
- a callback or polling/wait mechanism is required before issuing record queries
Record Query
Public entry points
RSNetQueryRecordRSNetQueryRecordEx
The simpler function is:
RSNetQueryRecord(session, query_req)
which routes into FUN_10005ca0.
The extended function is:
RSNetQueryRecordEx(session, query_req)
which routes into FUN_10005eb0.
Behavior
Both paths:
- build a request message with message subtype
0x6f - send it through the session object
- wait up to 20 seconds for a response
- decode the returned payload into caller-facing record structures through callback helpers
The record-query dispatcher distinguishes at least two output styles using the first field of the query struct:
0x650x66
Those two cases use different callbacks:
FUN_100046b0FUN_10004750
FUN_100046b0 iterates a result array in units of 0x1c bytes.
FUN_10004750 iterates a result array in units of 0x0c bytes.
Interpretation:
- one query result format is likely the full record segment list (
0x1cbytes each) - the other is likely a compact per-day/per-file summary format (
0x0cbytes each)
For a downloader, the 0x1c record entries are the important one because the EXE uses 0x1c-byte records when building playback and download requests.
Likely record entry format
The download worker consumes entries of size 0x1c and treats them like timestamped record descriptors.
From earlier EXE analysis and DLL behavior, each 0x1c entry likely contains:
- start date/time fields
- end date/time fields
- channel or stream selection data
- a record type flag or subtype
This matches how the EXE performed time-range intersection over returned recording segments.
Download
Public entry points
RSNetStartDownloadRecordRSNetStartDownloadRecordEx
These are the core APIs for a standalone downloader.
RSNetStartDownloadRecord
This is the simpler form.
The EXE-side wrapper showed this request shape:
struct RSNET_DOWNLOAD_REQ_BASE {
uint32_t field00;
uint32_t field04;
uint32_t field08;
uint32_t callback_or_ctx;
uint32_t callback_user;
};
Inside RSNet.dll, FUN_10010330 adapts this into the extended worker request by supplying defaults:
- count or mode fields are normalized
- an extra byte field is forced to zero
- a
local_14 = 1flag is inserted before forwarding to the common worker builder
Interpretation:
- the base function is just a convenience wrapper around the extended worker
- for a script,
RSNetStartDownloadRecordis probably easier to prototype first
RSNetStartDownloadRecordEx
This is the common implementation target.
The decompiled request consumer FUN_100103b0 proves the worker expects at least:
- two required non-zero fields at offsets
+0x04and+0x08 - a count field at
+0x0c - an extra mode byte at
+0x10 - a sequence of seven 32-bit values copied from offsets
+0x00 .. +0x18into the worker object
Then it:
- allocates
count * 0x1cbytes for one array - allocates
count * 0x108bytes for a second array - copies source arrays from worker request indices
0x19and0x1a - queues a background worker thread beginning at
0x100105b0
Interpretation:
- the extended request supports batch download of one or more
0x1crecord descriptors - each record also has an associated
0x108byte metadata block, likely including the destination path or output-file information
This is a strong indication that a standalone downloader can submit one selected record entry at a time rather than needing a large complex batch.
Download Worker Behavior
The worker at 0x100105b0 is the core of remote file download.
Key verified behaviors:
- waits on session/login readiness
- opens a socket and configures timeouts
- sends a request with message subtype:
0x83in one mode0x196in another mode
- copies one or more
0x1crecord entries into the outgoing message - optionally handles an additional payload block depending on mode
- receives a response header and payload
- processes download status and progress messages
- writes or routes the resulting data into an internal record-file path
Important status/message strings in the DLL:
RSNetMsgDownloadRecordClosedRSNetMsgDownloadRecordStoreFailRSNetMsgDownloadRecordNoFileRSNetMsgDownloadRecordOKRSNetMsgDownloadRecordPercentRSNetMsgDownloadRecordFail
Observed message codes in the worker:
0x1320x1330x1420x145
Interpretation:
- the worker sends progress and completion back to the caller via callback/message dispatch
- success/failure is not just the return value of
RSNetStartDownloadRecord*; the returned handle only means the worker was started
Does The DLL Save The File Itself?
Yes, that appears to be the case.
Evidence:
- the download worker is large and manages long-running transfer state
- the DLL contains internal record-file classes:
.?AVCBaseRecordFile@@.?AVCRSAVIRecordFile@@.?AVCRSMP4RecordFile@@.?AVCRSPrivateRecordFile@@
- the DLL also contains AVI-writing error strings
- cleanup paths include an object with a
FILE *at offset0x54and a file/path buffer with small-string optimization behavior
Practical implication:
- the script most likely does not need to manually receive frame payloads and write them itself
- instead, it should provide the request/path metadata that the DLL expects, start the download, and wait for the worker to finish writing the file
Callback And Handle Lifetime
Session handle
The session handle returned by RSNetConnectionStart*:
- owns socket state
- runs a worker thread
- dispatches status via callback or window message
Important internal callback-related session fields:
- callback window/message at offsets around
+0x68and+0x6c - callback function at
+0x78 - callback user/context at
+0x70
Download handle
The download handle returned by RSNetStartDownloadRecord* is a heap object with:
- session pointer at
+0x20 - event handle at
+0x2c - worker state, copied record entries, and additional metadata
There is a tiny internal stop/cleanup wrapper around 0x1001ca81 that:
- validates the handle
- calls a stop routine
- destroys the download object via
FUN_1001cab0
So a full script should ideally also locate or call the exported stop-download function when cleanup is needed. The original EXE import table proves that such an export exists, even though the current symbol recovery in this DLL session did not label it cleanly.
Practical Downloader Strategy
The simplest viable approach is not to reverse the raw network protocol. It is to call the vendor DLL directly.
C# Interop Feasibility
Yes. C# can load RSNet.dll as an unmanaged native library and call its exports directly.
The DLL is a good fit for P/Invoke or NativeLibrary.GetExport-based interop because the key exports are ordinary x86 functions, not COM interfaces or C++ object methods.
ABI details confirmed from RSNet.dll
- architecture:
x86/little/32 - pointer size:
4 - compiler family: Visual Studio
- export names are undecorated in the import table used by the EXE
- the relevant exports use callee stack cleanup, which matches
StdCall
Observed export epilogues:
RSNetInit->RETRSNetRelease->RETRSNetConnectionStart->RET 0x4RSNetConnectionStartEx->RET 0x4RSNetQueryRecord->RET 0x8RSNetQueryRecordEx->RET 0x8RSNetStartDownloadRecord->RET 0x8RSNetStartDownloadRecordEx->RET 0x8RSNetReqRecordData->RET 0x4
Practical implication:
- the first C# prototype should treat the exported functions as
CallingConvention.StdCall - the process must be 32-bit
Runtime constraints for a C# host process
The managed host should be built and run as:
x86, notAnyCPU- Windows only
- from the vendor application directory, or with that directory added to the DLL search path
The safest deployment model is:
- place the C# test app in the same directory as
RSNet.dll - or launch it with the current directory set to
i:/Apps/Surveillance_client - or call
SetDllDirectory/AddDllDirectorybefore loadingRSNet.dll
Why that matters:
RSNet.dlldepends on the VC++ runtime and the rest of the vendor installation layout- it also expects
RSNet.ininearby for logging/config initialization - strings inside the DLL reference
RSP2PClient.exeandRSP2PDaemon.exe, so P2P mode likely depends on those companion files being present
For direct-IP downloading, the minimum runtime context is likely smaller than the full client install, but the easiest first run is still to execute from the installed client folder.
Recommended C# Loading Model
There are two realistic options.
Option 1: Static DllImport
Use this once the calling convention and signatures are stable enough.
Advantages:
- simplest code
- easiest to read
- easiest to debug once structs are correct
Disadvantages:
- more brittle while struct layouts are still being discovered
Option 2: NativeLibrary.Load plus delegates
Use this during the reverse-engineering phase.
Advantages:
- lets you experiment with signatures more safely
- avoids hard failure at process load if a dependency is missing
- makes it easy to log export resolution one function at a time
Disadvantages:
- more boilerplate
For the current stage, Option 2 is the better choice.
C# Interop Types To Use
Handles
Treat these as opaque IntPtr values:
- session handle returned by
RSNetConnectionStart* - download handle returned by
RSNetStartDownloadRecord*
Do not model them as managed classes or attempt to dereference them directly in C#.
Strings
Current evidence suggests the DLL uses ANSI char * strings, not UTF-16.
In C#:
- use
CharSet.Ansi - marshal strings as
LPStr - for uncertain layouts, prefer
IntPtrplusMarshal.StringToHGlobalAnsi
Struct packing
Use:
StructLayout(LayoutKind.Sequential, Pack = 4)
Reason:
- this is a 32-bit Visual Studio binary using 4-byte pointers and ordinary 32-bit alignment
Callbacks
Use delegates marked with:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
And keep the delegate alive for the entire native operation lifetime.
Practical rule:
- store callback delegates in fields, not local variables
Otherwise the GC may collect them while the native worker thread still holds the callback pointer.
First-Pass C# API Shape
Based on the confirmed export behavior, a first interop layer can be modeled like this:
internal static class RsNetNative
{
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
internal static extern void RSNetInit();
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern void RSNetRelease();
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern void RSNetSetEncription(uint value);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr RSNetConnectionStart(ref RsNetConnectRequest request);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr RSNetConnectionStartEx(IntPtr request);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern int RSNetQueryRecord(IntPtr session, IntPtr queryRequest);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern int RSNetQueryRecordEx(IntPtr session, IntPtr queryRequest);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr RSNetStartDownloadRecord(IntPtr session, IntPtr downloadRequest);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr RSNetStartDownloadRecordEx(IntPtr session, IntPtr downloadRequest);
[DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern void RSNetReqRecordData(IntPtr recordPlayHandle);
}
Notes:
RSNetConnectionStartis the first function worth strongly typingRSNetConnectionStartEx,RSNetQueryRecord*, andRSNetStartDownloadRecord*should initially stay asIntPtrrequest pointers until the layouts are fully proven- this reduces the chance of silent marshaling mistakes during the first prototype
First Strongly-Typed C# Struct Worth Trying
The current best candidate for a partial typed struct is the direct-IP login request used by RSNetConnectionStart.
Best current guess:
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct RsNetConnectRequest
{
public IntPtr AddressAnsi;
public ushort Port;
public ushort Padding0;
public IntPtr UsernameAnsi;
public IntPtr PasswordAnsi;
public uint Field10;
public uint Field14;
public uint Field18;
public uint Field1C;
public uint Field20;
public uint Field24;
public uint Field28;
}
Why this is reasonable:
- the decompiled function copies a pointer at offset
0x00 - a 16-bit port value is read from offset
0x04 - string pointers are copied from offsets
0x08and0x0c - the remaining fields are treated as 32-bit values or callback-related fields
This struct should still be treated as provisional.
Likely C# Callback Shape
The session object stores either:
- a callback window handle and message ID
- or a callback function pointer plus a user/context value
The worker invokes the function callback in patterns like:
callback(code, userContext)- or posts a window message with
PostMessageA(hwnd, msgId, code, userContext)
Best current guess for the login/status callback delegate:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate void RsNetStatusCallback(uint code, IntPtr userContext);
This is not fully proven, but it matches the currently observed call sites better than any multi-parameter alternative.
Why C# Is A Better Fit Than Raw Packet Reimplementation
For the current objective, C# plus unmanaged interop is likely better than reimplementing the network protocol because:
- the vendor DLL already knows the login handshake
- the vendor DLL already knows P2P versus direct-IP behavior
- the vendor DLL already manages record-query formatting
- the vendor DLL already performs the actual download worker loop
- the vendor DLL appears to write the file itself
That cuts the problem down to:
- loading the DLL safely
- building the request structs correctly
- handling callbacks/events
- waiting for the output file to appear and complete
Recommended C# Downloader Architecture
Layer 1: Native bootstrap
Responsibilities:
- locate the vendor install directory
- set DLL search path
- call
RSNetInit - optionally call
RSNetSetEncription
Layer 2: Session wrapper
Responsibilities:
- allocate unmanaged ANSI strings for device address, username, password
- build the login request
- call
RSNetConnectionStart - keep the callback delegate alive
- expose a
TaskorManualResetEventSlimfor login completion
Layer 3: Record query wrapper
Responsibilities:
- build an unmanaged query struct
- invoke
RSNetQueryRecord - collect returned
0x1centries into managed record models - expose them to higher-level range selection code
Layer 4: Download wrapper
Responsibilities:
- choose the target record entry or entries
- build the native download request and per-item metadata
- call
RSNetStartDownloadRecord - monitor status/progress callbacks
- resolve the final output file path
Layer 5: Cleanup
Responsibilities:
- call the native stop/cleanup entrypoints when known
- free all unmanaged strings and unmanaged buffers
- call
RSNetReleaseon process shutdown
What a minimal C# prototype should look like
The first prototype should not attempt the whole workflow at once.
Do it in stages:
- Load
RSNet.dllsuccessfully from C# in anx86process. - Call
RSNetInit()and verify it does not crash. - Call
RSNetConnectionStart(...)with a direct-IP request and log callback codes. - Confirm which callback code means login success.
- Add
RSNetQueryRecord(...)and dump raw0x1centries. - Only then attempt
RSNetStartDownloadRecord(...).
That staged approach is important because the unstable part is the native struct layout, not the export resolution.
Risks Specific To C# Interop
The main failure modes are:
- running as
AnyCPUorx64and failing to load the 32-bit DLL - incorrect calling convention
- incorrect struct packing
- wrong ANSI versus Unicode marshaling
- GC moving or collecting delegates or unmanaged buffers too early
- freeing unmanaged strings before the async worker is finished using them
- assuming login is synchronous when it is not
Operational rule:
- keep every unmanaged buffer and every callback delegate alive until the native session or download handle has definitively finished
Implementation Checklist For A C# Version
Before coding:
- confirm the C# host runs as
x86 - run from the vendor install folder or configure DLL search paths explicitly
- keep a copy of the vendor folder structure intact for first tests
During interop setup:
- use
StdCall - use
CharSet.Ansi - use
Pack = 4 - represent unknown native structs as
IntPtrfirst - keep callback delegates pinned by strong references
During login:
- prefer
RSNetConnectionStartoverRSNetConnectionStartEx - use direct IP and media port first
- do not assume immediate success after the function returns a non-null session handle
During record query:
- expect asynchronous or callback-driven result delivery
- log raw bytes for each
0x1crecord entry before trying to reinterpret every field semantically
During download:
- start with one record entry, not a batch
- expect completion via status event rather than via the return value alone
- inspect the output directory for the file the DLL creates
What Still Needs To Be Recovered For A Clean C# Implementation
The unresolved items are the same as for Python FFI, but C# can tolerate them if opaque pointers are used first.
The most important remaining tasks are:
- exact
RSNetConnectionStartrequest struct validation - exact query-request struct layout
- exact query callback prototype
- exact download-request struct layout
- exact per-item
0x108metadata layout - exact stop-download export signature
- exact mapping from numeric event codes to login/query/download states
Revised Assessment
Yes, a C# implementation is realistic.
It is probably one of the better options if the end goal is a Windows-only utility that uses the vendor binary as-is.
The current blocker is not can C# do it? but how much of the request structs do we want to strongly type before writing the prototype?
At this point, a cautious C# prototype is feasible if it:
- uses
x86 - calls the DLL with
StdCall - uses direct IP login first
- treats most request blobs as unmanaged buffers until the layouts are fully validated
Recommended first prototype flow
- Load
RSNet.dllwithctypes.WinDLL - Call
RSNetInit() - Optionally call
RSNetSetEncription(...)if required by the target device family - Build a direct-IP login struct for
RSNetConnectionStart - Wait until the async login callback reports success
- Build a record-query request for
RSNetQueryRecord - Collect returned
0x1crecord entries - Pick the entries intersecting the user-requested time range
- Build a single-entry download request and call
RSNetStartDownloadRecord - Wait for
RSNetMsgDownloadRecordOKor a terminal failure event - Locate the output file written by the DLL
- Release/cleanup handles
Why direct-IP first
The Ex login path clearly has extra logic for P2P or relay modes.
To keep the prototype small:
- use direct device IP / media port first
- defer P2P-only targets until after the basic downloader works
Best Current Guess At Minimal Script Design
Language
Python with ctypes is the fastest path.
C# is now a credible alternative for a Windows-only implementation that loads the vendor DLL directly.
Components
- DLL loader and function prototypes
- callback function declared with
ctypes.WINFUNCTYPE - login request struct for
RSNetConnectionStart - query request struct for
RSNetQueryRecord - query result callback handling
0x1centries - download request struct for
RSNetStartDownloadRecord - event loop or polling wait for download completion
Important caveat
The exact ctypes.Structure layouts are not fully proven yet.
So the first implementation should be treated as an iterative prototype, not a guaranteed drop-in script.
Still, the reverse engineering so far is enough to support the following plan:
- prototype direct login with
RSNetConnectionStart - verify callback codes for login success and failure
- prototype
RSNetQueryRecordwith a callback that logs each0x1cresult entry - once one real query works, use a single returned record entry as input to
RSNetStartDownloadRecord
That is now a tractable task.
Gaps That Still Matter Before Writing The Actual Script
These are the highest-value unresolved items:
- Exact layout of the
RSNetConnectionStartinput struct - Exact layout of the
RSNetQueryRecordrequest struct and callback prototype - Exact layout of the
RSNetStartDownloadRecordrequest struct, especially the destination-file metadata - Exact numeric callback codes corresponding to:
- login success
- login failure types
- query complete
- download progress
- download success
- download failure
- Exact output file naming and format-selection rules
Recommended Next Reverse-Engineering Steps
The next pass should focus on making the prototype script concrete:
-
Rename key
RSNet.dllinternals:FUN_100050a0FUN_100051f0FUN_10005ca0FUN_10005eb0FUN_10010330FUN_100103b0FUN_100105b0FUN_100074b0
-
Recover the exact login callback prototype from the session object fields around
+0x68,+0x6c,+0x70, and+0x78 -
Recover the exact query callback prototype used by result handlers
FUN_100046b0andFUN_10004750 -
Identify the structure at size
0x108associated with each download item -
Identify where the output file path is copied into the download-item metadata
-
Validate the call sequence against a real device using credentials
Current Assessment
The original objective was to analyze until it became possible to figure out how to make a simple script that downloads a video file.
That threshold has been reached in architectural terms:
- use
RSNet.dlldirectly - direct-IP login first
- query recordings first
- submit one record entry into the download API
- let the DLL write the file locally
- monitor callback/status events for completion
The remaining work is no longer “what is the path?” but “what are the exact struct layouts?”
That is a much narrower problem and should be solvable in a follow-up pass, especially with live credentials or one round of prototype-and-adjust testing.