home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 Mobile
/
Chip_Mobile_2001.iso
/
palm
/
system
/
_palmemu
/
palmemu.exe
/
Scripting
/
Perl
/
EmRPC.pm
< prev
next >
Wrap
Text File
|
2000-01-27
|
30KB
|
1,268 lines
########################################################################
#
# File: EmRPC.pm
#
# Purpose: Low-level functions for using RPC with the Palm OS
# Emulator.
#
# Description: This file contains base functions for using RPC:
#
# OpenConnection
# Opens a socket to the Emulator
#
# CloseConnection
# Closes the socket
#
# DoRPC
# Full-service RPC packet sending and receiving,
# including marshalling and unmarshalling of
# parameters.
#
# ReadBlock
# Read up to 256 bytes from the remote device's
# memory.
#
# WriteBlock
# Write up to 256 bytes to the remote device's
# memory.
#
# ReadString
# Read a C string from the remote device's memory.
#
# PrintString
# Debugging utility. Prints a Perl string
# (block of arbitrary data) as a hex dump.
#
########################################################################
package EmRPC;
use Exporter ();
@ISA = qw(Exporter);
@EXPORT_OK = qw(
OpenConnection CloseConnection
DoRPC
ReadBlock WriteBlock
ReadString PrintString
);
use IO::Socket;
use constant slkSocketDebugger => 0; # Debugger Socket
use constant slkSocketConsole => 1; # Console Socket
use constant slkSocketRemoteUI => 2; # Remote UI Socket
use constant slkSocketDLP => 3; # Desktop Link Socket
use constant slkSocketFirstDynamic => 4; # first dynamic socket ID
use constant slkSocketPoserRPC => 14;
use constant slkPktTypeSystem => 0; # System packets
use constant slkPktTypeUnused1 => 1; # used to be: Connection Manager packets
use constant slkPktTypePAD => 2; # PAD Protocol packets
use constant slkPktTypeLoopBackTest => 3; # Loop-back test packets
########################################################################
#
# FUNCTION: OpenConnection
#
# DESCRIPTION: Open a socket-based connection to Poser.
#
# PARAMETERS: Port number to talk to (hint, try 6415).
# IP address of computer to talk to. Can be null
# to use localhost.
#
# RETURNED: Nothing. Dies if fail to connect.
#
########################################################################
sub OpenConnection
{
($port_number, $ip_address) = @_;
if (not defined($sock))
{
if (defined($ip_address))
{
$remote = $ip_address;
}
else
{
$remote = "localhost";
}
$port = $port_number;
$sock = new IO::Socket::INET( PeerAddr => $remote,
PeerPort => $port,
Proto => 'tcp');
die "cannot connect to poser port. Reason: $@\n" unless $sock;
}
}
########################################################################
#
# FUNCTION: CloseConnection
#
# DESCRIPTION: Close the socket connection to Poser.
#
# PARAMETERS: None.
#
# RETURNED: Nothing.
#
########################################################################
sub CloseConnection
{
close ($sock);
undef $sock;
}
########################################################################
#
# FUNCTION: DoRPC
#
# DESCRIPTION: Performs full, round-trip RPC service.
#
# PARAMETERS: Trap word of function to call.
# Format string describing parameters.
# Parameters to pass in the RPC call.
#
# The format string contains a series of format
# descriptors. Descriptors must be seperated by
# some sort of delimiter, which can be a space, a
# common, a colon, or any combination of those. Each
# descriptor has the following format:
#
# <type><optional size><optional "*">
#
# The "type" describes the parameter in the format
# expected by the Palm OS. The RPC routines will
# convert the Perl variable corresponding to the
# parameter into the described type. The following
# types are supported:
#
# int: integer
# Err: 2 byte integer
# Coord: 2 byte integer;
# LocalID: 4 byte integer
# HostErr: 4 byte integer
# string: C string
# rptr: Pointer to something back on
# the emulated device
# point: Palm OS PointType
# rect: Palm OS RectangleType
# block: Block of arbitrary data
#
# Some format types can accept a size specifier
# after them. This size specifier is used when
# a default parameter size cannot be implied, or
# when you want to override the default parameter
# size. The following describes how the size
# specifier is handled for each parameter type:
#
# int:
# Length specifier must be supplied, and
# must be one of 8, 16, or 32.
#
# string:
# Default length is the value as returned
# by Perl's "length" function plus one.
# You can override this value by including
# your own length specifier.
#
# block:
# Default length is the value as returned
# by Perl's "length" function. You can
# override this value by including your
# own length specifier.
#
# all others:
# Any specified size is ignored.
#
# In general, integer types are passed by value, and
# all other types are passed by reference. That is
# after all the parameters are marhsalled, sent to
# the emulator, and unmarshalled, the "pass by value"
# parameters are pushed directly onto the emulated
# stack, and "pass by reference" parameters have
# their addresses pushed onto the stack. You can
# can change this behavior in one way: if you way
# an integer to be passed by reference, then you
# can append a "*" to its format specifier.
#
# Examples:
#
# "int16"
# Pass a 2 byte integer
#
# "int32 string"
# Pass a 4 byte integer, followed by
# a C-string
#
# "block32"
# Pass a 32 byte buffer, filling in
# its contents as much as possible
# with the given data
#
# "int16 in32 int32*"
# Pass a 2 byte integer, followed by
# a 4 byte integer, followed by a
# 4 byte integer passed by reference.
#
# RETURNED: List containing:
# Register D0
# Register A0
# Full parameter list. If any parameters were
# "pass by reference", you'll receive the
# updated parameters. If parameters are
# "pass by value", you'll get them back just
# the same way you provided them.
#
########################################################################
sub DoRPC
{
my ($trap_word, $format, @parameters) = @_;
my ($slkSocket) = slkSocketPoserRPC;
my ($slkPktType) = slkPktTypeSystem;
my ($send_body) = EmRPC::MakeRPCBody ($trap_word, $format, @parameters);
my ($packet) = MakePacket($slkSocket, $slkSocket, $slkPktType, $send_body);
SendPacket($packet);
my ($header, $body, $footer) = ReceivePacket();
EmRPC::UnmakeRPCBody ($body, $format);
}
########################################################################
#
# FUNCTION: ReturnValue
#
# DESCRIPTION: .
#
# PARAMETERS: .
#
# RETURNED: .
#
########################################################################
sub ReturnValue
{
my ($format, $D0, $A0, @parameters) = @_;
my ($type, $size, $by_ref) = GetFormat ($format, 0);
my ($result);
if ($type eq "int")
{
return $D0;
}
elsif ($type eq "string")
{
return ($A0, ReadString ($A0));
}
elsif ($type eq "rptr")
{
return $A0;
}
die "Unexpected type \"$type\" in EmRPC::ReturnValue, stopped";
}
########################################################################
#
# FUNCTION: ReadBlock
#
# DESCRIPTION: Read a range of memory from the remote device.
#
# PARAMETERS: address of remote device to start reading from.
# number of bytes to read (256 max).
#
# RETURNED: A Perl string containing the result.
#
########################################################################
$sysPktReadMemCmd = 0x01;
$sysPktReadMemRsp = 0x81;
# typedef struct SysPktReadMemCmdType {
# _sysPktBodyCommon; // Common Body header
# void* address; // Address to read
# Word numBytes; // # of bytes to read
# } SysPktReadMemCmdType;
# typedef SysPktReadMemCmdType* SysPktReadMemCmdPtr;
#
# typedef struct SysPktReadMemRspType {
# _sysPktBodyCommon; // Common Body header
# // Byte data[?]; // variable size
# } SysPktReadMemRspType;
# typedef SysPktReadMemRspType* SysPktReadMemRspPtr;
sub ReadBlock
{
my ($address, $num_bytes) = @_;
my ($slkSocket) = slkSocketPoserRPC;
my ($slkPktType) = slkPktTypeSystem;
my ($send_body) = pack ("cxNn", $sysPktReadMemCmd, $address, $num_bytes);
my ($packet) = MakePacket($slkSocket, $slkSocket, $slkPktType, $send_body);
SendPacket($packet);
my ($header, $body, $footer) = ReceivePacket();
unpack ("xx a$num_bytes", $body);
}
########################################################################
#
# FUNCTION: WriteBlock
#
# DESCRIPTION: Write a range of bytes to the remote device.
#
# PARAMETERS: address to start writing to.
# a Perl string containing the stuff to write.
#
# RETURNED: nothing
#
########################################################################
$sysPktWriteMemCmd = 0x02;
$sysPktWriteMemRsp = 0x82;
# typedef struct SysPktWriteMemCmdType {
# _sysPktBodyCommon; // Common Body header
# void* address; // Address to write
# Word numBytes; // # of bytes to write
# // Byte data[?]; // variable size data
# } SysPktWriteMemCmdType;
# typedef SysPktWriteMemCmdType* SysPktWriteMemCmdPtr;
#
# typedef struct SysPktWriteMemRspType {
# _sysPktBodyCommon; // Common Body header
# } SysPktWriteMemRspType;
# typedef SysPktWriteMemRspType* SysPktWriteMemRspPtr;
sub WriteBlock
{
my ($address, $data) = @_;
my ($slkSocket) = slkSocketPoserRPC;
my ($slkPktType) = slkPktTypeSystem;
my ($send_body) = pack ("cxNn", $sysPktWriteMemCmd, $address, length ($data)) . $data;
my ($packet) = MakePacket($slkSocket, $slkSocket, $slkPktType, $send_body);
SendPacket($packet);
ReceivePacket(); # receive the results, but we don't need to do anything with them
}
########################################################################
#
# FUNCTION: SendPacket
#
# DESCRIPTION: Send a fully-built packet to Poser. The socket
# connection to Poser should already have been
# established
#
# PARAMETERS: The packet to be sent.
#
# RETURNED: Nothing.
#
########################################################################
sub SendPacket
{
my ($packet) = @_;
print $sock $packet;
}
########################################################################
#
# FUNCTION: ReceivePacket
#
# DESCRIPTION: Receive a packet from Poser.
#
# PARAMETERS: None.
#
# RETURNED: The packet header, body, and footer as an array.
#
########################################################################
sub ReceivePacket
{
my ($header, $body, $footer);
my ($header_length) = 10;
sysread($sock, $header, $header_length);
my ($body_length) = GetBodySize($header);
sysread($sock, $body, $body_length);
my ($footer_length) = 2;
sysread($sock, $footer, $footer_length);
($header, $body, $footer);
}
########################################################################
#
# FUNCTION: MakePacket
#
# DESCRIPTION: Builds up a complete packet for sending to Poser
# including the header, body, and footer.
#
# PARAMETERS: $src - the source SLP socket. Generally something
# like slkSocketDebugger or slkSocketConsole.
#
# $dest - the destination SLP socket.
#
# $type - the type of packet. Generally something
# like slkPktTypeSystem or slkPktTypePAD.
#
# $body - the body of the packet.
#
# RETURNED: The built packet as a Perl string. The header and
# footer checksums will be calculated and filled in.
#
########################################################################
# struct SlkPktHeaderType
# {
# Word signature1; // X first 2 bytes of signature
# Byte signature2; // X 3 and final byte of signature
# Byte dest; // -> destination socket Id
# Byte src; // -> src socket Id
# Byte type; // -> packet type
# Word bodySize; // X size of body
# Byte transID; // -> transaction Id
# // if 0 specified, it will be replaced
# SlkPktHeaderChecksum checksum; // X check sum of header
# };
#
# struct SlkPktFooterType
# {
# Word crc16; // header and body crc
# };
$header_template = "H6CCCnCC"; # 6 Hex digits, 3 unsigned chars, a B.E. short, 2 unsigned chars
$footer_template = "n"; # a B.E. short
$signature = "BEEFED";
sub MakePacket
{
my ($src, $dest, $type, $body) = @_;
if (not defined($transID))
{
$transID = 0;
}
++$transID;
my ($bodySize) = length ($body);
my ($header_checksum) = CalcHeaderChecksum ($signature, $dest, $src, $type, $bodySize, $transID);
my ($header) = pack ($header_template, $signature, $dest, $src, $type, $bodySize, $transID, $header_checksum);
# my ($footer_checksum) = CalcFooterChecksum ($header, &body);
my ($footer_checksum) = 0;
my ($footer) = pack ($footer_template, $footer_checksum);
$header . $body . $footer;
}
########################################################################
#
# FUNCTION: CalcHeaderChecksum
#
# DESCRIPTION: Calculate that checksum value for the packet header.
#
# PARAMETERS: The components of the header.
#
# RETURNED: The checksum that should be placed in the SLP
# packet header.
#
########################################################################
sub CalcHeaderChecksum
{
my ($signature, $dest, $src, $type, $bodySize, $transID) = @_;
my ($checksum, $temp_buffer);
$checksum = 0;
$temp_buffer = pack ($header_template, $signature, $dest, $src, $type, $bodySize, $transID, 0);
@bytes = unpack("C8", $temp_buffer);
$checksum = $bytes[0] + $bytes[1] + $bytes[2] + $bytes[3] + $bytes[4] +
$bytes[5] + $bytes[6] + $bytes[7];
$checksum % 256;
}
########################################################################
#
# FUNCTION: CalcFooterChecksum
#
# DESCRIPTION: Calculate the checksum value for the packet footer.
#
# PARAMETERS: The header and body.
#
# RETURNED: The checksum that should be placed in the SLP
# packet footer.
#
########################################################################
sub CalcFooterChecksum
{
my ($header, $body) = @_;
my ($checksum, $temp_buffer);
$temp_buffer = $header . $body;
$checksum = unpack("%16c*", $temp_buffer); # Wrong kind of checksum!
}
########################################################################
#
# FUNCTION: MakeRPCBody
#
# DESCRIPTION: Create the body of an RPC packet, suitable for
# being passed off to MakePacket.
#
# PARAMETERS: The "trap word" of the trap that needs to be called
# (as defined by the constants in SysTraps.pm) and
# the parameters of the RPC call, as created by the
# MakeParam function.
#
# RETURNED: The body of the packet as a string.
#
########################################################################
# struct SysPktRPCType
# {
# _sysPktBodyCommon; // Common Body header
# Word trapWord; // which trap to execute
# DWord resultD0; // result from D0 placed here
# DWord resultA0; // result from A0 placed here
# Word numParams; // how many parameters follow
# // Following is a variable length array ofSlkRPCParamInfo's
# SysPktRPCParamType param[1];
# };
$rpc_header_template = "CxH4NNn"; # unsigned byte, filler, 4 hex digits, 2 B.E. longs, B.E. short
$sysPktRPCCmd = 0x0A;
$sysPktRPCRsp = 0x8A;
sub MakeRPCBody
{
my ($trapword, $format, @param_list) = @_;
my ($rpc_header) = pack ($rpc_header_template, $sysPktRPCCmd, $trapword, 0, 0, $#param_list + 1);
my ($rpc_body) = join ("", $rpc_header, Marshal($format, @param_list));
$rpc_body;
}
sub UnmakeRPCBody
{
my ($body, $format) = @_;
my ($cmd, $trap_word, $D0, $A0, $num_params, $packed_parms) = unpack ("$rpc_header_template a*", $body);
my (@parms) = Unmarshal($packed_parms, $format);
return ($D0, $A0, @parms);
}
$rpc2_header_template = "CxH4NNN"; # unsigned byte, filler, 4 hex digits, 3 B.E. longs
$sysPktRPC2Cmd = 0x20;
$sysPktRPC2Rsp = 0xA0;
sub MakeRPC2Body
{
my ($trapword, $reg_list, @param_list) = @_;
my ($rpc_header) = pack ($rpc_header_template, $sysPktRPCCmd, $trapword, 0, 0, 0);
my ($param_count) = pack ("n", $#param_list + 1);
my ($rpc_body) = join ("", $rpc_header, $reg_list, $param_count, reverse @param_list);
$rpc_body;
}
########################################################################
#
# FUNCTION: PackRegList
#
# DESCRIPTION: Pack a list of register values into the format
# needed by an RPC2 packet.
#
# PARAMETERS: An associative array, where each key contains Ax
# or Dx, and the value contains the register value.
#
# RETURNED: The packed registers as a string.
#
########################################################################
sub PackRegList
{
my (%reg_list) = @_;
my ($dreg_bits, $areg_bits, $dregs, $aregs);
$dreg_bits = 0;
$areg_bits = 0;
$dregs = "";
$aregs = "";
foreach $key (sort keys %reg_list)
{
my($reg_space) = substr($key, 0, 1);
my($bit_to_set) = (1 << (ord(substr($key, 1, 1)) - ord("0")));
my($value) = $reg_list{$key};
if ($reg_space eq "D")
{
$dreg_bits |= $bit_to_set;
$dregs .= pack ("N", $value);
}
else
{
$areg_bits |= $bit_to_set;
$aregs .= pack ("N", $value);
}
}
my ($result) = join ("", pack("CC", $dreg_bits, $areg_bits), $dregs, $aregs);
}
########################################################################
#
# FUNCTION: MakeParam
#
# DESCRIPTION: Create a parameter array element, suitable for being
# added to other parameter array elements and --
# eventually -- to an RPC packet body.
#
# PARAMETERS: $data - the data to be added.
# $data_len - the length of the data to be added. If
# greater than zero, then we assume $data to be
# an integer. If equal to zero, then we assume
# data to be a string where the length of the
# string is determined by the length () function.
# If less than zero, then data is assumed to be
# a buffer with a length of -$data.
# $by_ref - zero if the parameter is to be treated as
# pass-by-value. Non-zero if it's pass-by-ref.
#
# RETURNED: A parameter string that can be appended to a longer
# string of parameters. If the length of the string
# would otherwise be odd, a padding byte is added.
#
########################################################################
# struct SysPktRPCParamInfo
# {
# Byte byRef; // true if param is by reference
# Byte size; // # of Bytes of paramData (must be even)
# Word data[1]; // variable length array of paramData
# };
sub ToParamBlock
{
my ($data, $data_len) = @_;
die "Undefined \$data, stopped" unless defined($data);
die "\$data_len is negative, stopped" if ($data_len < 0);
## If data_len == 0, determine the length using the length () function.
## Else, use the given length.
if ($data_len == 0)
{
$data_len = length ($data);
}
else
{
$data = pack ("a$data_len", $data);
}
## Pack up the data.
my ($param) = pack ("CC", 1, $data_len) . $data;
## Make sure the packed data is an even number of bytes long.
if (($data_len % 2) != 0)
{
$param .= "\0";
}
$param;
}
sub FromParamBlock
{
my ($param, $data_len) = @_;
die "Undefined \$param, stopped" unless defined($param);
die "\$data_len is negative, stopped" if ($data_len < 0);
## Just ignore the $data_len and use what's in the parameter block.
$data_len = unpack ("xC", $param);
unpack ("xxa$data_len", $param);
}
sub ToParamInt
{
my ($data, $data_len, $by_ref) = @_;
die "Undefined \$data, stopped" unless defined($data);
my ($format);
if ($data_len == 8)
{
$format = ("CCCx");
$data_len = 1;
}
elsif ($data_len == 16 || $data_len == 0)
{
$format = ("CCn");
$data_len = 2;
}
elsif ($data_len == 32)
{
$format = ("CCN");
$data_len = 4;
}
else
{
die "\$data_len not 8, 16, or 32, stopped";
}
## Pack up the data.
pack ($format, $by_ref, $data_len, $data);
}
sub FromParamInt
{
my ($param, $data_len) = @_;
die "Undefined \$param, stopped" unless defined($param);
my ($format);
if ($data_len == 8)
{
$format = ("xxCx");
}
elsif ($data_len == 16 || $data_len == 0)
{
$format = ("xxn");
}
elsif ($data_len == 32)
{
$format = ("xxN");
}
else
{
die "\$data_len not 8, 16, or 32, stopped";
}
unpack ($format, $param);
}
sub ToParamPoint
{
my ($point) = @_;
my ($param);
if (defined $point->{x})
{
$param = pack ("CCnn", 1, 4, $point->{x}, $point->{y});
}
else
{
$param = pack ("CCxxxx", 1, 4);
}
$param;
}
sub FromParamPoint
{
my ($param) = @_;
die "Undefined \$param, stopped" unless defined($param);
my (@coords) = unpack ("xxnn", $param);
{x => $coords[0],
y => $coords[1]};
}
sub ToParamRect
{
my ($rect) = @_;
my ($param);
if (defined $rect->{height})
{
$param = pack ("CCnnnn", 1, 8, $rect->{left}, $rect->{top}, $rect->{width}, $rect->{height});
}
elsif (defined $rect->{bottom})
{
$param = pack ("CCnnnn", 1, 8, $rect->{left}, $rect->{top}, $rect->{right} - $rect->{left}, $rect->{bottom} - $rect->{top});
}
else
{
$param = pack ("CCxxxxxxxx", 1, 8);
}
$param;
}
sub FromParamRect
{
my ($param) = @_;
die "Undefined \$param, stopped" unless defined($param);
my (@coords) = unpack ("xxnnnn", $param);
{left => $coords[0],
top => $coords[1],
width => $coords[2],
height => $coords[3],
right => $coords[0] + $coords[2],
bottom => $coords[1] + $coords[3]};
}
sub ToParamString
{
my ($data, $data_len) = @_;
die "Undefined \$data, stopped" unless defined($data);
die "\$data_len is negative, stopped" if ($data_len < 0);
## If $data_len == 0, determine the length using the length () function.
if ($data_len == 0)
{
$data_len = length ($data) + 1; # Add 1 to get 1 byte of NULL padding
}
## Pack up the data.
my ($param) = pack ("CCa$data_len", 1, $data_len, $data);
## Make sure the packed data is an even number of bytes long.
if (($data_len % 2) != 0)
{
$param .= "\0";
}
$param;
}
sub FromParamString
{
my ($param) = @_;
unpack ("xxA*", $param);
}
########################################################################
#
# FUNCTION: UnpackHeader
#
# DESCRIPTION: Disassemble a packet header into its consituent
# parts.
#
# PARAMETERS: The packet header as received from Poser
#
# RETURNED: The signature, destination port, source port,
# packet type, body size, transaction ID, and
# checksum as an array.
#
########################################################################
sub UnpackHeader
{
my($header) = @_;
my ($signature, $dest, $src, $type, $bodySize, $transID, $checksum)
= unpack ($header_template, $header);
($signature, $dest, $src, $type, $bodySize, $transID, $checksum);
}
########################################################################
#
# FUNCTION: GetBodySize
#
# DESCRIPTION: Utility function to extract the packet body size
# field from the packet header.
#
# PARAMETERS: The packet header as received from Poser.
#
# RETURNED: The size of the body following the header.
#
########################################################################
sub GetBodySize
{
my($header) = @_;
my ($signature, $dest, $srs, $type, $bodySize, $transID, $checksum)
= UnpackHeader ($header);
$bodySize;
}
sub SkipWhite
{
my ($format, $format_index) = @_;
while ()
{
last if ($format_index >= length ($format));
my ($char) = substr ($format, $format_index, 1);
last unless ($char eq " " || $char eq "," || $char eq ":");
$format_index += 1;
}
$format_index
}
sub GetType
{
my ($format, $format_index) = @_;
my ($type) = "";
$format_index = SkipWhite ($format, $format_index);
while ()
{
last if ($format_index >= length ($format));
my ($char) = substr ($format, $format_index, 1);
last if (($char lt "a" || $char gt "z") && ($char lt "A" || $char gt "Z"));
$type .= $char;
$format_index += 1;
}
die "Unknown type (\"$type\" @ $format_index), stopped"
unless ($type eq "int" ||
$type eq "Err" ||
$type eq "Coord" ||
$type eq "LocalID" ||
$type eq "HostErr" ||
$type eq "string" ||
$type eq "rptr" ||
$type eq "point" ||
$type eq "rect" ||
$type eq "block");
return ($type, $format_index);
}
sub GetSize
{
my ($format, $format_index) = @_;
my ($size) = 0;
while ()
{
last if ($format_index >= length ($format));
my ($char) = substr ($format, $format_index, 1);
last if ($char lt "0" || $char gt "9");
$size = $size * 10 + $char;
$format_index += 1;
}
return ($size, $format_index);
}
sub GetByRef
{
my ($format, $format_index) = @_;
my ($by_ref) = 0;
if (substr ($format, $format_index, 1) eq "*")
{
$by_ref = 1;
}
if ($by_ref)
{
$format_index += 1;
}
return ($by_ref, $format_index);
}
sub GetFormat
{
my ($format, $format_index) = @_;
my ($type, $size, $by_ref) = (" ", 0, 0);
($type, $format_index) = GetType ($format, $format_index);
($size, $format_index) = GetSize ($format, $format_index);
($by_ref, $format_index) = GetByRef ($format, $format_index);
## Deal with aliases
if ($type eq "LocalID" or $type eq "HostErr")
{
$type = "int";
$size = 32;
}
elsif ($type eq "Err" or $type eq "Coord")
{
$type = "int";
$size = 16;
}
return ($type, $size, $by_ref, $format_index);
}
sub Marshal
{
my ($format, @parameters) = @_;
my (@result);
my ($format_index) = 0;
my ($parameter_index) = 0;
while ($format_index < length ($format))
{
my ($parm);
my ($type, $size);
($type, $size, $by_ref, $format_index) = GetFormat ($format, $format_index);
if ($type eq "int")
{
$parm = EmRPC::ToParamInt($parameters[$parameter_index], $size, $by_ref);
}
elsif ($type eq "rptr")
{
$parm = EmRPC::ToParamInt($parameters[$parameter_index], 32, 0);
}
elsif ($type eq "point")
{
$parm = EmRPC::ToParamPoint($parameters[$parameter_index], $size);
}
elsif ($type eq "rect")
{
$parm = EmRPC::ToParamRect($parameters[$parameter_index], $size);
}
elsif ($type eq "string")
{
$parm = EmRPC::ToParamString($parameters[$parameter_index], $size);
}
elsif ($type eq "block")
{
$parm = EmRPC::ToParamBlock($parameters[$parameter_index], $size);
}
else
{
die "Unexpected type \"$type\" in EmRPC::Marshal, stopped";
}
push (@result, $parm);
$parameter_index += 1;
}
return join ("", reverse @result);
}
sub BreakApartParameters
{
my ($packed_parms) = @_;
my (@result) = 0;
my ($offset) = 0;
while ($offset < length ($packed_parms))
{
# Get the size field.
my ($size) = unpack ("x$offset" . "xC", $packed_parms);
# Add in the lengths of the byRef and size fields.
$size += 2;
# Make sure the field is word-aligned.
if (($size % 2) != 0)
{
$size += 1;
}
# Get the SysPktRPCParamInfo.
my ($parm) = unpack ("x$offset a$size", $packed_parms);
push (@result, $parm);
$offset += $size;
}
return @result;
}
sub Unmarshal
{
my ($packed_parms, $format) = @_;
my (@result);
my ($format_index) = 0;
my ($parameter_index) = 0;
my (@parameters) = reverse BreakApartParameters($packed_parms);
while ($format_index < length ($format))
{
my ($parm);
my ($type, $size);
($type, $size, $by_ref, $format_index) = GetFormat ($format, $format_index);
if ($type eq "int")
{
$parm = EmRPC::FromParamInt($parameters[$parameter_index], $size);
}
elsif ($type eq "rptr")
{
$parm = EmRPC::FromParamInt($parameters[$parameter_index], 32);
}
elsif ($type eq "point")
{
$parm = EmRPC::FromParamPoint($parameters[$parameter_index]);
}
elsif ($type eq "rect")
{
$parm = EmRPC::FromParamRect($parameters[$parameter_index]);
}
elsif ($type eq "string")
{
$parm = EmRPC::FromParamString($parameters[$parameter_index]);
}
elsif ($type eq "block")
{
$parm = EmRPC::FromParamBlock($parameters[$parameter_index], $size);
}
else
{
die "Unexpected type \"$type\" in EmRPC::Unmarshal, stopped";
}
push (@result, $parm);
$parameter_index += 1;
}
return @result;
}
sub ReadString
{
my ($address) = @_;
my ($block) = EmRPC::ReadBlock($address, 128);
my ($string, $ch);
foreach $ii (0..length ($block) - 1)
{
$ch = substr($block, $ii, 1);
last if (ord($ch) == 0);
$string .= $ch;
}
$string;
}
sub PrintString
{
my($string) = @_;
foreach $ii (0..length ($string) - 1)
{
my($ch) = substr($string, $ii, 1);
printf "0x%02X, ", ord($ch);
if ($ii % 8 == 7)
{
print "\n";
}
}
printf "\n";
$string;
}
1;