Commit 24f0f14b authored by Stephanie Gawroriski's avatar Stephanie Gawroriski
Browse files

Refactor getting manifest properties but also read properties from JADs...

Refactor getting manifest properties but also read properties from JADs including ones embedded within KJXs.
parent cf76e415
Pipeline #91552 passed with stages
in 6 minutes and 43 seconds
......@@ -16,6 +16,8 @@
#define JARSHELF_LIBRARIES_DESC "()[Lcc/squirreljme/jvm/mle/brackets/JarPackageBracket;"
#define JARSHELF_LIBRARYPATH_DESC "(Lcc/squirreljme/jvm/mle/brackets/JarPackageBracket;)Ljava/lang/String;"
#define JARSHELF_OPENRESOURCE_DESC "(Lcc/squirreljme/jvm/mle/brackets/JarPackageBracket;Ljava/lang/String;)Ljava/io/InputStream;"
#define JARSHELF_RAWDATA_DESC "(Lcc/squirreljme/jvm/mle/brackets/JarPackageBracket;I[BII)I"
#define JARSHELF_RAWSIZE_DESC "(Lcc/squirreljme/jvm/mle/brackets/JarPackageBracket;)I"
JNIEXPORT jobject JNICALL Impl_mle_JarShelf_classPath(JNIEnv* env,
jclass classy)
......@@ -47,12 +49,31 @@ JNIEXPORT jobject JNICALL Impl_mle_JarShelf_openResource(JNIEnv* env,
jar, rcName);
}
JNIEXPORT jint JNICALL Impl_mle_JarShelf_rawData(JNIEnv* env,
jclass classy, jobject jar, jint jarOff,
jbyteArray buf, jint off, jint len)
{
return forwardCallStaticInteger(env, JARSHELF_CLASSNAME,
"rawData", JARSHELF_RAWDATA_DESC,
jar, jarOff, buf, off, len);
}
JNIEXPORT jint JNICALL Impl_mle_JarShelf_rawSize(JNIEnv* env,
jclass classy, jobject jar)
{
return forwardCallStaticInteger(env, JARSHELF_CLASSNAME,
"rawSize", JARSHELF_RAWSIZE_DESC,
jar);
}
static const JNINativeMethod mleJarMethods[] =
{
{"classPath", JARSHELF_CLASSPATH_DESC, (void*)Impl_mle_JarShelf_classPath},
{"libraries", JARSHELF_LIBRARIES_DESC, (void*)Impl_mle_JarShelf_libraries},
{"libraryPath", JARSHELF_LIBRARYPATH_DESC, (void*)Impl_mle_JarShelf_libraryPath},
{"openResource", JARSHELF_OPENRESOURCE_DESC, (void*)Impl_mle_JarShelf_openResource},
{"rawData", JARSHELF_RAWDATA_DESC, (void*)Impl_mle_JarShelf_rawData},
{"rawSize", JARSHELF_RAWSIZE_DESC, (void*)Impl_mle_JarShelf_rawSize},
};
jint JNICALL mleJarInit(JNIEnv* env, jclass classy)
......
......@@ -16,9 +16,12 @@ import cc.squirreljme.vm.DataContainerLibrary;
import cc.squirreljme.vm.JarClassLibrary;
import cc.squirreljme.vm.VMClassLibrary;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
......@@ -163,4 +166,89 @@ public final class EmulatedJarPackageShelf
// Store them all
return fill.toArray(new JarPackageBracket[fill.size()]);
}
/**
* Reads a section of a JAR representation, note that the format is not
* necessarily in the format of a JAR or ZIP file but may exist in SQC
* form.
*
* @param __jar The library to read the raw data from.
* @param __jarOffset The offset into the raw data.
* @param __b The buffer to write into.
* @param __o The offset into the buffer.
* @param __l The length of the buffer.
* @return The number of bytes read from the raw Jar data.
* @throws MLECallError On null arguments or if the offset and/or length
* exceed the array bounds.
* @since 2022/03/04
*/
public static int rawData(JarPackageBracket __jar,
int __jarOffset, byte[] __b, int __o, int __l)
throws MLECallError
{
if (__jar == null || __jarOffset < 0 || __b == null ||
__o < 0 || __l < 0 || (__o + __l) > __b.length)
throw new MLECallError("Invalid parameters.");
// Determine the path to the JAR
EmulatedJarPackageBracket emul = (EmulatedJarPackageBracket)__jar;
Path path = emul.vmLib.path();
if (path == null)
throw new MLECallError("JAR is not physically backed.");
// Seek through and find the data
try (InputStream in = Files.newInputStream(path,
StandardOpenOption.READ))
{
// Seek first, stop if EOF is hit
for (int at = 0; at < __jarOffset; at++)
if (in.read() < 0)
return -1;
// Do a standard read here
return in.read(__b, __o, __l);
}
catch (IOException e)
{
throw new MLECallError("Could not raw read JAR.", e);
}
}
/**
* Returns the raw size of a given JAR, note that this may not be
* the size of a JAR file but a compiled form such a SQC.
*
* @param __jar The JAR to lookup.
* @return The raw size of the JAR, this will be a negative value if the
* JAR is virtual and its size is not known.
* @throws MLECallError If {@code __jar} is null.
* @since 2022/03/04
*/
public static int rawSize(JarPackageBracket __jar)
throws MLECallError
{
if (__jar == null)
throw new MLECallError("No JAR specified.");
// Determine the path to the JAR
EmulatedJarPackageBracket emul = (EmulatedJarPackageBracket)__jar;
Path path = emul.vmLib.path();
if (path == null)
return -1;
// Use the file size directly
try
{
return (int)Math.min(Integer.MAX_VALUE, Files.size(path));
}
// Size is not valid?
catch (IOException e)
{
e.printStackTrace();
return -1;
}
}
}
......@@ -79,6 +79,7 @@ public final class PathSuiteManager
String fn = p.getFileName().toString();
if (fn.endsWith(".jar") || fn.endsWith(".JAR") ||
fn.endsWith(".jad") || fn.endsWith(".JAD") ||
fn.endsWith(".jam") || fn.endsWith(".JAM") ||
fn.endsWith(".kjx") || fn.endsWith(".KJX"))
rv.add(fn);
......
......@@ -576,9 +576,10 @@ public abstract class VMFactory
// Not a known extension or normalized type
if (!(__name.endsWith(".jar") || __name.endsWith(".JAR") ||
__name.endsWith(".jad") || __name.endsWith(".JAD") ||
__name.endsWith(".jam") || __name.endsWith(".JAM") ||
__name.endsWith(".sqc") || __name.endsWith(".SQC") ||
__name.endsWith(".kjx") || __name.endsWith(".KJX") ||
__name.endsWith(".jam") || __name.endsWith(".JAM")))
__name.endsWith(".kjx") || __name.endsWith(".KJX")))
return __name;
// Get the base name of the JAR or SQC
......
......@@ -54,8 +54,9 @@ public final class __JarWalker__
// If this is a JAR, we will grab it
String fn = __path.getFileName().toString();
if (fn.endsWith(".jar") || fn.endsWith(".JAR") ||
fn.endsWith(".kjx") || fn.endsWith(".KJX") ||
fn.endsWith(".jam") || fn.endsWith(".JAM"))
fn.endsWith(".jad") || fn.endsWith(".JAD") ||
fn.endsWith(".jam") || fn.endsWith(".JAM") ||
fn.endsWith(".kjx") || fn.endsWith(".KJX"))
this._files.add(__path.toString());
// The default way is to just handle it
......
......@@ -77,4 +77,36 @@ public final class JarPackageShelf
public static native InputStream openResource(JarPackageBracket __jar,
String __rc)
throws MLECallError;
/**
* Reads a section of a JAR representation, note that the format is not
* necessarily in the format of a JAR or ZIP file but may exist in SQC
* form.
*
* @param __jar The library to read the raw data from.
* @param __jarOffset The offset into the raw data.
* @param __b The buffer to write into.
* @param __o The offset into the buffer.
* @param __l The length of the buffer.
* @return The number of bytes read from the raw Jar data.
* @throws MLECallError On null arguments or if the offset and/or length
* exceed the array bounds.
* @since 2022/03/04
*/
public static native int rawData(JarPackageBracket __jar,
int __jarOffset, byte[] __b, int __o, int __l)
throws MLECallError;
/**
* Returns the raw size of a given JAR, note that this may not be
* the size of a JAR file but a compiled form such a SQC.
*
* @param __jar The JAR to lookup.
* @return The raw size of the JAR, this will be a negative value if the
* JAR is virtual and its size is not known.
* @throws MLECallError If {@code __jar} is null.
* @since 2022/03/04
*/
public static native int rawSize(JarPackageBracket __jar)
throws MLECallError;
}
// ---------------------------------------------------------------------------
// SquirrelJME
// Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the GNU General Public License v3+, or later.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------
package cc.squirreljme.jvm.mle;
import cc.squirreljme.jvm.mle.brackets.JarPackageBracket;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.IOException;
import java.io.InputStream;
/**
* This is an input stream over the raw representation of a
* {@link JarPackageBracket} which might not be in the form of a JAR or any
* actual readable data.
*
* @since 2022/03/04
*/
public class RawJarPackageBracketInputStream
extends InputStream
{
/** The given library. */
protected final JarPackageBracket jar;
/** The size of the JAR. */
protected final int jarSize;
/** Single byte read, as only bulk read is supported. */
private final byte[] _singleByte =
new byte[1];
/** The current read position. */
private int _readPos;
/**
* Initializes the input stream to read the raw JAR.
*
* @param __jar The JAR to read raw data from.
* @throws IOException If reading from the given JAR in its
* raw data form is not possible.
* @throws NullPointerException On null arguments.
* @since 2022/03/04
*/
public RawJarPackageBracketInputStream(JarPackageBracket __jar)
throws IOException, NullPointerException
{
if (__jar == null)
throw new NullPointerException("NARG");
// {@squirreljme.error ZZ3u The specified JAR cannot be accessed
// directly. (The JAR path)}
int jarSize = JarPackageShelf.rawSize(__jar);
if (jarSize < 0)
throw new IOException("ZZ3u " +
JarPackageShelf.libraryPath(__jar));
// Set for later
this.jar = __jar;
this.jarSize = jarSize;
}
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public int available()
throws IOException
{
return this.jarSize - this._readPos;
}
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public int read()
throws IOException
{
// Keep trying to read a single byte
byte[] singleByte = this._singleByte;
for (;;)
{
// Try reading byte
int read = this.read(singleByte, 0, 1);
// EOF?
if (read < 0)
return -1;
// Use the given byte assuming it was read
if (read != 0)
return singleByte[0] & 0xFF;
}
}
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public int read(byte[] __b, int __o, int __l)
throws IndexOutOfBoundsException, IOException, NullPointerException
{
if (__b == null)
throw new NullPointerException("NARG");
if (__o < 0 || __l < 0 || (__o + __l) > __b.length)
throw new IndexOutOfBoundsException("IOOB");
// Read in the JAR data
int readPos = this._readPos;
int count = JarPackageShelf.rawData(this.jar, readPos, __b, __o, __l);
// EOF?
if (count < 0)
{
this._readPos = this.jarSize;
return -1;
}
// Count up what we read
this._readPos = readPos + count;
// And use our count!
return count;
}
}
......@@ -21,7 +21,7 @@ public class DataInputStream
implements DataInput
{
/** The wrapped stream. */
protected final InputStream in;
protected final InputStream in;
/**
* Wraps the specified stream.
......
// ---------------------------------------------------------------------------
// SquirrelJME
// Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the GNU General Public License v3+, or later.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------
package cc.squirreljme.runtime.midlet;
import cc.squirreljme.jvm.manifest.JavaManifest;
import cc.squirreljme.runtime.midlet.ManifestSourceType;
/**
* Represents a source for a manifest, if discovered.
*
* @see ManifestSourceType
* @since 2022/03/04
*/
public final class ManifestSource
{
/** Is the manifest missing? */
public boolean isMissing;
/** The actual manifest. */
public JavaManifest manifest;
}
// ---------------------------------------------------------------------------
// SquirrelJME
// Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the GNU General Public License v3+, or later.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------
package cc.squirreljme.runtime.midlet;
import cc.squirreljme.jvm.mle.JarPackageShelf;
import cc.squirreljme.jvm.mle.RawJarPackageBracketInputStream;
import cc.squirreljme.jvm.mle.brackets.JarPackageBracket;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import javax.microedition.midlet.MIDlet;
import net.multiphasicapps.collections.UnmodifiableCollection;
/**
* Represents a source type for a given manifest for any given {@link MIDlet}.
*
* @since 2022/03/04
*/
public enum ManifestSourceType
{
/** JAD Manifest. */
JAD
{
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public InputStream manifestStream(JarPackageBracket __ourJar)
throws IOException, NullPointerException
{
if (__ourJar == null)
throw new NullPointerException("NARG");
// Make assumptions on what our JAD is called.
String ourPath = JarPackageShelf.libraryPath(__ourJar);
String assumedJad;
if (ourPath.endsWith(".jar") || ourPath.endsWith(".JAR"))
{
boolean caps = ourPath.endsWith(".JAR");
assumedJad = ourPath.substring(0,
ourPath.length() - 4) + (caps ? ".JAD" : ".jad");
}
else
assumedJad = ourPath + ".jad";
// Find the matching assumed JAD
for (JarPackageBracket otherJar : JarPackageShelf.libraries())
{
// If the other JAR is a match for the path we assumed, then
// we should attempt reading the JAD to get properties from
String otherPath = JarPackageShelf.libraryPath(otherJar);
if (assumedJad.equals(otherPath))
return JarPackageShelf.openResource(otherJar,
"META-INF/MANIFEST.MF");
}
// Not found
return null;
}
},
/** KJX Embedded JAD manifest. */
KJX_EMBEDDED_JAD
{
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public String encoding()
{
// For some reason, Shift-JIS is used for the manifests instead
// of what should be UTF-8...
return "shift-jis";
}
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public InputStream manifestStream(JarPackageBracket __ourJar)
throws IOException, NullPointerException
{
if (__ourJar == null)
throw new NullPointerException("NARG");
// If we are not a KJX, then do not bother trying!
String ourPath = JarPackageShelf.libraryPath(__ourJar);
if (!ourPath.endsWith(".kjx") && !ourPath.endsWith(".KJX"))
return null;
// We need to parse the KJX to handle this
try (DataInputStream raw = new DataInputStream(
new RawJarPackageBracketInputStream(__ourJar)))
{
// Check magic number
byte[] magic = new byte[3];
raw.readFully(magic);
if (magic[0] != 'K' || magic[1] != 'J' || magic[2] != 'X')
throw new IOException("ZZ4j");
// Position of the JAD
int jadPos = raw.readUnsignedByte();
// Name of the current KJX (ignored, we do not care for it)
int kjxNameLen = raw.readUnsignedByte();
raw.skipBytes(kjxNameLen);
// The size of the JAD
int jadLen = raw.readUnsignedShort();
// Ignore the name of the JAD, we do not care
int jadNameLen = raw.readUnsignedByte();
raw.skipBytes(jadNameLen);
// Determine the relative position to where the JAD should
// start and how much we have read, then skip it
int currentAt = 3 + 1 + 1 + kjxNameLen + 2 + 1 + jadNameLen;
if (currentAt < jadPos)
raw.skipBytes(jadPos - currentAt);
// Read in the entire JAD
byte[] jad = new byte[jadLen];
raw.readFully(jad);
// Use whatever we read as the manifest data
return new ByteArrayInputStream(jad);
}
}
},
/** JAR Manifest. */
JAR
{
/**
* {@inheritDoc}
* @since 2022/03/04
*/
@Override
public InputStream manifestStream(JarPackageBracket __ourJar)
throws IOException, NullPointerException
{
if (__ourJar == null)
throw new NullPointerException("NARG");
// Load the internal manifest
return JarPackageShelf.openResource(__ourJar,
"META-INF/MANIFEST.MF");
}
},
/* End. */
;
/** The available values. */
public static final Collection<ManifestSourceType> VALUES =
UnmodifiableCollection.of(Arrays.asList(ManifestSourceType.values()));
/** The number of available values. */
public static final int COUNT =
ManifestSourceType.VALUES.size();
/**
* Returns the manifest stream.
*
* @param __ourJar Our current JAR.
* @return The stream used to read the manifest or {@code null} if it
* could not be obtained.
* @throws IOException If it could not be read.
* @throws NullPointerException On null arguments.
* @since 2022/03/04
*/
public abstract InputStream manifestStream(JarPackageBracket __ourJar)
throws<