use super::{arg_dword, HostState, Registry, StubFn, Win32Error};
use crate::com::{
call::vtable_is_plausible, call_method, Guid, CLASS_E_CLASSNOTAVAILABLE, E_POINTER,
SLOT_CLASS_FACTORY_CREATE_INSTANCE,
};
use crate::emulator::{Cpu, Mmu};
const S_OK: u32 = 0;
pub fn register(registry: &mut Registry) {
registry.register(
"ole32.dll",
"CoCreateInstance",
stub_co_create_instance as StubFn,
5,
);
registry.register(
"ole32.dll",
"CoFreeUnusedLibraries",
stub_co_free_unused_libraries as StubFn,
0,
);
registry.register("ole32.dll", "CoInitialize", stub_co_initialize as StubFn, 1);
registry.register(
"ole32.dll",
"CoInitializeEx",
stub_co_initialize_ex as StubFn,
2,
);
registry.register(
"ole32.dll",
"CoTaskMemAlloc",
stub_co_task_mem_alloc as StubFn,
1,
);
registry.register(
"ole32.dll",
"CoTaskMemFree",
stub_co_task_mem_free as StubFn,
1,
);
registry.register(
"ole32.dll",
"CoTaskMemRealloc",
stub_co_task_mem_realloc as StubFn,
2,
);
registry.register(
"ole32.dll",
"CoUninitialize",
stub_co_uninitialize as StubFn,
0,
);
registry.register(
"ole32.dll",
"StringFromGUID2",
stub_string_from_guid2 as StubFn,
3,
);
}
fn stub_co_create_instance(
cpu: &mut Cpu,
mmu: &mut Mmu,
state: &mut HostState,
registry: &Registry,
) -> Result<u32, Win32Error> {
let rclsid = arg_dword(cpu, mmu, 0).map_err(|t| trap("CoCreateInstance", t))?;
let p_unk_outer = arg_dword(cpu, mmu, 1).map_err(|t| trap("CoCreateInstance", t))?;
let _dw_cls_ctx = arg_dword(cpu, mmu, 2).map_err(|t| trap("CoCreateInstance", t))?;
let riid = arg_dword(cpu, mmu, 3).map_err(|t| trap("CoCreateInstance", t))?;
let ppv = arg_dword(cpu, mmu, 4).map_err(|t| trap("CoCreateInstance", t))?;
if rclsid == 0 || riid == 0 || ppv == 0 {
return Ok(E_POINTER);
}
let clsid = Guid::load(mmu, rclsid).map_err(|t| trap("CoCreateInstance", t))?;
let _iid = Guid::load(mmu, riid).map_err(|t| trap("CoCreateInstance", t))?;
let Some(factory) = state.com.lookup_class_factory(&clsid) else {
return Ok(CLASS_E_CLASSNOTAVAILABLE);
};
if !vtable_is_plausible(mmu, factory) {
return Ok(CLASS_E_CLASSNOTAVAILABLE);
}
let r = call_method(
cpu,
mmu,
registry,
state,
factory,
SLOT_CLASS_FACTORY_CREATE_INSTANCE,
&[p_unk_outer, riid, ppv],
)
.map_err(|e| Win32Error::InvalidArgument {
stub: "CoCreateInstance",
reason: format!("IClassFactory::CreateInstance failed: {e}"),
})?;
if r == S_OK {
if let Ok(out_ptr) = mmu.load32(ppv) {
if out_ptr != 0 {
state.com.intern(out_ptr, None);
}
}
}
Ok(r)
}
fn stub_co_free_unused_libraries(
_cpu: &mut Cpu,
_mmu: &mut Mmu,
_state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
Ok(0)
}
fn stub_co_initialize(
_cpu: &mut Cpu,
_mmu: &mut Mmu,
_state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
Ok(S_OK)
}
fn stub_co_initialize_ex(
_cpu: &mut Cpu,
_mmu: &mut Mmu,
_state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
Ok(S_OK)
}
fn stub_co_task_mem_alloc(
cpu: &mut Cpu,
mmu: &mut Mmu,
state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
let n = arg_dword(cpu, mmu, 0).map_err(|t| trap("CoTaskMemAlloc", t))?;
if n == 0 {
return Ok(0);
}
let addr = state.arena_alloc(n)?;
let buf = vec![0u8; n as usize];
mmu.write_initializer(addr, &buf)
.map_err(|t| trap("CoTaskMemAlloc", t))?;
Ok(addr)
}
fn stub_co_task_mem_free(
_cpu: &mut Cpu,
_mmu: &mut Mmu,
_state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
Ok(0)
}
fn stub_co_task_mem_realloc(
cpu: &mut Cpu,
mmu: &mut Mmu,
state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
let pv = arg_dword(cpu, mmu, 0).map_err(|t| trap("CoTaskMemRealloc", t))?;
let cb = arg_dword(cpu, mmu, 1).map_err(|t| trap("CoTaskMemRealloc", t))?;
if cb == 0 {
return Ok(0);
}
let new_addr = state.arena_alloc(cb)?;
let zero = vec![0u8; cb as usize];
mmu.write_initializer(new_addr, &zero)
.map_err(|t| trap("CoTaskMemRealloc", t))?;
if pv != 0 {
for i in 0..cb {
let b = mmu.load8(pv + i).unwrap_or(0);
mmu.store8(new_addr + i, b)
.map_err(|t| trap("CoTaskMemRealloc", t))?;
}
}
Ok(new_addr)
}
fn stub_co_uninitialize(
_cpu: &mut Cpu,
_mmu: &mut Mmu,
_state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
Ok(0)
}
fn stub_string_from_guid2(
cpu: &mut Cpu,
mmu: &mut Mmu,
_state: &mut HostState,
_registry: &Registry,
) -> Result<u32, Win32Error> {
let pguid = arg_dword(cpu, mmu, 0).map_err(|t| trap("StringFromGUID2", t))?;
let psz = arg_dword(cpu, mmu, 1).map_err(|t| trap("StringFromGUID2", t))?;
let cch = arg_dword(cpu, mmu, 2).map_err(|t| trap("StringFromGUID2", t))?;
if pguid == 0 || psz == 0 || cch < 39 {
return Ok(0);
}
let mut g = [0u8; 16];
for (i, b) in g.iter_mut().enumerate() {
*b = mmu
.load8(pguid + i as u32)
.map_err(|t| trap("StringFromGUID2", t))?;
}
let d1 = u32::from_le_bytes([g[0], g[1], g[2], g[3]]);
let d2 = u16::from_le_bytes([g[4], g[5]]);
let d3 = u16::from_le_bytes([g[6], g[7]]);
let d4 = &g[8..16];
let s = format!(
"{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
d1, d2, d3, d4[0], d4[1], d4[2], d4[3], d4[4], d4[5], d4[6], d4[7],
);
for (i, c) in s.encode_utf16().enumerate() {
let off = (i as u32) * 2;
mmu.store16(psz + off, c)
.map_err(|t| trap("StringFromGUID2", t))?;
}
let len = s.encode_utf16().count() as u32;
mmu.store16(psz + len * 2, 0)
.map_err(|t| trap("StringFromGUID2", t))?;
Ok(len + 1)
}
fn trap(stub: &'static str, t: crate::emulator::Trap) -> Win32Error {
Win32Error::InvalidArgument {
stub,
reason: format!("{t}"),
}
}