mpw/toolbox/rm-new.cpp
2016-08-02 13:46:18 -04:00

992 lines
22 KiB
C++

/*
* Copyright (c) 2016, Kelvin W Sherlock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "rm.h"
#include "mm.h"
#include "toolbox.h"
#include <vector>
#include <algorithm>
#include <string>
#include <algorithm>
#include <memory>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <cpu/defs.h>
#include <cpu/CpuModule.h>
#include <cpu/fmem.h>
#include <macos/errors.h>
#include <macos/tool_return.h>
#include <macos/sysequ.h>
#include <native/native.h>
#include "stackframe.h"
#include "fs_spec.h"
/*
* todo -- iterators for the resource map?
*/
/*
* refNum 0 = system resource
* refNum 1 = ROM resource.
*/
using namespace MacOS;
using ToolBox::Log;
using ToolBox::TypeToString;
inline constexpr uint32_t read_32(const uint8_t *cp) {
return (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | (cp[3] << 0);
}
inline constexpr uint32_t read_24(const uint8_t *cp) {
return (cp[0] << 16) | (cp[1] << 8) | (cp[2] << 0);
}
inline constexpr uint16_t read_16(const uint8_t *cp) {
return (cp[0] << 8) | (cp[1] << 0);
}
inline constexpr uint8_t read_8(const uint8_t *cp) {
return cp[0];
}
inline void check_size(const std::vector<uint8_t> v, size_t size) {
if (size > v.size()) throw std::out_of_range("check_size");
}
inline void check_offset(const std::vector<uint8_t> v, size_t offset) {
if (offset >= v.size()) throw std::out_of_range("check_offset");
}
inline uint32_t read_32(const std::vector<uint8_t> v, size_t offset, const std::nothrow_t &) noexcept {
return (v[offset+0] << 24) | (v[offset+1] << 16) | (v[offset+2] << 8) | (v[offset+3] << 0);
}
inline uint32_t read_24(const std::vector<uint8_t> v, size_t offset, const std::nothrow_t &) noexcept {
return (v[offset+0] << 16) | (v[offset+1] << 8) | (v[offset+2] << 0);
}
inline uint32_t read_16(const std::vector<uint8_t> v, size_t offset, const std::nothrow_t &) noexcept {
return (v[offset+0] << 8) | (v[offset+1] << 0);
}
inline uint8_t read_8(const std::vector<uint8_t> v, size_t offset, const std::nothrow_t &) noexcept {
return v[offset+0];
}
inline uint32_t read_32(const std::vector<uint8_t> v, size_t offset) {
check_size(v, offset + 4);
return read_32(v, offset, std::nothrow);
}
inline uint32_t read_24(const std::vector<uint8_t> v, size_t offset) {
check_size(v, offset + 3);
return read_24(v, offset, std::nothrow);
}
inline uint32_t read_16(const std::vector<uint8_t> v, size_t offset) {
check_size(v, offset + 2);
return read_16(v, offset, std::nothrow);
}
inline uint8_t read_8(const std::vector<uint8_t> v, size_t offset) {
return v.at(offset+0);
}
inline std::string read_string(const std::vector<uint8_t> v, size_t offset, const std::nothrow_t &) noexcept {
int length = v[offset + 0];
return std::string((const char *)v.data() + offset + 1, length);
}
inline std::string read_string(const std::vector<uint8_t> v, size_t offset) {
int length = v.at(offset + 0);
check_size(v, offset + length + 1);
return std::string((const char *)v.data() + offset + 1, length);
}
enum {
resSysHeap = 64,
resPurgeable = 32,
resLocked = 16,
resProtected = 8,
resPreload = 4,
resChanged = 2,
};
struct resource {
uint32_t type = 0;
uint16_t id = 0;
uint16_t attr = 0;
uint32_t data_offset = 0;
//uint32_t name_offset = 0;
uint32_t size = -1;
uint32_t handle = 0;
std::string name;
};
struct resource_file {
resource_file *next = nullptr;
int fd = -1;
int refnum = -1;
bool readonly = true;
// header
uint32_t offset_rdata = 0; /* offset to resource data */
uint32_t offset_rmap = 0; /* offset to resource map */
uint32_t length_rdata = 0; /* length of resource data */
uint32_t length_rmap = 0; /* length of resource map */
// map
uint16_t attr = 0;
std::vector<resource> resources;
resource_file() = default;
resource_file(resource_file &&) = delete;
resource_file(const resource_file &) = delete;
resource_file &operator=(const resource_file &) = delete;
resource_file &operator=(resource_file &&) = delete;
~resource_file() {
if (fd >= 0) close(fd);
}
};
bool operator<(const resource &lhs, const resource &rhs) {
return (lhs.type < rhs.type) || (lhs.type == rhs.type && lhs.id < rhs.id);
}
namespace {
int refnum = 100;
bool res_load = true;
resource_file *current = nullptr;
resource_file *head = nullptr;
tool_return<void> read_resource_map(resource_file &rf) {
ssize_t ok;
uint8_t header[100];
ok = read(rf.fd, header, sizeof(header));
if (ok != sizeof(header)) return mapReadErr;
rf.offset_rdata = read_32(header+0);
rf.offset_rmap = read_32(header+4);
rf.length_rdata = read_32(header+8);
rf.length_rmap = read_32(header+12);
if (rf.length_rmap < 30) return mapReadErr;
std::vector<uint8_t> rmap(rf.length_rmap);
if (lseek(rf.fd, rf.offset_rmap, SEEK_SET) < 0) return macos_error_from_errno();
ok = read(rf.fd, rmap.data(), rf.length_rmap);
if (ok != rf.length_rmap) return mapReadErr;
rf.attr = read_16(rmap, 22, std::nothrow);
uint16_t offset_type_list = read_16(rmap, 24, std::nothrow) + 2;
uint16_t offset_name_list = read_16(rmap, 26, std::nothrow);
int16_t count = read_16(rmap, 28, std::nothrow);
if (offset_type_list < 30) return mapReadErr;
if (offset_name_list < 30) return mapReadErr;
if (offset_type_list > rf.length_rmap) return mapReadErr;
if (offset_name_list > rf.length_rmap) return mapReadErr;
try {
unsigned tl_offset = offset_type_list;
check_size(rmap, tl_offset + (count + 1) * 8);
while (count-- >= 0) {
struct resource r;
r.type = read_32(rmap, tl_offset + 0, std::nothrow);
int16_t count = read_16(rmap, tl_offset + 4, std::nothrow);
unsigned rl_offset = offset_type_list + read_16(rmap, tl_offset + 6, std::nothrow);
check_size(rmap, rl_offset + (count + 1) * 12);
while (count-- >= 0) {
r.id = read_16(rmap, rl_offset + 0, std::nothrow);
unsigned nl_offset = read_16(rmap, rl_offset + 2, std::nothrow);
r.attr = read_8(rmap, rl_offset + 4, std::nothrow);
r.data_offset = read_24(rmap, rl_offset + 5, std::nothrow);
// 4 bytes reserved.
r.name.clear();
if (nl_offset != 0xffff) {
r.name = read_string(rmap, nl_offset + offset_name_list);
}
rf.resources.emplace_back(std::move(r));
rl_offset += 12;
}
tl_offset += 8;
}
} catch (std::out_of_range &ex) {
return mapReadErr;
}
std::sort(rf.resources.begin(), rf.resources.end());
return noErr;
}
tool_return<uint32_t> read_resource(resource_file &rf, resource &r) {
ssize_t ok;
if (r.handle) return r.handle;
if (!res_load) {
/* create a null handle */
auto hh = MM::Native::NewHandle();
if (hh.error()) return hh.error();
r.handle = *hh;
return *hh;
}
if (r.size == -1) {
uint8_t buffer[4];
if (lseek(rf.fd, rf.offset_rdata + r.data_offset, SEEK_SET) < 0)
return macos_error_from_errno();
ok = read(rf.fd, buffer, sizeof(buffer));
if (ok != 4) return badExtResource;
r.size = read_32(buffer);
} else {
if (lseek(rf.fd, rf.offset_rdata + r.data_offset + 4, SEEK_SET) < 0)
return macos_error_from_errno();
}
auto hh = MM::Native::NewHandle(r.size, false);
if (hh.error()) return hh.error();
MM::Native::HSetRBit(hh->handle);
ok = read(rf.fd, memoryPointer(hh->pointer), r.size);
if (ok < 0) {
auto rv = macos_error_from_errno();
MM::Native::DisposeHandle(hh->handle);
return rv;
}
if (ok != r.size) {
MM::Native::DisposeHandle(hh->handle);
return badExtResource;
}
r.handle = hh->handle;
return hh->handle;
}
inline macos_error SetResError(uint16_t error)
{
memoryWriteWord(error, MacOS::ResErr);
return (macos_error)error;
}
};
namespace RM {
#pragma mark - Native
namespace Native {
tool_return<int16_t> OpenResFile(const std::string &path_name, uint16_t perm) {
int fd = native::open_resource_fork(path_name, O_RDONLY);
if (fd < 0) return macos_error_from_errno();
auto rf = std::make_unique<resource_file>();
rf->fd = fd;
rf->readonly = true;
auto rv = read_resource_map(*rf);
if (rv.error()) return rv.error();
rf->refnum = ++refnum;
rf->next = head;
head = rf.release();
current = head;
return refnum;
}
tool_return<void> UpdateResFile(resource_file &rf)
{
return noErr;
}
tool_return<void> CloseResFile(uint16_t refNum) {
resource_file *rf = head;
resource_file *prev = nullptr;
while (rf) {
if (rf->refnum == refNum) break;
prev = rf;
rf = rf->next;
}
if (!rf) return SetResError(MacOS::resFNotFound);
auto rv = UpdateResFile(*rf);
for (auto &r : rf->resources) {
if (r.handle) MM::Native::DisposeHandle(r.handle);
}
if (prev) prev->next = rf->next;
else head = rf->next;
if (current == rf) current = rf->next;
delete rf;
return rv.error();
}
tool_return<void> SetResLoad(bool load)
{
res_load = load;
memoryWriteByte(load ? 0xff : 0x00, MacOS::ResLoad); // word or byte?
return SetResError(MacOS::noErr);
}
tool_return<uint32_t> Get1Resource(resource_file &rf, uint32_t type, uint16_t id) {
auto &resources = rf.resources;
auto iter = std::lower_bound(resources.begin(), resources.end(), 0,
[type,id](const resource &lhs, int rhs){
if (lhs.type < type) return true;
if (lhs.type == type && lhs.id < id) return true;
return false;
});
if (iter == resources.end())
return SetResError(MacOS::resNotFound);
auto &r = *iter;
if (r.type != type || r.id != id)
return SetResError(MacOS::resNotFound);
auto rv = read_resource(rf, r);
SetResError(rv.error());
return rv;
}
tool_return<uint32_t> Get1NamedResource(resource_file &rf, uint32_t type, const std::string &name) {
auto &resources = rf.resources;
auto iter = std::lower_bound(resources.begin(), resources.end(), 0,
[type](const resource &lhs, int rhs){
if (lhs.type < type) return true;
return false;
});
if (iter == resources.end())
return SetResError(MacOS::resNotFound);
for (; iter != resources.end(); ++iter) {
auto &r = *iter;
if (r.type != type) break;
if (r.name == name) {
auto rv = read_resource(rf, r);
SetResError(rv.error());
return rv;
}
}
return SetResError(MacOS::resNotFound);
}
tool_return<uint32_t> Get1Resource(uint32_t type, uint16_t id) {
resource_file *rf = current;
if (rf) return Get1Resource(*rf, type, id);
return SetResError(MacOS::resNotFound);
}
tool_return<uint32_t> Get1NamedResource(uint32_t type, const std::string &name) {
resource_file *rf = current;
if (rf) return Get1NamedResource(*rf, type, name);
return SetResError(MacOS::resNotFound);
}
tool_return<uint32_t> GetResource(uint32_t type, uint16_t id) {
for (auto rf = current; rf; rf = rf->next) {
auto rv = Get1Resource(*rf, type, id);
if (!rv.error()) return rv;
}
return SetResError(MacOS::resNotFound);
}
tool_return<uint32_t> GetNamedResource(uint32_t type, const std::string &name) {
for (auto rf = current; rf; rf = rf->next) {
auto rv = Get1NamedResource(*rf, type, name);
if (!rv.error()) return rv;
}
return SetResError(MacOS::resNotFound);
}
} // Native
uint16_t CurResFile(uint16_t trap)
{
Log("%04x CurResFile()\n", trap);
uint16_t refNum = 0xffff;
if (current) refNum = current->refnum;
ToolReturn<2>(-1, refNum);
return SetResError(refNum == 0xffff ? resFNotFound : noErr);
}
uint16_t UseResFile(uint16_t trap)
{
uint16_t refNum;
StackFrame<2>(refNum);
Log("%04x UseResFile(%04x)\n", trap, refNum);
// todo -- 0 is the system resource file.
resource_file *rf;
for (rf = head; rf; rf = rf->next) {
if (rf->refnum == refNum) break;
}
if (rf) current = rf;
return SetResError(rf ? noErr : resFNotFound);
}
uint16_t GetResInfo(uint16_t trap)
{
// PROCEDURE GetResInfo (theResource: Handle;
// VAR theID: INTEGER; VAR theType: ResType; VAR name: Str255);
uint32_t theResource;
uint32_t theID;
uint32_t theType;
uint32_t name;
StackFrame<16>(theResource, theID, theType, name);
Log("%04x GetResInfo(%08x)\n", trap, theResource);
if (!theResource) return SetResError(resNotFound);
for (resource_file *rf = head; rf; rf = rf->next) {
for (const auto &r : rf->resources) {
if (r.handle == theResource) {
if (theID) memoryWriteWord(r.id, theID);
if (theType) memoryWriteLong(r.type, theType);
if (name) ToolBox::WritePString(name, r.name);
return SetResError(noErr);
}
}
}
return SetResError(resNotFound);
}
uint16_t GetResAttrs(uint16_t trap)
{
// FUNCTION GetResAttrs (theResource: Handle): Integer;
uint32_t sp;
uint32_t theResource;
sp = StackFrame<4>(theResource);
Log("%04x GetResAttrs(%08x)\n", trap, theResource);
if (!theResource) return SetResError(resNotFound);
for (resource_file *rf = head; rf; rf = rf->next) {
for (const auto &r : rf->resources) {
if (r.handle == theResource) {
ToolReturn<2>(sp, r.attr);
return SetResError(noErr);
}
}
}
return SetResError(resNotFound);
}
uint16_t DetachResource(uint16_t trap)
{
// PROCEDURE DetachResource (theResource: Handle);
uint32_t theResource;
StackFrame<4>(theResource);
Log("%04x DetachResource(%08x)\n", trap, theResource);
if (!theResource) return SetResError(resNotFound);
for (resource_file *rf = head; rf; rf = rf->next) {
for (auto &r : rf->resources) {
if (r.handle == theResource) {
/*
* DetachResource won't let you detach a resource whose resChanged
* attribute has been set; however, ResError will still return noErr.
*/
if ((r.attr & resChanged) == 0) {
r.handle = 0;
MM::Native::HClrRBit(theResource);
}
return SetResError(noErr);
}
}
}
return SetResError(resNotFound);
}
uint16_t ReleaseResource(uint16_t trap)
{
// ReleaseResource (theResource: Handle);
/*
* ------------
* +0 theResource
* ------------
*
*/
uint32_t sp;
uint32_t theResource;
sp = StackFrame<4>(theResource);
Log("%04x ReleaseResource(%08x)\n", trap, theResource);
if (!theResource) return SetResError(resNotFound);
for (resource_file *rf = head; rf; rf = rf->next) {
for (auto &r : rf->resources) {
if (r.handle == theResource) {
/*
* ReleaseResource won't let you release a resource whose resChanged
* attribute has been set; however, ResError will still return noErr.
*/
if ((r.attr & resChanged) == 0) {
MM::Native::DisposeHandle(theResource);
r.handle = 0;
}
return SetResError(noErr);
}
}
}
return SetResError(resNotFound);
}
uint16_t HomeResFile(uint16_t trap)
{
// PPCAsm
// FUNCTION HomeResFile (theResource: Handle): Integer;
uint32_t sp;
uint32_t theResource;
sp = StackFrame<4>(theResource);
Log("%04x HomeResFile(%08x)\n", trap, theResource);
if (!theResource) {
ToolReturn<2>(sp, -1);
return SetResError(resNotFound);
}
for (resource_file *rf = head; rf; rf = rf->next) {
for (const auto &r : rf->resources) {
if (r.handle == theResource) {
ToolReturn<2>(sp, rf->refnum);
return SetResError(noErr);
}
}
}
ToolReturn<2>(sp, -1);
return SetResError(resNotFound);
}
uint16_t ResError(uint16_t trap)
{
uint32_t sp;
Log("%04x ResError()\n", trap);
sp = cpuGetAReg(7);
ToolReturn<2>(sp, memoryReadWord(MacOS::ResErr));
return 0;
}
// SetResLoad (load: BOOLEAN);
uint16_t SetResLoad(uint16_t trap)
{
uint16_t load;
StackFrame<2>(load);
Log("%04x SetResLoad(%04x)\n", trap, load);
res_load = load;
memoryWriteByte(load ? 0xff : 0x00, MacOS::ResLoad); // word or byte?
return SetResError(0);
}
#pragma mark - Open Resource Files
uint16_t OpenResFile(uint16_t trap)
{
// OpenResFile (fileName: Str255) : INTEGER;
uint32_t sp;
uint32_t fileName;
sp = StackFrame<4>(fileName);
std::string sname = ToolBox::ReadPString(fileName, true);
Log("%04x OpenResFile(%s)\n", trap, sname.c_str());
auto rv = Native::OpenResFile(sname);
ToolReturn<2>(sp, rv.value_or(-1));
return SetResError(rv.error());
}
uint16_t HOpenResFile(uint16_t trap)
{
// FUNCTION HOpenResFile (vRefNum: Integer; dirID: LongInt;
// fileName: Str255; permission: SignedByte): Integer;
uint32_t sp;
uint16_t vRefNum;
uint32_t dirID;
uint32_t fileName;
uint8_t permission;
sp = StackFrame<12>(vRefNum, dirID, fileName, permission);
std::string sname = ToolBox::ReadPString(fileName, true);
Log("%04x HOpenResFile(%04x, %08x, %s, %04x)\n",
trap, vRefNum, dirID, sname.c_str(), permission);
if (vRefNum) {
fprintf(stderr, "HOpenResFile: vRefNum not supported yet.\n");
exit(1);
}
sname = OS::FSSpecManager::ExpandPath(sname, dirID);
if (sname.empty())
{
ToolReturn<2>(sp, (uint16_t)-1);
return SetResError(MacOS::dirNFErr);
}
auto rv = Native::OpenResFile(sname, permission);
ToolReturn<2>(sp, rv.value_or(-1));
return SetResError(rv.error());
}
uint16_t OpenRFPerm(uint16_t trap)
{
// FUNCTION OpenRFPerm (fileName: Str255; vRefNum: Integer;
// permission: SignedByte): Integer;
uint32_t sp;
uint32_t fileName;
uint16_t vRefNum;
uint16_t permission;
sp = StackFrame<8>(fileName, vRefNum, permission);
std::string sname = ToolBox::ReadPString(fileName, true);
Log("%04x OpenRFPerm(%s, %04x, %04x)\n",
trap, sname.c_str(), vRefNum, permission);
auto rv = Native::OpenResFile(sname, permission);
ToolReturn<2>(sp, rv.value_or(-1));
return SetResError(rv.error());
}
uint16_t FSpOpenResFile(void)
{
// FUNCTION FSpOpenResFile (spec: FSSpec; permission: SignedByte): Integer;
uint32_t sp;
uint32_t spec;
uint8_t permission;
sp = StackFrame<6>(spec, permission);
int parentID = memoryReadLong(spec + 2);
std::string sname = ToolBox::ReadPString(spec + 6, false);
Log(" FSpOpenResFile(%s, %04x)\n", sname.c_str(), permission);
sname = OS::FSSpecManager::ExpandPath(sname, parentID);
if (sname.empty())
{
ToolReturn<2>(sp, (uint16_t)-1);
return SetResError(MacOS::dirNFErr);
}
auto rv = Native::OpenResFile(sname, permission);
ToolReturn<2>(sp, rv.value_or(-1));
return SetResError(rv.error());
}
uint16_t CloseResFile(uint16_t trap)
{
// PROCEDURE CloseResFile (refNum: Integer);
uint16_t refNum;
StackFrame<2>(refNum);
Log("%04x CloseResFile(%04x)\n", trap, refNum);
// If the value of the refNum parameter is 0, it represents the System file and is ignored.
// n.b. -- should close --all-- resources.
auto rv = Native::CloseResFile(refNum);
return SetResError(rv.error());
}
uint16_t Get1NamedResource(uint16_t trap)
{
// Get1NamedResource (theType: ResType; name: Str255) : Handle;
/*
* -----------
* +8 outHandle
* ------------
* +4 theType
* ------------
* +0 name
* ------------
*
*/
// nb - return address is not on the stack.
uint32_t sp;
uint32_t theType;
uint32_t name;
sp = StackFrame<8>(theType, name);
std::string sname = ToolBox::ReadPString(name);
Log("%04x Get1NamedResource(%08x ('%s'), %s)\n",
trap, theType, TypeToString(theType).c_str(), sname.c_str());
auto rv = Native::Get1NamedResource(theType, sname);
ToolReturn<4>(sp, rv.value());
return SetResError(rv.error());
}
uint16_t GetNamedResource(uint16_t trap)
{
// FUNCTION GetNamedResource (theType: ResType; name: Str255): Handle;
uint32_t sp;
uint32_t theType;
uint32_t name;
sp = StackFrame<8>(theType, name);
std::string sname = ToolBox::ReadPString(name);
Log("%04x GetNamedResource(%08x ('%s'), %s)\n",
trap, theType, TypeToString(theType).c_str(), sname.c_str());
auto rv = Native::GetNamedResource(theType, sname);
ToolReturn<4>(sp, rv.value());
return SetResError(rv.error());
}
uint16_t GetResource(uint16_t trap)
{
// GetResource (theType: ResType; theID: Integer): Handle;
/*
* -----------
* +6 outHandle
* ------------
* +2 theType
* ------------
* +0 theID
* ------------
*
*/
// nb - return address is not on the stack.
uint32_t sp;
uint32_t theType;
uint16_t theID;
sp = StackFrame<6>(theType, theID);
Log("%04x GetResource(%08x ('%s'), %04x)\n",
trap, theType, TypeToString(theType).c_str(), theID);
auto rv = Native::GetResource(theType, theID);
ToolReturn<4>(sp, rv.value());
return SetResError(rv.error());
}
uint16_t Get1Resource(uint16_t trap)
{
// Get1Resource (theType: ResType; theID: Integer): Handle;
/*
* -----------
* +6 outHandle
* ------------
* +2 theType
* ------------
* +0 theID
* ------------
*
*/
// nb - return address is not on the stack.
uint32_t sp;
uint32_t theType;
uint16_t theID;
sp = StackFrame<6>(theType, theID);
Log("%04x Get1Resource(%08x ('%s'), %04x)\n", trap, theType, TypeToString(theType).c_str(), theID);
auto rv = Native::Get1Resource(theType, theID);
ToolReturn<4>(sp, rv.value());
return SetResError(rv.error());
}
} // RM