Attempts to add support for MSCOM via jni4net

Also only building the necessary modules
This commit is contained in:
Norbi Peti 2020-10-16 03:19:34 +02:00
parent 26d8cc1ff0
commit d68ddb3418
22 changed files with 212 additions and 174 deletions

View file

@ -37,14 +37,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>
@ -52,6 +44,8 @@
<groupId>org.virtualbox</groupId> <groupId>org.virtualbox</groupId>
<artifactId>VirtualBox-MSCOM</artifactId> <artifactId>VirtualBox-MSCOM</artifactId>
<version>6.1</version> <version>6.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/../VirtualComputerMSCOM/src/main/resources/Interop.VirtualBox.j4n.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.aparapi</groupId> <groupId>com.aparapi</groupId>

View file

@ -20,11 +20,22 @@
<version>6.1</version> <version>6.1</version>
<optional>true <optional>true
</optional> <!-- https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html --> </optional> <!-- https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html -->
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/Interop.VirtualBox.j4n.jar</systemPath>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.sf.jacob-project</groupId> <groupId>net.sf.jni4net</groupId>
<artifactId>jacob</artifactId> <artifactId>clr</artifactId>
<version>1.19</version> <version>0.8.9.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/jni4net.j-0.8.9.0.jar</systemPath>
</dependency>
<dependency>
<groupId>io.github.NorbiPeti</groupId>
<artifactId>VirtualComputer-Windows</artifactId>
<version>0.8.9.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/VirtualComputerWindows.j4n.jar</systemPath>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -0,0 +1,77 @@
// ------------------------------------------------------------------------------
// <autogenerated>
// This code was generated by jni4net. See http://jni4net.sourceforge.net/
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </autogenerated>
// ------------------------------------------------------------------------------
package org.virtualbox_6_1;
@net.sf.jni4net.attributes.ClrInterface
public interface ISession {
//<generated-interface>
@net.sf.jni4net.attributes.ClrMethod("()Lorg/virtualbox_6_1/SessionState;")
org.virtualbox_6_1.SessionState getState_FixIt();
default org.virtualbox_6_1.SessionState getState() {
return SessionState.Locked;
}
@net.sf.jni4net.attributes.ClrMethod("()Lorg/virtualbox_6_1/SessionType;")
org.virtualbox_6_1.SessionType getType_FixIt();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/String;")
java.lang.String getName();
@net.sf.jni4net.attributes.ClrMethod("(LSystem/String;)V")
void setName(java.lang.String aName);
@net.sf.jni4net.attributes.ClrMethod("()Lorg/virtualbox_6_1/IMachine;")
org.virtualbox_6_1.IMachine getMachine();
@net.sf.jni4net.attributes.ClrMethod("()Lorg/virtualbox_6_1/IConsole;")
org.virtualbox_6_1.IConsole getConsole();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute1ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute2ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute3ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute4ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute5ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute6ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute7ISession();
@net.sf.jni4net.attributes.ClrMethod("()LSystem/UInt32;")
int getInternalAndReservedAttribute8ISession();
@net.sf.jni4net.attributes.ClrMethod("()V")
void unlockMachine();
@net.sf.jni4net.attributes.ClrMethod("()V")
void internalAndReservedMethod1ISession();
@net.sf.jni4net.attributes.ClrMethod("()V")
void internalAndReservedMethod2ISession();
@net.sf.jni4net.attributes.ClrMethod("()V")
void internalAndReservedMethod3ISession();
@net.sf.jni4net.attributes.ClrMethod("()V")
void internalAndReservedMethod4ISession();
//</generated-interface>
}

View file

@ -0,0 +1,5 @@
package org.virtualbox_6_1;
public enum SessionState {
Locked
}

View file

@ -1,41 +1,44 @@
package sznp.virtualcomputer; package sznp.virtualcomputer;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.sf.jni4net.Ref;
import org.virtualbox_6_1.BitmapFormat; import org.virtualbox_6_1.BitmapFormat;
import org.virtualbox_6_1.FramebufferCapabilities; import org.virtualbox_6_1.IFramebuffer;
import org.virtualbox_6_1.IFramebufferOverlay; import org.virtualbox_6_1.IFramebufferOverlay;
import system.Array;
import sznp.virtualcomputer.util.IMCFrameBuffer; import sznp.virtualcomputer.util.IMCFrameBuffer;
import virtualcomputerwindows.Exports;
import java.util.Arrays; import java.util.Arrays;
@RequiredArgsConstructor @RequiredArgsConstructor
public class COMFrameBuffer { public class COMFrameBuffer implements IFramebuffer {
private final IMCFrameBuffer frameBuffer; private final IMCFrameBuffer frameBuffer;
public long getBitsPerPixel() { public int getBitsPerPixel() {
return 32; return 32;
} }
public long getBytesPerLine() { public int getBytesPerLine() {
return 640L; return 640;
} }
public long[] getCapabilities(long[] arg0) { public Array getCapabilities_FixIt() {
try { try {
System.out.println("Capabilities queried"); System.out.println("Capabilities queried");
System.out.println("Capabilities: " + Arrays.toString(arg0)); //return new long[]{FramebufferCapabilities.UpdateImage.value()};
return new long[]{FramebufferCapabilities.UpdateImage.value()}; return Array.CreateInstance(system.Type.GetType("System.Int32"), 0);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return new long[]{}; return Array.CreateInstance(system.Type.GetType("System.Int32"), 0);
} }
} }
public long getHeight() { public int getHeight() {
return 480; return 480;
} }
public long getHeightReduction() { public int getHeightReduction() {
return 0; return 0;
} }
@ -43,15 +46,16 @@ public class COMFrameBuffer {
return null; return null;
} }
public long getPixelFormat() { public BitmapFormat getPixelFormat_FixIt() {
return BitmapFormat.BGRA.value(); //return BitmapFormat.BGRA.value();
return null;
} }
public long getVisibleRegion(byte arg0, long arg1) { public int getVisibleRegion(Ref<Byte> arg0, int arg1) {
return 0; return 0;
} }
public long getWidth() { public int getWidth() {
return 640; return 640;
} }
@ -59,23 +63,23 @@ public class COMFrameBuffer {
return 0; // Zero means no win id return 0; // Zero means no win id
} }
public void notify3DEvent(long type, byte[] data) { public void notify3DEvent_FixIt(int type, Array data) {
System.out.println("3D event! " + type + " - " + Arrays.toString(data)); System.out.println("3D event! " + type + " - " + Arrays.toString(Exports.ConvertArrayByte(data)));
} }
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) { public void notifyChange(int screenId, int xOrigin, int yOrigin, int width, int height) {
frameBuffer.notifyChange(screenId, xOrigin, yOrigin, width, height); frameBuffer.notifyChange(screenId, xOrigin, yOrigin, width, height);
} }
public void notifyUpdate(long x, long y, long width, long height) { public void notifyUpdate(int x, int y, int width, int height) {
frameBuffer.notifyUpdate(x, y, width, height); frameBuffer.notifyUpdate(x, y, width, height);
} }
public void notifyUpdateImage(long arg0, long arg1, long arg2, long arg3, byte[] arg4) { public void notifyUpdateImage_FixIt(int arg0, int arg1, int arg2, int arg3, Array arg4) {
frameBuffer.notifyUpdateImage(arg0, arg1, arg2, arg3, arg4); frameBuffer.notifyUpdateImage(arg0, arg1, arg2, arg3, Exports.ConvertArrayByte(arg4));
} }
public void setVisibleRegion(byte arg0, long arg1) { public void setVisibleRegion(Ref<Byte> arg0, int arg1) {
} }
/** /**
@ -87,11 +91,11 @@ public class COMFrameBuffer {
* @param enmCmd The validated VBOXVHWACMD::enmCmd value from the command. * @param enmCmd The validated VBOXVHWACMD::enmCmd value from the command.
* @param fromGuest Set when the command origins from the guest, clear if host. * @param fromGuest Set when the command origins from the guest, clear if host.
*/ //https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Frontends/VirtualBox/src/VBoxFBOverlay.cpp#L4645 */ //https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Frontends/VirtualBox/src/VBoxFBOverlay.cpp#L4645
public void processVHWACommand(byte command, int enmCmd, boolean fromGuest) { public void processVHWACommand(Ref<Byte> command, int enmCmd, int fromGuest) {
} }
public boolean videoModeSupported(long arg0, long arg1, long arg2) { public int videoModeSupported(int arg0, int arg1, int arg2) {
return true; return 1;
} }
} }

View file

@ -1,13 +1,13 @@
package sznp.virtualcomputer; package sznp.virtualcomputer;
import org.virtualbox_6_1.IEvent; import org.virtualbox_6_1.IEvent;
import sznp.virtualcomputer.util.COMObjectBase; import org.virtualbox_6_1.IEventListener;
import sznp.virtualcomputer.util.IEventHandler; import sznp.virtualcomputer.util.IEventHandler;
/** /**
* A Bukkit-like event system which calls the appropriate methods on an event. * A Bukkit-like event system which calls the appropriate methods on an event.
*/ */
public final class EventHandler extends COMObjectBase { public final class EventHandler implements IEventListener {
private final IEventHandler handler; private final IEventHandler handler;
private boolean enabled = true; private boolean enabled = true;
@ -20,6 +20,7 @@ public final class EventHandler extends COMObjectBase {
this.handler = handler; this.handler = handler;
} }
@Override
public final void handleEvent(IEvent iEvent) { public final void handleEvent(IEvent iEvent) {
if (!enabled) if (!enabled)
return; return;

View file

@ -1,10 +0,0 @@
package sznp.virtualcomputer.util;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
public abstract class COMObjectBase extends Dispatch {
public COMObjectBase() {
super("clsid:{67099191-32E7-4F6C-85EE-422304C71B90}");
}
}

View file

@ -1,52 +1,55 @@
package sznp.virtualcomputer.util; package sznp.virtualcomputer.util;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.*;
import lombok.val; import lombok.val;
import net.sf.jni4net.Out;
import org.virtualbox_6_1.*; import org.virtualbox_6_1.*;
import org.virtualbox_6_1.mscom.Helper;
import org.virtualbox_6_1.mscom.IUnknown;
import sznp.virtualcomputer.COMFrameBuffer; import sznp.virtualcomputer.COMFrameBuffer;
import sznp.virtualcomputer.EventHandler; import sznp.virtualcomputer.EventHandler;
import virtualcomputerwindows.Exports;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
public final class COMUtils { public final class COMUtils {
private COMUtils() { private COMUtils() {
} }
//public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) { //public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) {
public static org.virtualbox_6_1.IEventListener registerListener(IEventSource source, IEventHandler listener, List<VBoxEventType> types) { public static IEventListener registerListener(IEventSource source, IEventHandler listener, List<VBoxEventType> types) {
//new DispatchEvents(source.getTypedWrapped(), listener); //new DispatchEvents(source.getTypedWrapped(), listener);
val ret = new org.virtualbox_6_1.IEventListener(new EventHandler(listener)); val ret = new EventHandler(listener);
/*com.jacob.activeX.ActiveXComponent.createNewInstance("IEventListener"); /*com.jacob.activeX.ActiveXComponent.createNewInstance("IEventListener");
new ActiveXComponent(""); new ActiveXComponent("");
source.registerListener(ret, types, true);*/ source.registerListener(ret, types, true);*/
//registerListener(source, new EventHandler(listener), types, true); //registerListener(source, new EventHandler(listener), types, true);
System.out.println("Testing listener..."); System.out.println("Testing listener...");
ret.handleEvent(null); ret.handleEvent(null);
System.out.println("Tested"); System.out.println("Tested");
return ret; return ret;
//return null; //return null;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends IEvent> T getEvent(IEvent event, Class<T> cl) { public static <T extends IEvent> T getEvent(IEvent event, Class<T> cl) {
try { //val method = cl.getMethod("queryInterface", IUnknown.class);
val method = cl.getMethod("queryInterface", IUnknown.class); //return (T) method.invoke(null, event);
return (T) method.invoke(null, event); return null; //TODO
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { }
e.printStackTrace();
return null;
}
}
public static IFramebuffer gimmeAFramebuffer(IMCFrameBuffer frameBuffer) { public static IFramebuffer gimmeAFramebuffer(IMCFrameBuffer frameBuffer) {
return new IFramebuffer(new Variant(new COMFrameBuffer(frameBuffer)).getDispatch()); return new COMFrameBuffer(frameBuffer);
} }
public static void queryBitmapInfo(IDisplaySourceBitmap bitmap, long[] ptr, long[] w, long[] h, long[] bpp, long[] bpl, long[] pf) { public static void queryBitmapInfo(IDisplaySourceBitmap bitmap, long[] ptr, long[] w, long[] h, long[] bpp, long[] bpl, long[] pf) {
Dispatch.call(bitmap.getTypedWrapped(), "queryBitmapInfo", ptr, w, h, bpp, bpl, pf); Out<Integer> wo = new Out<>(), ho = new Out<>(), bppo = new Out<>(), bplo = new Out<>();
} val pfo = new Out<BitmapFormat>();
val ptro = new Out<Long>();
bitmap.queryBitmapInfo(ptro, wo, ho, bppo, bplo, pfo);
ptr[0] = ptro.getValue();
w[0] = wo.getValue();
h[0] = ho.getValue();
bpp[0] = bppo.getValue();
bpl[0] = bplo.getValue();
pf[0] = Exports.ConvertEnum(pfo.getValue());
}
} }

View file

@ -17,7 +17,7 @@ namespace VirtualComputerWindows
private static VirtualBoxClient vbc; private static VirtualBoxClient vbc;
public static async Task<IVirtualBox> Init() => await Task.Run(() => public static void Init()
{ {
try try
{ {
@ -27,7 +27,7 @@ namespace VirtualComputerWindows
//https://www.virtualbox.org/svn/vbox/trunk/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp //https://www.virtualbox.org/svn/vbox/trunk/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
vbc = new VirtualBoxClientClass(); vbc = new VirtualBoxClientClass();
var vbox = vbc.VirtualBox; var vbox = vbc.VirtualBox;
RTR3InitExe(0, "", 0); //RTR3InitExe(0, "", 0);
var ses = vbc.Session; var ses = vbc.Session;
var machine = vbox.Machines.GetValue(0) as IMachine; var machine = vbox.Machines.GetValue(0) as IMachine;
ses.Name = "minecraft"; ses.Name = "minecraft";
@ -38,22 +38,39 @@ namespace VirtualComputerWindows
machine = ses.Machine; machine = ses.Machine;
Console.WriteLine("Powering up..."); Console.WriteLine("Powering up...");
ses.Console.PowerUp().WaitForCompletion(10000); ses.Console.PowerUp().WaitForCompletion(10000);
Console.WriteLine("Framebuffer attach");
ses.Console.Display.AttachFramebuffer(0, new VBFB(ses.Console.Display));
return vbox;
} }
catch(Exception e) catch(Exception e)
{ {
Console.WriteLine(e); Console.WriteLine(e);
Console.ReadLine(); Console.ReadLine();
return null;
} }
}); }
public static void Main() public static void Main()
{ {
Init(); Init();
Console.ReadLine(); Console.ReadLine();
} }
public static int[] ConvertArrayInt(Array array)
{
return (int[]) array;
}
public static byte[] ConvertArrayByte(Array array)
{
return (byte[]) array;
}
public static uint[] ConvertArrayUint(Array array)
{
return (uint[]) array;
}
public static int ConvertEnum(object someEnum)
{
BitmapFormat.BitmapFormat_RGBA;
return (int) someEnum;
}
} }
} }

View file

@ -1,75 +0,0 @@
using System;
using VirtualBox;
namespace VirtualComputerWindows
{
internal class VBFB : IFramebuffer
{
private IDisplay display;
public VBFB(IDisplay display)
{
this.display = display;
}
public void NotifyUpdate(uint aX, uint aY, uint aWidth, uint aHeight)
{
Console.WriteLine("Update: " + aX + " " + aY + " " + aWidth + " " + aHeight);
}
public void NotifyUpdateImage(uint aX, uint aY, uint aWidth, uint aHeight, Array aImage)
{
Console.WriteLine("UpdateImage: " + aX + " " + aY + " " + aWidth + " " + aHeight);
}
public void NotifyChange(uint aScreenId, uint aXOrigin, uint aYOrigin, uint aWidth, uint aHeight)
{
Console.WriteLine("Change: " + aXOrigin + " " + aYOrigin + " " + aWidth + " " + aHeight);
display.QuerySourceBitmap(0, out var isd);
var addr = new IntPtr();
isd.QueryBitmapInfo(addr, out var w, out var h, out var bpp, out var bpl, out var bf);
Console.WriteLine("Bitmap info: " + addr + " " + w + " " + h + " " + bpp + " " + bpl + " " + bf);
}
public int VideoModeSupported(uint aWidth, uint aHeight, uint aBpp)
{
return 1;
}
public uint GetVisibleRegion(ref byte aRectangles, uint aCount)
{
return aCount;
}
public void SetVisibleRegion(ref byte aRectangles, uint aCount)
{
}
public void ProcessVHWACommand(ref byte aCommand, int aEnmCmd, int aFromGuest)
{
}
public void Notify3DEvent(uint aType, Array aData)
{
}
public uint Width => 640;
public uint Height => 480;
public uint BitsPerPixel => 32;
public uint BytesPerLine => 640 * 4;
public BitmapFormat PixelFormat => BitmapFormat.BitmapFormat_BGRA;
public uint HeightReduction => 0;
public IFramebufferOverlay Overlay => null;
public long WinId => 0;
//public Array Capabilities => new[] { FramebufferCapabilities.FramebufferCapabilities_UpdateImage };
public Array Capabilities => new FramebufferCapabilities[] { };
}
}

View file

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualComputerWindows</RootNamespace> <RootNamespace>VirtualComputerWindows</RootNamespace>
<AssemblyName>VirtualComputerWindows</AssemblyName> <AssemblyName>VirtualComputerWindows</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<DllExportNamespace>VirtualComputerWindows</DllExportNamespace> <DllExportNamespace>VirtualComputerWindows</DllExportNamespace>
@ -93,7 +93,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="Exports.cs" /> <Compile Include="Exports.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VBFB.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />

View file

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0"/>
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.InteropServices" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0"/>
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup></configuration>

16
pom.xml
View file

@ -8,9 +8,7 @@
<artifactId>VirtualComputer</artifactId> <artifactId>VirtualComputer</artifactId>
<version>2.1-SNAPSHOT</version> <version>2.1-SNAPSHOT</version>
<modules> <modules>
<module>VirtualComputerXPCOM</module>
<module>VirtualComputer-Core</module> <module>VirtualComputer-Core</module>
<module>VirtualComputerMSCOM</module>
</modules> </modules>
<packaging>pom</packaging> <packaging>pom</packaging>
@ -127,4 +125,18 @@
</plugins> </plugins>
</pluginManagement> </pluginManagement>
</build> </build>
<profiles>
<profile>
<id>XPCOM</id>
<modules>
<module>VirtualComputerXPCOM</module>
</modules>
</profile>
<profile>
<id>MSCOM</id>
<modules>
<module>VirtualComputerMSCOM</module>
</modules>
</profile>
</profiles>
</project> </project>

View file

@ -7,6 +7,6 @@
<versions> <versions>
<version>1.19</version> <version>1.19</version>
</versions> </versions>
<lastUpdated>20200811112851</lastUpdated> <lastUpdated>20200811224130</lastUpdated>
</versioning> </versioning>
</metadata> </metadata>