This commit is contained in:
eaglercraft 2024-12-04 00:39:49 -08:00
parent 8b8ae0669a
commit c67fe4733e
211 changed files with 33946 additions and 171 deletions
EAGLERCRAFTX_README.mdREADME.md
desktopRuntime/resources
src
game/java/net/minecraft
lwjgl/java/net/lax1dude/eaglercraft/v1_8
main/java/net/lax1dude/eaglercraft/v1_8
teavm-boot-menu/java/net/lax1dude/eaglercraft/v1_8/boot_menu/teavm
teavm/java/net/lax1dude/eaglercraft/v1_8
wasm-gc-teavm-bootstrap/js
wasm-gc-teavm-loader
wasm-gc-teavm/java/com/jcraft

@ -21,14 +21,14 @@
## Getting Started:
### To compile the latest version of the client, on Windows:
### To compile the latest version of the JavaScript client, on Windows:
1. Make sure you have at least Java 11 installed and added to your PATH, it is recommended to use Java 17
2. Download (clone) this repository to your computer
3. Double click `CompileLatestClient.bat`, a GUI resembling a classic windows installer should open
4. Follow the steps shown to you in the new window to finish compiling
### To compile the latest version of the client, on Linux/macOS:
### To compile the latest version of the JavaScript client, on Linux/macOS:
1. Make sure you have at least Java 11 installed, it is recommended to use Java 17
2. Download (clone) this repository to your computer
@ -37,9 +37,21 @@
5. Type `./CompileLatestClient.sh` and hit enter, a GUI resembling a classic windows installer should open
6. Follow the steps shown to you in the new window to finish compiling
### To set up the development environment
1. Prepare the required files in the mcp918 folder ([readme](mcp918/readme.txt))
2. Run the `build_init` script
3. Run the `build_make_workspace` script
## Browser Compatibility
EaglercraftX 1.8 is currently known to work on browsers as old as Chrome 38 on Windows XP, the game supports both WebGL 1.0 and WebGL 2.0 however features such as dynamic lighting and PBR shaders require WebGL 2.0. The game also supports mobile browsers that don't have a keyboard or mouse, the game will enter touch screen mode automatically when touch input is detected. The game also includes an embedded OGG codec (JOrbis) for loading audio files on iOS where the browsers don't support loading OGG files in an AudioContext.
The JavaScript runtime of EaglercraftX 1.8 is currently known to work on browsers as old as Chrome 38 on Windows XP, the game supports both WebGL 1.0 and WebGL 2.0 however features such as dynamic lighting and PBR shaders require WebGL 2.0. The game also supports mobile browsers that don't have a keyboard or mouse, the game will enter touch screen mode automatically when touch input is detected. The game also includes an embedded OGG codec (JOrbis) for loading audio files on iOS where the browsers don't support loading OGG files in an AudioContext.
## WebAssembly GC Support
EaglercraftX 1.8 also has an experimental WebAssembly GC (WASM-GC) runtime, almost all of the features supported on the JavaScript runtime are also supported on the WebAssembly GC runtime, however it is still incompatible with several major browsers (especially Safari) and will not run in Chrome unless you can access the `chrome://flags` menu or request an origin trial token from Google for your website. Its based on experimental technology and may still crash sometimes due to browser bugs or unresolved issues in the Java to WASM compiler. Hopefully in the coming months the required feature (JSPI, WebAssembly JavaScript Promise Integration) will become enabled by default on the Chrome browser. It performs significantly better than the JavaScript client, around 50% more FPS and TPS in some cases, and will hopefully replace it someday. Just make sure you enable VSync when you play it, otherwise the game will run "too fast" and choke the browser's event loop, causing input lag.
You can compile the WebAssembly GC runtime by creating a development environment (workspace) and reading the README in the "wasm_gc_teavm" folder.
## Singleplayer
@ -200,6 +212,7 @@ The default eaglercraftXOpts values is this:
- `ramdiskMode:` if worlds and resource packs should be stored in RAM instead of IndexedDB
- `singleThreadMode:` if the game should run the client and integrated server in the same context instead of creating a worker object
- `enableEPKVersionCheck:` if the game should attempt to bypass the browser's cache and retry downloading assets.epk when its outdated
- `enforceVSync:` (WASM only) if the game should automatically re-enable VSync at launch if its disabled
- `hooks:` can be used to define JavaScript callbacks for certain events
* `localStorageSaved:` JavaScript callback to save local storage keys (key, data)
* `localStorageLoaded:` JavaScript callback to load local storage keys (key) returns data

@ -17,6 +17,9 @@ Java must be added to your PATH!
3. Run `MakeOfflineDownload`
4. Check the "javascript" folder
**To compile the WASM GC client:**
Consult the [README](wasm_gc_teavm/README.md) in the wasm_gc_teavm folder
**To use the desktop runtime:**
1. Import the Eclipse project in "desktopRuntime/eclipseProject" into your IDE
2. Open one of the .java files from the source folders (workaround for a bug)

@ -785,3 +785,78 @@
* THE SOFTWARE.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Project Name: Emscripten
Project Author: Emscripten authors
Project URL: https://emscripten.org/
Used For: Compiling the WASM runtime's loader.wasm program
* Emscripten is available under 2 licenses, the MIT license and the
* University of Illinois/NCSA Open Source License.
*
* Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Project Name: XZ Embedded
Project Author: Lasse Collin (Larhzu)
Project URL: https://tukaani.org/xz/embedded.html
Used For: Decompressing the WASM runtime
* Copyright (C) The XZ Embedded authors and contributors
*
* Permission to use, copy, modify, and/or distribute this
* software for any purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Project Name: XZ for Java
Project Author: Lasse Collin (Larhzu)
Project URL: https://tukaani.org/xz/java.html
Used For: Compression in the MakeWASMClientBundle command
* Copyright (C) The XZ for Java authors and contributors
*
* Permission to use, copy, modify, and/or distribute this
* software for any purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

@ -823,6 +823,13 @@ eaglercraft.options.badVideoSettingsDetected.fixSettings=Fix Settings
eaglercraft.options.badVideoSettingsDetected.continueAnyway=Continue Anyway
eaglercraft.options.badVideoSettingsDetected.doNotShowAgain=Do Not Show Again
eaglercraft.options.vsyncReEnabled.title=Issues Detected
eaglercraft.options.vsyncReEnabled.0=You are using the WASM-GC client
eaglercraft.options.vsyncReEnabled.1=VSync has been automatically re-enabled
eaglercraft.options.vsyncReEnabled.2=Using the WASM-GC version of EaglercraftX without
eaglercraft.options.vsyncReEnabled.3=VSync enabled causes bad input lag, sorry!
eaglercraft.options.vsyncReEnabled.continue=Continue
selectServer.title=Select Server
selectServer.empty=empty
selectServer.select=Join Server

@ -1 +1 @@
{"pluginName":"EaglercraftXBungee","pluginVersion":"1.3.3","pluginButton":"Download \"EaglerXBungee-1.3.3.jar\"","pluginFilename":"EaglerXBungee.zip"}
{"pluginName":"EaglercraftXBungee","pluginVersion":"1.3.4","pluginButton":"Download \"EaglerXBungee-1.3.4.jar\"","pluginFilename":"EaglerXBungee.zip"}

@ -43,6 +43,7 @@ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack;
import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFontRenderer;
import net.lax1dude.eaglercraft.v1_8.minecraft.EnumInputEvent;
import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenGenericErrorMessage;
import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenVSyncReEnabled;
import net.lax1dude.eaglercraft.v1_8.minecraft.GuiScreenVideoSettingsWarning;
import net.lax1dude.eaglercraft.v1_8.notifications.ServerNotificationRenderer;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglerMeshLoader;
@ -524,15 +525,26 @@ public class Minecraft implements IThreadListener {
mainMenu = new GuiConnecting(mainMenu, this, this.serverName, this.serverPort);
}
mainMenu = new GuiScreenEditProfile(mainMenu);
if (!EagRuntime.getConfiguration().isForceProfanityFilter() && !gameSettings.hasShownProfanityFilter) {
mainMenu = new GuiScreenContentWarning(mainMenu);
}
boolean vsyncScreen = false;
if (EagRuntime.getConfiguration().isEnforceVSync() && Display.isVSyncSupported() && !gameSettings.enableVsync) {
gameSettings.enableVsync = true;
gameSettings.saveOptions();
vsyncScreen = true;
}
int vidIssues = gameSettings.checkBadVideoSettings();
if (vidIssues != 0) {
mainMenu = new GuiScreenVideoSettingsWarning(mainMenu, vidIssues);
}
mainMenu = new GuiScreenEditProfile(mainMenu);
if (!EagRuntime.getConfiguration().isForceProfanityFilter() && !gameSettings.hasShownProfanityFilter) {
mainMenu = new GuiScreenContentWarning(mainMenu);
if (vsyncScreen) {
mainMenu = new GuiScreenVSyncReEnabled(mainMenu);
}
this.displayGuiScreen(mainMenu);

@ -430,7 +430,7 @@ public class GuiOverlayDebug extends Gui {
protected List<String> getDebugInfoRight() {
ArrayList arraylist;
if (EagRuntime.getPlatformType() != EnumPlatformType.JAVASCRIPT) {
if (EagRuntime.getPlatformType() == EnumPlatformType.DESKTOP) {
long i = EagRuntime.maxMemory();
long j = EagRuntime.totalMemory();
long k = EagRuntime.freeMemory();

@ -118,7 +118,7 @@ public abstract class GuiScreen extends Gui implements GuiYesNoCallback {
long millis = EagRuntime.steadyTimeMillis();
long closeKeyTimeout = millis - showingCloseKey;
if (closeKeyTimeout < 3000l) {
if (closeKeyTimeout < 3000l && showingCloseKey != 0l) {
int alpha1 = 0xC0000000;
int alpha2 = 0xFF000000;
if (closeKeyTimeout > 2500l) {

@ -374,92 +374,119 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient {
double d0 = (double) packetIn.getX() / 32.0D;
double d1 = (double) packetIn.getY() / 32.0D;
double d2 = (double) packetIn.getZ() / 32.0D;
Object object = null;
if (packetIn.getType() == 10) {
Entity object = null;
boolean b = false;
switch (packetIn.getType()) {
case 10:
object = EntityMinecart.func_180458_a(this.clientWorldController, d0, d1, d2,
EntityMinecart.EnumMinecartType.byNetworkID(packetIn.func_149009_m()));
} else if (packetIn.getType() == 90) {
break;
case 90:
b = true;
Entity entity = this.clientWorldController.getEntityByID(packetIn.func_149009_m());
if (entity instanceof EntityPlayer) {
object = new EntityFishHook(this.clientWorldController, d0, d1, d2, (EntityPlayer) entity);
}
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 60) {
break;
case 60:
object = new EntityArrow(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 61) {
break;
case 61:
object = new EntitySnowball(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 71) {
break;
case 71:
b = true;
object = new EntityItemFrame(this.clientWorldController,
new BlockPos(MathHelper.floor_double(d0), MathHelper.floor_double(d1), MathHelper.floor_double(d2)),
EnumFacing.getHorizontal(packetIn.func_149009_m()));
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 77) {
break;
case 77:
b = true;
object = new EntityLeashKnot(this.clientWorldController, new BlockPos(MathHelper.floor_double(d0),
MathHelper.floor_double(d1), MathHelper.floor_double(d2)));
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 65) {
break;
case 65:
object = new EntityEnderPearl(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 72) {
break;
case 72:
object = new EntityEnderEye(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 76) {
break;
case 76:
object = new EntityFireworkRocket(this.clientWorldController, d0, d1, d2, (ItemStack) null);
} else if (packetIn.getType() == 63) {
break;
case 63:
b = true;
object = new EntityLargeFireball(this.clientWorldController, d0, d1, d2,
(double) packetIn.getSpeedX() / 8000.0D, (double) packetIn.getSpeedY() / 8000.0D,
(double) packetIn.getSpeedZ() / 8000.0D);
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 64) {
break;
case 64:
b = true;
object = new EntitySmallFireball(this.clientWorldController, d0, d1, d2,
(double) packetIn.getSpeedX() / 8000.0D, (double) packetIn.getSpeedY() / 8000.0D,
(double) packetIn.getSpeedZ() / 8000.0D);
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 66) {
break;
case 66:
b = true;
object = new EntityWitherSkull(this.clientWorldController, d0, d1, d2,
(double) packetIn.getSpeedX() / 8000.0D, (double) packetIn.getSpeedY() / 8000.0D,
(double) packetIn.getSpeedZ() / 8000.0D);
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 62) {
break;
case 62:
object = new EntityEgg(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 73) {
break;
case 73:
b = true;
object = new EntityPotion(this.clientWorldController, d0, d1, d2, packetIn.func_149009_m());
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 75) {
break;
case 75:
b = true;
object = new EntityExpBottle(this.clientWorldController, d0, d1, d2);
packetIn.func_149002_g(0);
} else if (packetIn.getType() == 1) {
break;
case 1:
object = new EntityBoat(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 50) {
break;
case 50:
object = new EntityTNTPrimed(this.clientWorldController, d0, d1, d2, (EntityLivingBase) null);
} else if (packetIn.getType() == 78) {
break;
case 78:
object = new EntityArmorStand(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 51) {
break;
case 51:
object = new EntityEnderCrystal(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 2) {
break;
case 2:
object = new EntityItem(this.clientWorldController, d0, d1, d2);
} else if (packetIn.getType() == 70) {
break;
case 70:
b = true;
object = new EntityFallingBlock(this.clientWorldController, d0, d1, d2,
Block.getStateById(packetIn.func_149009_m() & '\uffff'));
break;
}
if (b) {
// fix for compiler bug
packetIn.func_149002_g(0);
}
if (object != null) {
((Entity) object).serverPosX = packetIn.getX();
((Entity) object).serverPosY = packetIn.getY();
((Entity) object).serverPosZ = packetIn.getZ();
((Entity) object).rotationPitch = (float) (packetIn.getPitch() * 360) / 256.0F;
((Entity) object).rotationYaw = (float) (packetIn.getYaw() * 360) / 256.0F;
Entity[] aentity = ((Entity) object).getParts();
object.serverPosX = packetIn.getX();
object.serverPosY = packetIn.getY();
object.serverPosZ = packetIn.getZ();
object.rotationPitch = (float) (packetIn.getPitch() * 360) / 256.0F;
object.rotationYaw = (float) (packetIn.getYaw() * 360) / 256.0F;
Entity[] aentity = object.getParts();
if (aentity != null) {
int i = packetIn.getEntityID() - ((Entity) object).getEntityId();
int i = packetIn.getEntityID() - object.getEntityId();
for (int j = 0; j < aentity.length; ++j) {
aentity[j].setEntityId(aentity[j].getEntityId() + i);
}
}
((Entity) object).setEntityId(packetIn.getEntityID());
this.clientWorldController.addEntityToWorld(packetIn.getEntityID(), (Entity) object);
object.setEntityId(packetIn.getEntityID());
this.clientWorldController.addEntityToWorld(packetIn.getEntityID(), object);
if (packetIn.func_149009_m() > 0) {
if (packetIn.getType() == 60) {
Entity entity1 = this.clientWorldController.getEntityByID(packetIn.func_149009_m());
@ -468,8 +495,8 @@ public class NetHandlerPlayClient implements INetHandlerPlayClient {
}
}
((Entity) object).setVelocity((double) packetIn.getSpeedX() / 8000.0D,
(double) packetIn.getSpeedY() / 8000.0D, (double) packetIn.getSpeedZ() / 8000.0D);
object.setVelocity((double) packetIn.getSpeedX() / 8000.0D, (double) packetIn.getSpeedY() / 8000.0D,
(double) packetIn.getSpeedZ() / 8000.0D);
}
}

@ -69,22 +69,28 @@ public class BlockRendererDispatcher implements IResourceManagerReloadListener {
public boolean renderBlock(IBlockState state, BlockPos pos, IBlockAccess blockAccess,
WorldRenderer worldRendererIn) {
try {
boolean res;
int i = state.getBlock().getRenderType();
if (i == -1) {
return false;
res = false;
} else {
switch (i) {
case 1:
return this.fluidRenderer.renderFluid(blockAccess, state, pos, worldRendererIn);
res = this.fluidRenderer.renderFluid(blockAccess, state, pos, worldRendererIn);
break;
case 2:
return false;
res = false;
break;
case 3:
IBakedModel ibakedmodel = this.getModelFromBlockState(state, blockAccess, pos);
return this.blockModelRenderer.renderModel(blockAccess, ibakedmodel, state, pos, worldRendererIn);
res = this.blockModelRenderer.renderModel(blockAccess, ibakedmodel, state, pos, worldRendererIn);
break;
default:
return false;
res = false;
break;
}
}
return res;
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Tesselating block in world");
CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being tesselated");

@ -50,6 +50,9 @@ public class CrashReport {
private String[] stacktrace;
public CrashReport(String descriptionIn, Throwable causeThrowable) {
if (causeThrowable == null) {
throw new NullPointerException("Crash report created for null throwable!");
}
this.description = descriptionIn;
this.cause = causeThrowable;
this.stacktrace = EagRuntime.getStackTraceElements(causeThrowable);
@ -83,7 +86,7 @@ public class CrashReport {
+ System.getProperty("java.vm.vendor");
}
});
if (EagRuntime.getPlatformType() != EnumPlatformType.JAVASCRIPT) {
if (EagRuntime.getPlatformType() == EnumPlatformType.DESKTOP) {
this.theReportCategory.addCrashSectionCallable("Memory", new Callable<String>() {
public String call() {
long i = EagRuntime.maxMemory();

@ -8,6 +8,8 @@ import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.minecraft.EntityConstructor;
@ -288,11 +290,16 @@ public class EntityList {
Set<String> set = stringToClassMapping.keySet();
ArrayList arraylist = Lists.newArrayList();
for (String s : set) {
Class oclass = (Class) stringToClassMapping.get(s);
if ((oclass.getModifiers() & 1024) != 1024) {
arraylist.add(s);
// TODO: Eventually TeaVM will support getModifiers
if (EagRuntime.getPlatformType() != EnumPlatformType.WASM_GC) {
for (String s : set) {
Class oclass = (Class) stringToClassMapping.get(s);
if ((oclass.getModifiers() & 1024) != 1024) {
arraylist.add(s);
}
}
} else {
arraylist.addAll(set);
}
arraylist.add("LightningBolt");

@ -37,7 +37,6 @@ import net.minecraft.util.ReportedException;
import net.minecraft.util.Util;
import net.minecraft.util.Vec3;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.World;
import net.minecraft.world.WorldManager;
import net.minecraft.world.WorldServer;
@ -307,11 +306,7 @@ public abstract class MinecraftServer implements Runnable, ICommandSender, IThre
+ worldserver.provider.getDimensionName());
}
try {
worldserver.saveAllChunks(true, (IProgressUpdate) null);
} catch (MinecraftException minecraftexception) {
logger.warn(minecraftexception.getMessage());
}
worldserver.saveAllChunks(true, (IProgressUpdate) null);
}
}

@ -2772,14 +2772,6 @@ public abstract class World implements IBlockAccess {
public void sendQuittingDisconnectingPacket() {
}
/**+
* Checks whether the session lock file was modified by another
* process
*/
public void checkSessionLock() throws MinecraftException {
this.saveHandler.checkSessionLock();
}
public void setTotalWorldTime(long worldTime) {
this.worldInfo.setWorldTotalTime(worldTime);
}

@ -765,7 +765,7 @@ public class WorldServer extends World implements IThreadListener {
/**+
* Saves all chunks to disk while updating progress bar.
*/
public void saveAllChunks(boolean progressCallback, IProgressUpdate parIProgressUpdate) throws MinecraftException {
public void saveAllChunks(boolean progressCallback, IProgressUpdate parIProgressUpdate) {
if (this.chunkProvider.canSave()) {
if (parIProgressUpdate != null) {
parIProgressUpdate.displaySavingString("Saving level");
@ -802,8 +802,7 @@ public class WorldServer extends World implements IThreadListener {
/**+
* Saves the chunks to disk.
*/
protected void saveLevel() throws MinecraftException {
this.checkSessionLock();
protected void saveLevel() {
this.worldInfo.setBorderSize(this.getWorldBorder().getDiameter());
this.worldInfo.getBorderCenterX(this.getWorldBorder().getCenterX());
this.worldInfo.getBorderCenterZ(this.getWorldBorder().getCenterZ());

@ -64,12 +64,6 @@ public class WorldServerMulti extends WorldServer {
});
}
/**+
* Saves the chunks to disk.
*/
protected void saveLevel() throws MinecraftException {
}
public World init() {
this.mapStorage = this.delegate.getMapStorage();
this.worldScoreboard = this.delegate.getScoreboard();

@ -1,7 +1,6 @@
package net.minecraft.world.chunk.storage;
import java.io.IOException;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
@ -31,7 +30,7 @@ public interface IChunkLoader {
*/
Chunk loadChunk(World var1, int var2, int var3) throws IOException;
void saveChunk(World var1, Chunk var2) throws IOException, MinecraftException;
void saveChunk(World var1, Chunk var2) throws IOException;
/**+
* Save extra data associated with this Chunk not normally saved

@ -16,7 +16,6 @@ import net.minecraft.util.IProgressUpdate;
import net.minecraft.util.LongHashMap;
import net.minecraft.util.ReportedException;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.BiomeGenBase;
@ -200,11 +199,7 @@ public class ChunkProviderServer implements IChunkProvider {
} catch (IOException ioexception) {
logger.error("Couldn\'t save chunk");
logger.error(ioexception);
} catch (MinecraftException minecraftexception) {
logger.error("Couldn\'t save chunk; already in use by another instance of Minecraft?");
logger.error(minecraftexception);
}
}
}

@ -2,7 +2,6 @@ package net.minecraft.world.storage;
import net.minecraft.nbt.NBTTagCompound;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.chunk.storage.IChunkLoader;
@ -32,11 +31,6 @@ public interface ISaveHandler {
*/
WorldInfo loadWorldInfo();
/**+
* Checks the session lock to prevent save collisions
*/
void checkSessionLock() throws MinecraftException;
/**+
* initializes and returns the chunk loader for the specified
* world provider

@ -8,7 +8,6 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.chunk.storage.IChunkLoader;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
@ -64,12 +63,6 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData {
return this.worldDirectory;
}
/**+
* Checks the session lock to prevent save collisions
*/
public void checkSessionLock() throws MinecraftException {
}
/**+
* initializes and returns the chunk loader for the specified
* world provider

@ -2,7 +2,6 @@ package net.minecraft.world.storage;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.chunk.storage.IChunkLoader;
@ -34,12 +33,6 @@ public class SaveHandlerMP implements ISaveHandler {
return null;
}
/**+
* Checks the session lock to prevent save collisions
*/
public void checkSessionLock() throws MinecraftException {
}
/**+
* Saves the given World Info with the given NBTTagCompound as
* the Player.

@ -198,6 +198,11 @@ public class DesktopClientConfigAdapter implements IClientConfigAdapter {
return false;
}
@Override
public boolean isEnforceVSync() {
return false;
}
@Override
public IClientConfigAdapterHooks getHooks() {
return hooks;

@ -10,6 +10,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformWebRTC;
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.sp.server.IWASMCrashCallback;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection;
/**
@ -91,4 +92,8 @@ public class ServerPlatformSingleplayer {
return false;
}
public static void setCrashCallbackWASM(IWASMCrashCallback callback) {
}
}

@ -99,4 +99,9 @@ public class EagUtils {
return EaglercraftUUID.nameUUIDFromBytes(("EaglercraftXClientOld:" + name).getBytes(StandardCharsets.UTF_8));
}
public static void sleepPrint(String string) {
System.out.println(string);
PlatformRuntime.sleep(500);
}
}

@ -10,7 +10,7 @@ public class EaglercraftVersion {
/// Customize these to fit your fork:
public static final String projectForkName = "EaglercraftX";
public static final String projectForkVersion = "u43";
public static final String projectForkVersion = "u44";
public static final String projectForkVendor = "lax1dude";
public static final String projectForkURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8";
@ -20,20 +20,20 @@ public class EaglercraftVersion {
public static final String projectOriginName = "EaglercraftX";
public static final String projectOriginAuthor = "lax1dude";
public static final String projectOriginRevision = "1.8";
public static final String projectOriginVersion = "u43";
public static final String projectOriginVersion = "u44";
public static final String projectOriginURL = "https://gitlab.com/lax1dude/eaglercraftx-1.8"; // rest in peace
// EPK Version Identifier
public static final String EPKVersionIdentifier = "u43"; // Set to null to disable EPK version check
public static final String EPKVersionIdentifier = "u44"; // Set to null to disable EPK version check
// Updating configuration
public static final boolean enableUpdateService = true;
public static final String updateBundlePackageName = "net.lax1dude.eaglercraft.v1_8.client";
public static final int updateBundlePackageVersionInt = 43;
public static final int updateBundlePackageVersionInt = 44;
public static final String updateLatestLocalStorageKey = "latestUpdate_" + updateBundlePackageName;
@ -52,8 +52,8 @@ public class EaglercraftVersion {
// Miscellaneous variables:
public static final String mainMenuStringA = "Minecraft 1.8.8";
public static final String mainMenuStringB = projectOriginName + " " +
projectOriginRevision + "-" + projectOriginVersion + " ultimate";
public static final String mainMenuStringB = projectOriginName + " " + projectOriginRevision + "-"
+ projectOriginVersion + " ultimate [" + EagRuntime.getPlatformType().getName() + "]";
public static final String mainMenuStringC = "";
public static final String mainMenuStringD = "Resources Copyright Mojang AB";
@ -63,6 +63,9 @@ public class EaglercraftVersion {
public static final String mainMenuStringG = "Collector's Edition";
public static final String mainMenuStringH = "PBR Shaders";
public static final String screenRecordingFilePrefix = projectOriginName + " "
+ projectOriginRevision + "-" + projectOriginVersion;
public static final long demoWorldSeed = (long) "North Carolina".hashCode();
public static final boolean mainMenuEnableGithubButton = false;

@ -16,7 +16,7 @@ package net.lax1dude.eaglercraft.v1_8.internal;
*
*/
public enum EnumPlatformType {
DESKTOP("Desktop"), JAVASCRIPT("JavaScript"), WASM_GC("ASM");
DESKTOP("Desktop"), JAVASCRIPT("JS"), WASM_GC("WASM-GC");
private final String name;

@ -98,6 +98,8 @@ public interface IClientConfigAdapter {
boolean isRamdiskMode();
boolean isEnforceVSync();
IClientConfigAdapterHooks getHooks();
}

@ -39,11 +39,9 @@ class VFileOutputStream extends EaglerOutputStream {
try {
copyBuffer.put(buf, 0, count);
copyBuffer.flip();
try {
vfsFile.getFS().eaglerWrite(vfsFile.path, copyBuffer);
}catch(Throwable t) {
throw new IOException("Could not write stream contents to file!", t);
}
vfsFile.getFS().eaglerWrite(vfsFile.path, copyBuffer);
}catch(Throwable t) {
throw new IOException("Could not write stream contents to file!", t);
}finally {
PlatformRuntime.freeByteBuffer(copyBuffer);
}

@ -159,6 +159,10 @@ public class Logger {
}
private void logExcp(final Level level, String h, Throwable msg) {
if(msg == null) {
log(level, "{}: <null>", h);
return;
}
log(level, "{}: {}", h, msg.toString());
EagRuntime.getStackTrace(msg, (e) -> log(level, " at {}", e));
PlatformRuntime.printJSExceptionIfBrowser(msg);

@ -189,11 +189,15 @@ public class EaglerFontRenderer extends FontRenderer {
if(hasStrike) {
GlStateManager.color(0.25f, 0.25f, 0.25f, 1.0f);
GlStateManager.translate(1.0f, 1.0f, 0.0f);
GlStateManager.disableTexture2D();
tessellator.draw();
GlStateManager.translate(-1.0f, -1.0f, 0.0f);
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
GlStateManager.enableTexture2D();
InstancedFontRenderer.render(8, 8, texScale, texScale, true);
GlStateManager.disableTexture2D();
EaglercraftGPU.renderAgain();
GlStateManager.enableTexture2D();
}else {
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
InstancedFontRenderer.render(8, 8, texScale, texScale, true);
@ -201,7 +205,9 @@ public class EaglerFontRenderer extends FontRenderer {
}else {
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
if(hasStrike) {
GlStateManager.disableTexture2D();
tessellator.draw();
GlStateManager.enableTexture2D();
}
InstancedFontRenderer.render(8, 8, texScale, texScale, false);
}

@ -0,0 +1,51 @@
package net.lax1dude.eaglercraft.v1_8.minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
public class GuiScreenVSyncReEnabled extends GuiScreen {
private GuiScreen cont;
public GuiScreenVSyncReEnabled(GuiScreen cont) {
this.cont = cont;
}
public void initGui() {
this.buttonList.clear();
this.buttonList.add(new GuiButton(0, this.width / 2 - 100, this.height / 6 + 136, I18n.format("options.vsyncReEnabled.continue")));
}
public void drawScreen(int par1, int par2, float par3) {
this.drawDefaultBackground();
this.drawCenteredString(fontRendererObj, I18n.format("options.vsyncReEnabled.title"), this.width / 2, 70, 11184810);
this.drawCenteredString(fontRendererObj, I18n.format("options.vsyncReEnabled.0"), this.width / 2, 95, 16777215);
this.drawCenteredString(fontRendererObj, I18n.format("options.vsyncReEnabled.1"), this.width / 2, 120, 16777215);
this.drawCenteredString(fontRendererObj, I18n.format("options.vsyncReEnabled.2"), this.width / 2, 145, 16777215);
this.drawCenteredString(fontRendererObj, I18n.format("options.vsyncReEnabled.3"), this.width / 2, 160, 16777215);
super.drawScreen(par1, par2, par3);
}
protected void actionPerformed(GuiButton par1GuiButton) {
if(par1GuiButton.id == 0) {
this.mc.displayGuiScreen(cont);
}
}
}

@ -65,7 +65,7 @@ class ServerQueryImpl implements IServerQuery {
IWebSocketFrame frame = lst.get(i);
alive = true;
if(pingTimer == -1) {
pingTimer = PlatformRuntime.steadyTimeMillis() - pingStart;
pingTimer = frame.getTimestamp() - pingStart;
if(pingTimer < 1) {
pingTimer = 1;
}

@ -15,6 +15,7 @@ import org.apache.commons.lang3.StringUtils;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
@ -84,6 +85,7 @@ public class SingleplayerServerController implements ISaveFormat {
issuesDetected.clear();
statusState = IntegratedServerState.WORLD_WORKER_BOOTING;
loggingState = true;
callFailed = false;
boolean singleThreadSupport = ClientPlatformSingleplayer.isSingleThreadModeSupported();
if(!singleThreadSupport && forceSingleThread) {
throw new UnsupportedOperationException("Single thread mode is not supported!");
@ -294,10 +296,12 @@ public class SingleplayerServerController implements ISaveFormat {
}
}
boolean logWindowState = PlatformApplication.isShowingDebugConsole();
if(loggingState != logWindowState) {
loggingState = logWindowState;
sendIPCPacket(new IPCPacket1BEnableLogging(logWindowState));
if(EagRuntime.getPlatformType() == EnumPlatformType.JAVASCRIPT) {
boolean logWindowState = PlatformApplication.isShowingDebugConsole();
if(loggingState != logWindowState) {
loggingState = logWindowState;
sendIPCPacket(new IPCPacket1BEnableLogging(logWindowState));
}
}
if(ClientPlatformSingleplayer.isRunningSingleThreadMode()) {

@ -41,6 +41,8 @@ class LANClientPeer {
protected long startTime;
protected String localICECandidate = null;
protected LANClientPeer(String clientId) {
this.clientId = clientId;
this.startTime = EagRuntime.steadyTimeMillis();
@ -50,7 +52,13 @@ class LANClientPeer {
protected void handleICECandidates(String candidates) {
if(state == SENT_DESCRIPTION) {
PlatformWebRTC.serverLANPeerICECandidates(clientId, candidates);
state = RECEIVED_ICE_CANDIDATE;
if(localICECandidate != null) {
LANServerController.lanRelaySocket.writePacket(new RelayPacket03ICECandidate(clientId, localICECandidate));
localICECandidate = null;
state = SENT_ICE_CANDIDATE;
}else {
state = RECEIVED_ICE_CANDIDATE;
}
}else {
logger.error("Relay [{}] unexpected IPacket03ICECandidate for '{}'", LANServerController.lanRelaySocket.getURI(), clientId);
}
@ -100,6 +108,12 @@ class LANClientPeer {
disconnect();
}else {
switch(state) {
case SENT_DESCRIPTION:{
if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
localICECandidate = ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates;
continue read_loop;
}
}
case RECEIVED_ICE_CANDIDATE: {
if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
LANServerController.lanRelaySocket.writePacket(new RelayPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates));
@ -136,7 +150,7 @@ class LANClientPeer {
}
}
if(state != CLOSED) {
logger.error("LAN client '{}' had an accident: {}", clientId, evt.getClass().getSimpleName());
logger.error("LAN client '{}' had an accident: {} (state {})", clientId, evt.getClass().getSimpleName(), state);
}
disconnect();
return;

@ -82,7 +82,7 @@ public class LANServerController {
}
}
EagUtils.sleep(50);
}while(EagRuntime.steadyTimeMillis() - millis < 1000l);
}while(EagRuntime.steadyTimeMillis() - millis < 2500l);
logger.info("Relay [{}] relay provide ICE servers timeout", sock.getURI());
closeLAN();
return null;

@ -8,7 +8,6 @@ import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
@ -88,7 +87,7 @@ public class EaglerChunkLoader extends AnvilChunkLoader {
}
@Override
public void saveChunk(World var1, Chunk var2) throws IOException, MinecraftException {
public void saveChunk(World var1, Chunk var2) throws IOException {
NBTTagCompound chunkData = new NBTTagCompound();
this.writeChunkToNBT(var2, var1, chunkData);
NBTTagCompound fileData = new NBTTagCompound();

@ -485,6 +485,8 @@ public class EaglerIntegratedServerWorker {
// signal thread startup successful
sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF));
ServerPlatformSingleplayer.setCrashCallbackWASM(EaglerIntegratedServerWorker::sendIntegratedServerCrashWASMCB);
while(true) {
mainLoop(false);
ServerPlatformSingleplayer.immediateContinue();
@ -525,4 +527,11 @@ public class EaglerIntegratedServerWorker {
mainLoop(true);
}
public static void sendIntegratedServerCrashWASMCB(String stringValue, boolean terminated) {
sendIPCPacket(new IPCPacket15Crashed(stringValue));
if(terminated) {
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacketFFProcessKeepAlive.EXITED));
}
}
}

@ -0,0 +1,22 @@
package net.lax1dude.eaglercraft.v1_8.sp.server;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
public interface IWASMCrashCallback {
void callback(String crashReport, boolean terminated);
}

@ -139,14 +139,24 @@ public class IntegratedVoiceService {
}
public void handleVoiceSignalPacketTypeConnect(EntityPlayerMP sender) {
if (voicePlayers.containsKey(sender.getUniqueID())) {
EaglercraftUUID senderUuid = sender.getUniqueID();
if (voicePlayers.containsKey(senderUuid)) {
return;
}
boolean hasNoOtherPlayers = voicePlayers.isEmpty();
voicePlayers.put(sender.getUniqueID(), sender);
voicePlayers.put(senderUuid, sender);
if (hasNoOtherPlayers) {
return;
}
GameMessagePacket v3p = null;
GameMessagePacket v4p = null;
for(EntityPlayerMP conn : voicePlayers.values()) {
if(conn.playerNetServerHandler.getEaglerMessageProtocol().ver <= 3) {
conn.playerNetServerHandler.sendEaglerMessage(v3p == null ? (v3p = new SPacketVoiceSignalConnectV3EAG(senderUuid.msb, senderUuid.lsb, true, false)) : v3p);
} else {
conn.playerNetServerHandler.sendEaglerMessage(v4p == null ? (v4p = new SPacketVoiceSignalConnectAnnounceV4EAG(senderUuid.msb, senderUuid.lsb)) : v4p);
}
}
Collection<SPacketVoiceSignalGlobalEAG.UserData> userDatas = new ArrayList<>(voicePlayers.size());
for(EntityPlayerMP player : voicePlayers.values()) {
EaglercraftUUID uuid = player.getUniqueID();

@ -94,13 +94,16 @@ public class VoiceClientController {
}
public static void handleVoiceSignalPacketTypeGlobalNew(Collection<SPacketVoiceSignalGlobalEAG.UserData> voicePlayers) {
boolean isGlobal = voiceChannel == EnumVoiceChannelType.GLOBAL;
uuidToNameLookup.clear();
for (SPacketVoiceSignalGlobalEAG.UserData player : voicePlayers) {
EaglercraftUUID uuid = new EaglercraftUUID(player.uuidMost, player.uuidLeast);
if(player.username != null) {
uuidToNameLookup.put(uuid, player.username);
}
sendPacketRequestIfNeeded(uuid);
if (isGlobal) {
sendPacketRequestIfNeeded(uuid);
}
}
}
@ -138,7 +141,7 @@ public class VoiceClientController {
}
public static void handleVoiceSignalPacketTypeConnectAnnounce(EaglercraftUUID user) {
if (voiceChannel != EnumVoiceChannelType.NONE) sendPacketRequest(user);
if (voiceChannel != EnumVoiceChannelType.NONE && (voiceChannel == EnumVoiceChannelType.GLOBAL || listeningSet.contains(user))) sendPacketRequest(user);
}
public static void handleVoiceSignalPacketTypeDisconnect(EaglercraftUUID user) {

@ -48,11 +48,13 @@ public class BootMenuEntryPoint {
private static native void setHasAlreadyBooted();
public static boolean checkShouldLaunchFlag(Window win) {
wasManuallyInvoked = false;
int flag = BootMenuDataManager.getBootMenuFlags(win);
if(flag == -1) {
return IBootMenuConfigAdapter.instance.isShowBootMenuOnLaunch() && !getHasAlreadyBooted();
}
if((flag & 2) != 0) {
wasManuallyInvoked = true;
BootMenuDataManager.setBootMenuFlags(win, flag & ~2);
setHasAlreadyBooted();
return true;
@ -63,6 +65,7 @@ public class BootMenuEntryPoint {
private static boolean hasInit = false;
private static byte[] signatureData = null;
private static byte[] bundleData = null;
public static boolean wasManuallyInvoked = false;
public static void launchMenu(Window parentWindow, HTMLElement parentElement) {
signatureData = PlatformUpdateSvc.getClientSignatureData();

@ -144,7 +144,7 @@ public class BootMenuMain {
bootMenuFatOfflineLoader = new BootMenuFatOfflineLoader(parentWindow.getDocument().getHead());
logger.info("Entering boot menu display state");
eventQueue.clear();
changeState(new MenuStateBoot(true));
changeState(new MenuStateBoot(!BootMenuEntryPoint.wasManuallyInvoked));
enterUpdateLoop();
}

@ -74,6 +74,12 @@ public class OfflineDownloadParser {
logger.info("Detected format: EAGLERCRAFTX_1_8_OFFLINE (International)");
return EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE;
}
if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse("]OFNI[ :partstooBredaoL")), 0, 16384)) {
if(offlineDownloadData.indexOf(StringUtils.reverse(",46esab;maerts-tetco/noitacilppa:atad\" = IRUstessa.stpOXtfarcrelgae.wodniw")) != -1) {
logger.info("Detected format: EAGLERCRAFTX_1_8_OFFLINE (WASM-GC)");
return EnumOfflineParseType.EAGLERCRAFTX_1_8_OFFLINE;
}
}
if(foundWithin(offlineDownloadData.indexOf(StringUtils.reverse("{ = stpOtfarcrelgae.wodniw")), 32, 2048) && foundWithin(offlineDownloadData.indexOf(StringUtils.reverse(">\"rekrow_ps\"=di \"rekrowrelgae/txet\"=epyt tpircs<")), 4194304, offlineDownloadData.length() - 1048576)) {
logger.info("Detected format: EAGLERCRAFTX_1_5_NEW_OFFLINE");
return EnumOfflineParseType.EAGLERCRAFT_1_5_NEW_OFFLINE;

@ -1569,7 +1569,7 @@ public class PlatformInput {
isOnMobilePressAnyKey = true;
setupAnyKeyScreenMobile(allowBootMenu);
if(pressAnyKeyScreenMobile() && allowBootMenu) {
PlatformRuntime.enterBootMenu();
PlatformRuntime.enterBootMenu(true);
}
}finally {
isOnMobilePressAnyKey = false;

@ -185,8 +185,7 @@ public class PlatformRuntime {
}
CSSStyleDeclaration style = root.getStyle();
style.setProperty("overflowX", "hidden");
style.setProperty("overflowY", "hidden");
style.setProperty("overflow", "hidden");
TeaVMClientConfigAdapter teavmCfg = (TeaVMClientConfigAdapter) getClientConfigAdapter();
boolean allowBootMenu = teavmCfg.isAllowBootMenu();
@ -241,8 +240,7 @@ public class PlatformRuntime {
style.setProperty("position", "relative");
style.setProperty("width", "100%");
style.setProperty("height", "100%");
style.setProperty("overflowX", "hidden");
style.setProperty("overflowY", "hidden");
style.setProperty("overflow", "hidden");
root.appendChild(parent);
ClientMain.configRootElement = parent; // hack
@ -421,7 +419,7 @@ public class PlatformRuntime {
Collections.sort(exts);
logger.info("Unlocked the following OpenGL ES extensions:");
for(int i = 0, l = exts.size(); i < l; ++i) {
logger.info(" - " + exts.get(i));
logger.info(" - {}", exts.get(i));
}
}
@ -443,7 +441,7 @@ public class PlatformRuntime {
if(allowBootMenu && BootMenuEntryPoint.checkShouldLaunchFlag(win)) {
logger.info("Boot menu enable flag is set, entering boot menu...");
enterBootMenu();
enterBootMenu(BootMenuEntryPoint.wasManuallyInvoked);
}
byte[] finalLoadScreen = PlatformAssets.getResourceBytes("/assets/eagler/eagtek.png");
@ -1134,7 +1132,7 @@ public class PlatformRuntime {
if(PlatformInput.keyboardGetEventKeyState()) {
int key = PlatformInput.keyboardGetEventKey();
if(key == KeyboardConstants.KEY_DELETE || key == KeyboardConstants.KEY_BACK) {
enterBootMenu();
enterBootMenu(true);
}
}
}
@ -1143,7 +1141,7 @@ public class PlatformRuntime {
@JSBody(params = {}, script = "delete __isEaglerX188Running;")
private static native void clearRunningFlag();
static void enterBootMenu() {
static void enterBootMenu(boolean manual) {
if(!getClientConfigAdapter().isAllowBootMenu()) {
throw new IllegalStateException("Boot menu is disabled");
}
@ -1170,7 +1168,7 @@ public class PlatformRuntime {
immediateContinueChannel = null;
clearRunningFlag();
logger.info("Firing boot menu escape signal...");
throw new TeaVMEnterBootMenuException();
throw new TeaVMEnterBootMenuException(manual);
}
public static void postCreate() {

@ -188,7 +188,7 @@ public class PlatformScreenRecord {
TeaVMUtils.addEventListener(mediaRec, "dataavailable", new EventListener<DataAvailableEvent>() {
@Override
public void handleEvent(DataAvailableEvent evt) {
final String fileName = EaglercraftVersion.mainMenuStringB + " - " + EaglerProfile.getName() + " - " + fmt.format(new Date()) + "." + params.codec.fileExt;
final String fileName = EaglercraftVersion.screenRecordingFilePrefix + " - " + EaglerProfile.getName() + " - " + fmt.format(new Date()) + "." + params.codec.fileExt;
if("video/webm".equals(params.codec.container)) {
FixWebMDurationJS.getRecUrl(evt, (int) (PlatformRuntime.steadyTimeMillis() - startTime), url -> {
PlatformApplication.downloadURLWithNameTeaVM(fileName, url, () -> TeaVMUtils.freeDataURL(url));

@ -421,7 +421,7 @@ public class PlatformWebRTC {
final Object[] evtHandler = new Object[1];
evtHandler[0] = (EventListener<Event>) evt -> {
if (!iceCandidates.isEmpty()) {
Window.setTimeout(() -> ((EventListener<Event>)evtHandler[0]).handleEvent(evt), 1);
Window.setTimeout(() -> ((EventListener<Event>)evtHandler[0]).handleEvent(evt), 10);
return;
}
clientDataChannelClosed = false;
@ -541,7 +541,7 @@ public class PlatformWebRTC {
final Object[] evtHandler = new Object[1];
evtHandler[0] = (EventListener<Event>) evt -> {
if (!iceCandidates.isEmpty()) {
Window.setTimeout(() -> ((EventListener<Event>)evtHandler[0]).handleEvent(evt), 1);
Window.setTimeout(() -> ((EventListener<Event>)evtHandler[0]).handleEvent(evt), 10);
return;
}
if (getChannel(evt) == null) return;

@ -200,7 +200,6 @@ public class PlatformWebView {
try {
List<String> sandboxArgs = new ArrayList<>();
sandboxArgs.add("allow-downloads");
sandboxArgs.add("allow-same-origin");
if(options.scriptEnabled) {
sandboxArgs.add("allow-scripts");
sandboxArgs.add("allow-pointer-lock");

@ -202,6 +202,7 @@ public class ClientMain {
}catch(TeaVMEnterBootMenuException ee) {
try {
systemOut.println("ClientMain: [INFO] launching eaglercraftx boot menu");
BootMenuEntryPoint.wasManuallyInvoked = ee.isManual;
BootMenuEntryPoint.launchMenu(Window.current(), configRootElement);
}catch(Throwable t) {
showCrashScreen("Failed to enter boot menu!", t);
@ -550,6 +551,7 @@ public class ClientMain {
}
if(el == null) {
Window.alert("Compatibility error: " + t);
System.err.println("Compatibility error: " + t);
return;
}
@ -573,11 +575,9 @@ public class ClientMain {
+ "<p><br /><span style=\"font-size:1.1em;border-bottom:1px dashed #AAAAAA;padding-bottom:5px;\">Things you can try:</span></p>"
+ "<ol>"
+ "<li><span style=\"font-weight:bold;\">Just try using Eaglercraft on a different device</span>, it isn't a bug it's common sense</li>"
+ "<li style=\"margin-top:7px;\">If you are on a mobile device, please try a proper desktop or a laptop computer</li>"
+ "<li style=\"margin-top:7px;\">If you are using a device with no mouse cursor, please use a device with a mouse cursor</li>"
+ "<li style=\"margin-top:7px;\">If this screen just appeared randomly, try restarting your browser or device</li>"
+ "<li style=\"margin-top:7px;\">If you are not using Chrome/Edge, try installing the latest Google Chrome</li>"
+ "<li style=\"margin-top:7px;\">If your browser is out of date, please update it to the latest version</li>"
+ "<li style=\"margin-top:7px;\">If you are using an old OS such as Windows 7, please try Windows 10 or 11</li>"
+ "</ol>"
+ "</div>");

@ -69,7 +69,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu
private boolean openDebugConsoleOnLaunch = false;
private boolean fixDebugConsoleUnloadListener = false;
private boolean forceWebViewSupport = false;
private boolean enableWebViewCSP = false;
private boolean enableWebViewCSP = true;
private boolean autoFixLegacyStyleAttr = false;
private boolean showBootMenuOnLaunch = false;
private boolean bootMenuBlocksUnsignedClients = false;
@ -77,7 +77,7 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu
private boolean forceProfanityFilter = false;
private boolean forceWebGL1 = false;
private boolean forceWebGL2 = false;
private boolean allowExperimentalWebGL1 = false;
private boolean allowExperimentalWebGL1 = true;
private boolean useWebGLExt = true;
private boolean useDelayOnSwap = false;
private boolean useJOrbisAudioDecoder = false;
@ -550,6 +550,11 @@ public class TeaVMClientConfigAdapter implements IClientConfigAdapter, IBootMenu
return ramdiskMode;
}
@Override
public boolean isEnforceVSync() {
return false;
}
@Override
public IClientConfigAdapterHooks getHooks() {
return hooks;

@ -17,4 +17,10 @@ package net.lax1dude.eaglercraft.v1_8.internal.teavm;
*/
public class TeaVMEnterBootMenuException extends RuntimeException {
public final boolean isManual;
public TeaVMEnterBootMenuException(boolean manual) {
this.isManual = manual;
}
}

@ -35,6 +35,7 @@ import net.lax1dude.eaglercraft.v1_8.internal.teavm.TeaVMUtils;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.IWASMCrashCallback;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
@ -291,4 +292,8 @@ public class ServerPlatformSingleplayer {
}
}
public static void setCrashCallbackWASM(IWASMCrashCallback callback) {
}
}

@ -0,0 +1,45 @@
/**
* @fileoverview loader bootstrap externs
* @externs
*/
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
window.main = function() {};
window.eaglercraftXOpts = {};
window.eaglercraftXOpts.assetsURI = "";
window.eaglercraftXOpts.container = "";
window.__eaglercraftXLoaderContextPre = {};
/** @type {!HTMLElement} */
window.__eaglercraftXLoaderContextPre.rootElement;
/** @type {!Object} */
window.__eaglercraftXLoaderContextPre.eaglercraftXOpts;
/** @type {!ArrayBuffer} */
window.__eaglercraftXLoaderContextPre.theEPWFileBuffer;
/** @type {string} */
window.__eaglercraftXLoaderContextPre.loaderWASMURL;
/** @type {string} */
window.__eaglercraftXLoaderContextPre.splashURL;

@ -0,0 +1,377 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
/**
* @param {*} msg
*/
function logInfo(msg) {
console.log("LoaderBootstrap: [INFO] " + msg);
}
/**
* @param {*} msg
*/
function logWarn(msg) {
console.log("LoaderBootstrap: [WARN] " + msg);
}
/**
* @param {*} msg
*/
function logError(msg) {
console.error("LoaderBootstrap: [ERROR] " + msg);
}
/** @type {function(string,number):ArrayBuffer|null} */
var decodeBase64Impl = null;
/**
* @return {function(string,number):ArrayBuffer}
*/
function createBase64Decoder() {
const revLookup = [];
const code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (var i = 0, len = code.length; i < len; ++i) {
revLookup[code.charCodeAt(i)] = i;
}
revLookup["-".charCodeAt(0)] = 62;
revLookup["_".charCodeAt(0)] = 63;
/**
* @param {string} b64
* @param {number} start
* @return {!Array<number>}
*/
function getLens(b64, start) {
const len = b64.length - start;
if (len % 4 > 0) {
throw new Error("Invalid string. Length must be a multiple of 4");
}
var validLen = b64.indexOf("=", start);
if (validLen === -1) {
validLen = len;
}else {
validLen -= start;
}
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4);
return [validLen, placeHoldersLen];
}
/**
* @param {string} b64
* @param {number} start
* @return {ArrayBuffer}
*/
function decodeImpl(b64, start) {
var tmp;
const lens = getLens(b64, start);
const validLen = lens[0];
const placeHoldersLen = lens[1];
const arr = new Uint8Array(((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen);
var curByte = 0;
const len = (placeHoldersLen > 0 ? validLen - 4 : validLen) + start;
var i;
for (i = start; i < len; i += 4) {
tmp = (revLookup[b64.charCodeAt(i)] << 18) |
(revLookup[b64.charCodeAt(i + 1)] << 12) |
(revLookup[b64.charCodeAt(i + 2)] << 6) |
revLookup[b64.charCodeAt(i + 3)]
arr[curByte++] = (tmp >> 16) & 0xFF
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
if (placeHoldersLen === 2) {
tmp = (revLookup[b64.charCodeAt(i)] << 2) |
(revLookup[b64.charCodeAt(i + 1)] >> 4)
arr[curByte++] = tmp & 0xFF
}else if (placeHoldersLen === 1) {
tmp = (revLookup[b64.charCodeAt(i)] << 10) |
(revLookup[b64.charCodeAt(i + 1)] << 4) |
(revLookup[b64.charCodeAt(i + 2)] >> 2)
arr[curByte++] = (tmp >> 8) & 0xFF
arr[curByte++] = tmp & 0xFF
}
return arr.buffer;
}
return decodeImpl;
}
/**
* @param {string} url
* @param {number} start
* @return {ArrayBuffer}
*/
function decodeBase64(url, start) {
if(!decodeBase64Impl) {
decodeBase64Impl = createBase64Decoder();
}
return decodeBase64Impl(url, start);
}
/**
* @param {number} ms
* @return {!Promise}
*/
function asyncSleep(ms) {
return new Promise(function(resolve) {
setTimeout(resolve, ms);
});
}
/**
* @param {string} url
* @return {!Promise<ArrayBuffer>}
*/
function downloadURL(url) {
return new Promise(function(resolve) {
fetch(url, { "cache": "force-cache" })
.then(function(res) {
return res.arrayBuffer();
})
.then(resolve)
.catch(function(ex) {
logError("Failed to fetch URL! " + ex);
resolve(null);
});
});
}
/**
* @param {string} url
* @return {!Promise<ArrayBuffer>}
*/
function downloadDataURL(url) {
if(!url.startsWith("data:application/octet-stream;base64,")) {
return downloadURL(url);
}else {
return new Promise(function(resolve) {
downloadURL(url).then(function(res) {
if(res) {
resolve(res);
}else {
logWarn("Failed to decode base64 via fetch, doing it the slow way instead...");
try {
resolve(decodeBase64(url, 37));
}catch(ex) {
logError("Failed to decode base64! " + ex);
resolve(null);
}
}
});
});
}
}
/**
* @param {HTMLElement} rootElement
* @param {string} msg
*/
function displayInvalidEPW(rootElement, msg) {
const downloadFailureMsg = /** @type {HTMLElement} */ (document.createElement("h2"));
downloadFailureMsg.style.color = "#AA0000";
downloadFailureMsg.style.padding = "25px";
downloadFailureMsg.style.fontFamily = "sans-serif";
downloadFailureMsg.style["marginBlock"] = "0px";
downloadFailureMsg.appendChild(document.createTextNode(msg));
rootElement.appendChild(downloadFailureMsg);
const downloadFailureMsg2 = /** @type {HTMLElement} */ (document.createElement("h4"));
downloadFailureMsg2.style.color = "#AA0000";
downloadFailureMsg2.style.padding = "25px";
downloadFailureMsg2.style.fontFamily = "sans-serif";
downloadFailureMsg2.style["marginBlock"] = "0px";
downloadFailureMsg2.appendChild(document.createTextNode("Try again later"));
rootElement.style.backgroundColor = "white";
rootElement.appendChild(downloadFailureMsg2);
}
window.main = async function() {
if(typeof window.eaglercraftXOpts === "undefined") {
const msg = "window.eaglercraftXOpts is not defined!";
logError(msg);
alert(msg);
return;
}
const containerId = window.eaglercraftXOpts.container;
if(typeof containerId !== "string") {
const msg = "window.eaglercraftXOpts.container is not a string!";
logError(msg);
alert(msg);
return;
}
var assetsURI = window.eaglercraftXOpts.assetsURI;
if(typeof assetsURI !== "string") {
if((typeof assetsURI === "object") && (typeof assetsURI[0] === "object") && (typeof assetsURI[0]["url"] === "string")) {
assetsURI = assetsURI[0]["url"];
}else {
const msg = "window.eaglercraftXOpts.assetsURI is not a string!";
logError(msg);
alert(msg);
return;
}
}
const rootElement = /** @type {HTMLElement} */ (document.getElementById(containerId));
if(!rootElement) {
const msg = "window.eaglercraftXOpts.container \"" + containerId + "\" is not a known element id!";
logError(msg);
alert(msg);
return;
}
var node;
while(node = rootElement.lastChild) {
rootElement.removeChild(node);
}
const splashElement = /** @type {HTMLElement} */ (document.createElement("div"));
splashElement.style.width = "100%";
splashElement.style.height = "100%";
splashElement.style.setProperty("image-rendering", "pixelated");
splashElement.style.background = "center / contain no-repeat url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAAAAAB3tzPbAAAACXBIWXMAAC4jAAAuIwF4pT92AAAG+UlEQVR42u2cy23jOhRATwbTwGwFvAJoF6BFGjColcGkASNuIPA6C68DN+BADZiCVxLSQBYqIGYBAbSdEvwWkvUzZWfymwlwCQwQUZeXPOT9URPkYs/3bj8QAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAH4x9vPvzFpAhAzM98UILmqfjDf1YT0N/cBk+71v+wDSczHmDeJ6TqO+SIfyD7IvC9g33Yc7dP6CQDxB+q62Hc2xnyJD2Sf5vuzL3Hi5MM0WbCN51u/Y/30ryEGmDVHlhwsY9Y7xlq0CuzVc4lh2n7NkGsnQ1nB7IefmrY/araJcbrq6Ryk9YqW4l3J/dHww1jdej+8kte042EW0Nba1hyWdl+9irq/FNXaD6BbQoexuvf+tQC2vX1+AFvP0kxiuyidfWwEbOtQtK0n0r6xbYCKsLcM21+pLZX3u4984Kq2xlnWDimllRudAXEpkGSHfqMzsmxfWnLWNf9aQznW4wMZWOMJxvGs/Ff5X+yPcD0g3dqZesdsI2f7Z2/73W2JSok9Gqu7P1q/I2qtj0qn/ZkTaCPWO2a0VyjrxY7sNUG1LxRlaE90MpDpGVeAxpaGobN2XPWH0aQVE1stfXPAj0+XzUmcob3aTRdVZ2+tRv+gMNBDaTkZ4k6uhtYPaK7iUkUcx9lgij92gZ6aXmxoDeK8D1hPfm18oBvTfPGwXoVG+4VfXcwl8dEOtCJS7De9M0VTqTA2p081O3kJ+uk5cU/RVN8C262Ms9HMlLHSmhNFTcc9u1uQRX4jMhqyNIk1GRk69a6hb0IDZ3pITnbfNqFuJWE9gbYrfmSqen/SiKy27G0VS20VWc+UEn59/YDPkc+0EunrAXQ/JXucYL+3VutyAqvP5wFvtEoyQPsMJMpKc3v7/Su9ALLkhAJDPCObGTDmonfNHAij3sg5866fmTHGnFt/crroh6vEv/Rq6vhEoP7hWWb2ylSQZP5zOVrDqVxSZnm/xL6OFnZwF3/4JoyGjyXu1X3n0rEFyE5Jzc5KEDfT7s2ZYs52s5e1HU88hB17nKTqAroXWPpXiHbN7R3Q8fVDbjzU6vb8hUbX67FWN8Xo4U5SIWjbukr1knY9XrcwS30aOuTatqa0vkA6cI05dyPrzWBbj7ZZrPUT2O7pdpKFtp4rph0E0AxtfN0u9kNVg25d4BPiDF0+R83dPol7/l4m4yQmQzdX+ISewqTnc8ngp94yaCan4vT+Hc228q8/T35+e8+XueSqCaPmEz9ofdbX6eSqE5iN/m4A8Qd9w/1bAEl2fPmafT3Axdv/ytlFeXUwTZyyf+NA3hWDGPrm+HXtHSdQ7nrz7fvv+MPFe/9Q3nAS+iYA3zcKCYAACIAACIAACIAACIAACIAACIAA1C2Komh++r9cogdv90M0+GoZAVHkSiGSaFmOmJdTRdESiKJ5Je4eovnSldoGNJ44gTBNbx+XH7tDYxwOniAPgEdygGWxTm/jBCAHV0u7xa90PV64IW0uOWdCapK7t600vfF2j4Ad5FCE4IopCSWMSg0Q4NgRVNKrwIBJ1ZDGxXO/5+fxhDvFQ87EsHxZMy9Sli/raMbjf9eqMpiciQG3yYOJwW1eQoBoesNBzG3yKdvqNwie1HMwiXFcwo7L7aMBtlSrC7c79RzyUm5w0f66Gk1vcJs8vFYHxUvy/u8leJz4N8t8vX5ccl04Chz5BOLR+mVVWXX5lsU4ncSOFevL7WFsJbYiPfQpcvJwhNsBxKiwcHDPNnoojzp8Jh8PnusiSMcLd1B8R5i+Igq5/BZKU3IEO8cIpoqw6L5NR8kjuOIaFR6GlmKdvmnhuFTsfqNwTBnzBOo+ZFua+jh3jAZtnksMu/b850wIfh1sVwVPhMEzKK9lz/+7Hi3Kx8CjOajVbVCEz3kIT1wyYnsD6s5t8tUaGLFpTfC7q2TH4rjzHMCoGgqTOJiMFi/TY5kduOJWHfzdtzdFrS4PYBwzhi0LAKcAdTcvKhur+VWQ3/TWcq/+LJG5VahUsILHUDGiGCmKy26cOrxlxwZUsMHlvVDW7lMQwghGOGZpmt6zcdFD47EhtQVyWySQRHUgVDzhmkeClyZFlGmiA5BH0WpyB+twPp/cgQpQBH0Lqt6qaTwfs+OW6Kl/RrdET/WqQi5BgWLDqNxmdV/Mo1X1QX5Ms0Pq/jmaP7d2/b6IVq3HW+a9qT7v6/TDNv2+tVA0hzz8klroc07AbXKmN98YQMppARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAD2//A2iD9ZsgY5XpAAAAAElFTkSuQmCC\") white";
rootElement.appendChild(splashElement);
/** @type {ArrayBuffer} */
var theEPWFileBuffer;
if(assetsURI.startsWith("data:")) {
logInfo("Downloading EPW file \"<data: " + assetsURI.length + " chars>\"...");
theEPWFileBuffer = await downloadDataURL(assetsURI);
}else {
logInfo("Downloading EPW file \"" + assetsURI + "\"...");
theEPWFileBuffer = await downloadURL(assetsURI);
}
var isInvalid = false;
if(!theEPWFileBuffer) {
isInvalid = true;
}else if(theEPWFileBuffer.byteLength < 384) {
logError("The EPW file is too short");
isInvalid = true;
}
if(isInvalid) {
rootElement.removeChild(splashElement);
const msg = "Failed to download EPW file!";
displayInvalidEPW(rootElement, msg);
logError(msg);
return;
}
const dataView = new DataView(theEPWFileBuffer);
if(dataView.getUint32(0, true) !== 608649541 || dataView.getUint32(4, true) !== 1297301847) {
logError("The file is not an EPW file");
isInvalid = true;
}
const phileLength = theEPWFileBuffer.byteLength;
if(dataView.getUint32(8, true) !== phileLength) {
logError("The EPW file is the wrong length");
isInvalid = true;
}
if(isInvalid) {
rootElement.removeChild(splashElement);
const msg = "EPW file is invalid!";
displayInvalidEPW(rootElement, msg);
logError(msg);
return;
}
const textDecoder = new TextDecoder("utf-8");
const splashDataOffset = dataView.getUint32(100, true);
const splashDataLength = dataView.getUint32(104, true);
const splashMIMEOffset = dataView.getUint32(108, true);
const splashMIMELength = dataView.getUint32(112, true);
if(splashDataOffset < 0 || splashDataOffset + splashDataLength > phileLength
|| splashMIMEOffset < 0 || splashMIMEOffset + splashMIMELength > phileLength) {
logError("The EPW file contains an invalid offset (component: splash)");
isInvalid = true;
}
if(isInvalid) {
rootElement.removeChild(splashElement);
const msg = "EPW file is invalid!";
displayInvalidEPW(rootElement, msg);
logError(msg);
return;
}
const splashBinSlice = new Uint8Array(theEPWFileBuffer, splashDataOffset, splashDataLength);
const splashMIMESlice = new Uint8Array(theEPWFileBuffer, splashMIMEOffset, splashMIMELength);
const splashURL = URL.createObjectURL(new Blob([ splashBinSlice ], { "type": textDecoder.decode(splashMIMESlice) }));
logInfo("Loaded splash img: " + splashURL);
splashElement.style.background = "center / contain no-repeat url(\"" + splashURL + "\"), 0px 0px / 1000000% 1000000% no-repeat url(\"" + splashURL + "\") white";
// allow the screen to update
await asyncSleep(20);
const loaderJSOffset = dataView.getUint32(164, true);
const loaderJSLength = dataView.getUint32(168, true);
const loaderWASMOffset = dataView.getUint32(180, true);
const loaderWASMLength = dataView.getUint32(184, true);
if(loaderJSOffset < 0 || loaderJSOffset + loaderJSLength > phileLength
|| loaderWASMOffset < 0 || loaderWASMOffset + loaderWASMLength > phileLength) {
logError("The EPW file contains an invalid offset (component: loader)");
isInvalid = true;
}
if(isInvalid) {
rootElement.removeChild(splashElement);
const msg = "EPW file is invalid!";
displayInvalidEPW(rootElement, msg);
logError(msg);
return;
}
const loaderJSSlice = new Uint8Array(theEPWFileBuffer, loaderJSOffset, loaderJSLength);
const loaderJSURL = URL.createObjectURL(new Blob([ loaderJSSlice ], { "type": "text/javascript;charset=utf-8" }));
logInfo("Loaded loader.js: " + splashURL);
const loaderWASMSlice = new Uint8Array(theEPWFileBuffer, loaderWASMOffset, loaderWASMLength);
const loaderWASMURL = URL.createObjectURL(new Blob([ loaderWASMSlice ], { "type": "application/wasm" }));
logInfo("Loaded loader.wasm: " + loaderWASMURL);
const optsObj = {};
for(const [key, value] of Object.entries(window.eaglercraftXOpts)) {
if(key !== "container" && key !== "assetsURI") {
optsObj[key] = value;
}
}
window.__eaglercraftXLoaderContextPre = {
"rootElement": rootElement,
"eaglercraftXOpts": optsObj,
"theEPWFileBuffer": theEPWFileBuffer,
"loaderWASMURL": loaderWASMURL,
"splashURL": splashURL
};
logInfo("Appending loader.js to document...");
const scriptElement = /** @type {HTMLScriptElement} */ (document.createElement("script"));
scriptElement.type = "text/javascript";
scriptElement.src = loaderJSURL;
document.head.appendChild(scriptElement);
};

@ -0,0 +1,88 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
#ifndef _INCLUDED_EPW_HEADER_H
#define _INCLUDED_EPW_HEADER_H
#include "stdint.h"
struct epw_slice {
uint32_t sliceOffset;
uint32_t sliceLength;
};
struct epw_slice_compressed {
uint32_t sliceOffset;
uint32_t sliceCompressedLength;
uint32_t sliceDecompressedLength;
uint32_t _reserved;
};
struct epw_assets_epk_file {
struct epw_slice filePath;
struct epw_slice loadPath;
struct epw_slice_compressed fileData;
};
struct epw_header {
uint8_t magic[8];
uint32_t fileLength;
uint32_t fileCRC32;
uint16_t versionMajor;
uint16_t versionMinor;
uint32_t clientVersionInt;
struct epw_slice clientPackageName;
struct epw_slice clientOriginName;
struct epw_slice clientOriginVersion;
struct epw_slice clientOriginVendor;
struct epw_slice clientForkName;
struct epw_slice clientForkVersion;
struct epw_slice clientForkVendor;
struct epw_slice metadataSegment;
uint64_t creationTime;
uint32_t numEPKs;
struct epw_slice splashImageData;
struct epw_slice splashImageMIME;
struct epw_slice pressAnyKeyImageData;
struct epw_slice pressAnyKeyImageMIME;
struct epw_slice crashImageData;
struct epw_slice crashImageMIME;
struct epw_slice faviconImageData;
struct epw_slice faviconImageMIME;
struct epw_slice loaderJSData;
uint32_t _reserved_0;
uint32_t _reserved_1;
struct epw_slice loaderWASMData;
uint32_t _reserved_2;
uint32_t _reserved_3;
struct epw_slice_compressed JSPIUnavailableData;
struct epw_slice_compressed eagruntimeJSData;
struct epw_slice_compressed classesWASMData;
struct epw_slice_compressed classesDeobfTEADBGData;
struct epw_slice_compressed classesDeobfWASMData;
struct epw_assets_epk_file assetsEPKs[];
};
#endif

@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
#ifndef _INCLUDED_IMPORTS_H
#define _INCLUDED_IMPORTS_H
#include "stdint.h"
struct epw_load_result_epk {
uint32_t epkData;
uint32_t epkName;
uint32_t epkPath;
};
struct epw_load_result {
uint32_t eagruntimeJSData;
uint32_t classesWASMData;
uint32_t classesDeobfTEADBGData;
uint32_t classesDeobfWASMData;
uint32_t pressAnyKeyImgData;
uint32_t pressAnyKeyImgMIME;
uint32_t crashImgData;
uint32_t crashImgMIME;
uint32_t faviconImgData;
uint32_t faviconImgMIME;
uint32_t numEPKs;
struct epw_load_result_epk epkData[];
};
struct jspi_unsupported_load_result {
uint32_t crashImgData;
uint32_t crashImgMIME;
uint32_t markup;
};
#define LOAD_RESULT_SIZE(numEPKs) (sizeof(struct epw_load_result) + sizeof(struct epw_load_result_epk) * (numEPKs))
extern uint8_t getJSPISupported();
extern uint32_t getEPWLength();
extern void memcpyFromEPW(void* dest, uint32_t off, uint32_t len);
extern uint32_t initResult(uint32_t bufLen);
extern void memcpyToResult(uint32_t bufId, const void* src, uint32_t off, uint32_t len);
extern void memcpyFromEPWToResult(uint32_t bufId, uint32_t dest, uint32_t off, uint32_t len);
extern uint32_t initEPWStringResult(uint32_t off, uint32_t len);
extern void resultFailed(const char* msg);
extern void resultSuccess(const struct epw_load_result* result);
extern void resultJSPIUnsupported(struct jspi_unsupported_load_result* result);
extern void dbgLog(const char* msg);
extern void dbgErr(const char* msg);
#endif

355
src/wasm-gc-teavm-loader/c/main.c Executable file

@ -0,0 +1,355 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "epw_header.h"
#include "imports.h"
#include "xz/xz.h"
static uint32_t initEPWBinaryCompressedHelper(struct epw_slice_compressed* sliceIn, uint32_t epwLen);
static uint32_t initEPWBinaryHelper(struct epw_slice* sliceIn, uint32_t epwLen);
static uint32_t initEPWStringHelper(struct epw_slice* sliceIn, uint32_t epwLen);
#define SLICE_IN_BOUNDS(pSlice, epwLen) (((struct epw_slice*)(pSlice))->sliceOffset + ((struct epw_slice*)(pSlice))->sliceLength <= (epwLen))
// Note: Linux kernel uses 4096
#define DEC_CHUNK_SIZE 16384
static char sprintfBuffer[65];
static uint8_t inputBuffer[DEC_CHUNK_SIZE];
static uint8_t outputBuffer[DEC_CHUNK_SIZE];
const char *const BAD_ALLOC = "Memory allocation failed";
const char *const EPW_INCOMPLETE = "EPW file is incomplete";
const char *const EPW_INVALID = "EPW file is invalid";
int main(int argc, char** argv) {
dbgLog("Executing loader WASM binary...");
uint32_t epwLen = getEPWLength();
snprintf(sprintfBuffer, sizeof(sprintfBuffer), "(Loading a %u byte EPW file)", epwLen);
dbgLog(sprintfBuffer);
if(epwLen < 384) {
resultFailed(EPW_INCOMPLETE);
return -1;
}
struct epw_header* headerPtr = (struct epw_header*)malloc(384);
if(!headerPtr) {
resultFailed(BAD_ALLOC);
return -1;
}
memcpyFromEPW(headerPtr, 0, 384);
// hehehe
if(*(uint64_t*)&headerPtr->magic != *(const uint64_t*)"EAG$WASM") {
resultFailed("The data provided is not an EPW file");
return -1;
}
dbgLog("Checking primary CRC32 checksum...");
uint32_t crc32Val = 0;
uint32_t epwRem = epwLen - 16;
uint32_t j;
xz_crc32_init();
while(epwRem > 0) {
j = epwRem < DEC_CHUNK_SIZE ? epwRem : DEC_CHUNK_SIZE;
memcpyFromEPW(inputBuffer, epwLen - epwRem, j);
epwRem -= j;
crc32Val = xz_crc32(inputBuffer, (size_t)j, crc32Val);
}
if(crc32Val != headerPtr->fileCRC32) {
resultFailed("EPW file has an invalid checksum");
return -1;
}
uint32_t numEPKs = headerPtr->numEPKs;
uint32_t headerLen = ((276 + 32 * numEPKs) + 127) & ~127;
if(headerLen > 384) {
snprintf(sprintfBuffer, sizeof(sprintfBuffer), "Note: Has %u EPK files, extending header to %u bytes", numEPKs, headerLen);
dbgLog(sprintfBuffer);
free(headerPtr);
if(headerLen > epwLen) {
resultFailed(EPW_INCOMPLETE);
return -1;
}
headerPtr = (struct epw_header*)malloc((size_t)headerLen);
if(!headerPtr) {
resultFailed("Memory allocation failed");
return -1;
}
memcpyFromEPW(headerPtr, 0, headerLen);
}
if(!getJSPISupported()) {
dbgErr("JSPI is not supported! The client cannot start");
struct jspi_unsupported_load_result result;
dbgLog("Copying crash image...");
result.crashImgData = initEPWBinaryHelper(&headerPtr->crashImageData, epwLen);
result.crashImgMIME = initEPWStringHelper(&headerPtr->crashImageMIME, epwLen);
dbgLog("Decompressing error screen...");
result.markup = initEPWBinaryCompressedHelper(&headerPtr->JSPIUnavailableData, epwLen);
if(!result.markup) {
resultFailed(EPW_INVALID);
return -1;
}
dbgLog("Displaying error screen...");
resultJSPIUnsupported(&result);
return 0;
}
struct epw_load_result* result = (struct epw_load_result*)malloc(sizeof(struct epw_load_result) + sizeof(struct epw_load_result_epk) * numEPKs);
dbgLog("Copying non-compressed segments...");
result->pressAnyKeyImgData = initEPWBinaryHelper(&headerPtr->pressAnyKeyImageData, epwLen);
if(!result->pressAnyKeyImgData) {
resultFailed(EPW_INVALID);
return -1;
}
result->pressAnyKeyImgMIME = initEPWStringHelper(&headerPtr->pressAnyKeyImageMIME, epwLen);
if(!result->pressAnyKeyImgMIME) {
resultFailed(EPW_INVALID);
return -1;
}
result->crashImgData = initEPWBinaryHelper(&headerPtr->crashImageData, epwLen);
if(!result->crashImgData) {
resultFailed(EPW_INVALID);
return -1;
}
result->crashImgMIME = initEPWStringHelper(&headerPtr->crashImageMIME, epwLen);
if(!result->crashImgMIME) {
resultFailed(EPW_INVALID);
return -1;
}
result->faviconImgData = initEPWBinaryHelper(&headerPtr->faviconImageData, epwLen);
if(!result->faviconImgData) {
resultFailed(EPW_INVALID);
return -1;
}
result->faviconImgMIME = initEPWStringHelper(&headerPtr->faviconImageMIME, epwLen);
if(!result->faviconImgMIME) {
resultFailed(EPW_INVALID);
return -1;
}
dbgLog("Decompressing eagruntime.js...");
result->eagruntimeJSData = initEPWBinaryCompressedHelper(&headerPtr->eagruntimeJSData, epwLen);
if(!result->eagruntimeJSData) {
resultFailed(EPW_INVALID);
return -1;
}
dbgLog("Decompressing classes.wasm...");
result->classesWASMData = initEPWBinaryCompressedHelper(&headerPtr->classesWASMData, epwLen);
if(!result->classesWASMData) {
resultFailed(EPW_INVALID);
return -1;
}
dbgLog("Decompressing classes.wasm.teadbg...");
result->classesDeobfTEADBGData = initEPWBinaryCompressedHelper(&headerPtr->classesDeobfTEADBGData, epwLen);
if(!result->classesDeobfTEADBGData) {
resultFailed(EPW_INVALID);
return -1;
}
dbgLog("Decompressing deobfuscator...");
result->classesDeobfWASMData = initEPWBinaryCompressedHelper(&headerPtr->classesDeobfWASMData, epwLen);
if(!result->classesDeobfWASMData) {
resultFailed(EPW_INVALID);
return -1;
}
result->numEPKs = numEPKs;
for(uint32_t i = 0; i < numEPKs; ++i) {
struct epw_assets_epk_file* epkFile = &headerPtr->assetsEPKs[i];
if(!SLICE_IN_BOUNDS(&epkFile->filePath, epwLen)) {
resultFailed("EPW file contains an invalid offset");
return -1;
}
char nameBuffer[33];
uint32_t nameStrLen = epkFile->filePath.sliceLength;
if(nameStrLen > 32) {
nameStrLen = 32;
}
memcpyFromEPW(nameBuffer, epkFile->filePath.sliceOffset, nameStrLen);
nameBuffer[nameStrLen] = 0;
snprintf(sprintfBuffer, sizeof(sprintfBuffer), "Decompressing assets EPK \"%s\"...", nameBuffer);
dbgLog(sprintfBuffer);
struct epw_load_result_epk* epkId = &result->epkData[i];
epkId->epkData = initEPWBinaryCompressedHelper(&epkFile->fileData, epwLen);
epkId->epkName = initEPWStringHelper(&epkFile->filePath, epwLen);
epkId->epkPath = initEPWStringHelper(&epkFile->loadPath, epwLen);
if(!epkId->epkData || !epkId->epkName || !epkId->epkPath) {
resultFailed(EPW_INVALID);
return -1;
}
}
dbgLog("Loader WASM binary executed successfully!");
resultSuccess(result);
return 0;
}
static uint32_t initEPWBinaryCompressedHelper(struct epw_slice_compressed* sliceIn, uint32_t epwLen) {
if(!SLICE_IN_BOUNDS(sliceIn, epwLen)) {
dbgErr("EPW file contains an invalid compressed offset");
return 0;
}
uint32_t bufId = initResult(sliceIn->sliceDecompressedLength);
struct xz_buf b;
b.in = inputBuffer;
b.in_pos = 0;
b.in_size = 0;
b.out = outputBuffer;
b.out_pos = 0;
b.out_size = DEC_CHUNK_SIZE;
struct xz_dec* s;
enum xz_ret ret;
s = xz_dec_init(XZ_DYNALLOC, (uint32_t)33554432);
if(!s) {
dbgErr("Failed to initialize XZ decompression stream");
return 0;
}
uint32_t bufInPos = 0;
uint32_t bufOutPos = 0;
uint32_t remainingIn = sliceIn->sliceCompressedLength;
uint32_t remainingOut = sliceIn->sliceDecompressedLength;
uint32_t toRead = 0;
uint32_t i;
do {
if(b.in_pos == b.in_size) {
i = (uint32_t)b.in_pos;
if(i > remainingIn) {
dbgErr("Decompression input buffer overflowed");
xz_dec_end(s);
return 0;
}
remainingIn -= i;
toRead = remainingIn < DEC_CHUNK_SIZE ? remainingIn : DEC_CHUNK_SIZE;
b.in_pos = 0;
b.in_size = (size_t)toRead;
memcpyFromEPW(inputBuffer, sliceIn->sliceOffset + bufInPos, toRead);
bufInPos += toRead;
}
ret = xz_dec_run(s, &b);
if(b.out_pos == b.out_size || (ret == XZ_STREAM_END && b.out_pos > 0)) {
i = (uint32_t)b.out_pos;
if(i > remainingOut) {
dbgErr("Decompression output buffer overflowed");
xz_dec_end(s);
return 0;
}
memcpyToResult(bufId, outputBuffer, bufOutPos, i);
remainingOut -= i;
bufOutPos += i;
b.out_pos = 0;
}
}while(ret == XZ_OK);
xz_dec_end(s);
if(ret != XZ_STREAM_END) {
snprintf(sprintfBuffer, sizeof(sprintfBuffer), "Decompression failed, code %u!", (uint32_t)ret);
dbgErr(sprintfBuffer);
return 0;
}
if(b.in_pos > remainingIn) {
dbgErr("Decompression input buffer overflowed");
return 0;
}
remainingIn -= (uint32_t)b.in_pos;
if(remainingIn > 0) {
dbgErr("Decompression completed, but there is still some input data remaining");
return 0;
}
return bufId;
}
static uint32_t initEPWBinaryHelper(struct epw_slice* sliceIn, uint32_t epwLen) {
if(!SLICE_IN_BOUNDS(sliceIn, epwLen)) {
dbgErr("EPW file contains an invalid offset");
return 0;
}else {
uint32_t ret = initResult(sliceIn->sliceLength);
memcpyFromEPWToResult(ret, 0, sliceIn->sliceOffset, sliceIn->sliceLength);
return ret;
}
}
static uint32_t initEPWStringHelper(struct epw_slice* sliceIn, uint32_t epwLen) {
if(!SLICE_IN_BOUNDS(sliceIn, epwLen)) {
dbgErr("EPW file contains an invalid offset");
return 0;
}else {
return initEPWStringResult(sliceIn->sliceOffset, sliceIn->sliceLength);
}
}

@ -0,0 +1,452 @@
/* SPDX-License-Identifier: 0BSD */
/*
* XZ decompressor
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
*/
#ifndef XZ_H
#define XZ_H
#ifdef __KERNEL__
# include <linux/stddef.h>
# include <linux/types.h>
#else
# include <stddef.h>
# include <stdint.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* In Linux, this is used to make extern functions static when needed. */
#ifndef XZ_EXTERN
# define XZ_EXTERN extern
#endif
/**
* enum xz_mode - Operation mode
*
* @XZ_SINGLE: Single-call mode. This uses less RAM than
* multi-call modes, because the LZMA2
* dictionary doesn't need to be allocated as
* part of the decoder state. All required data
* structures are allocated at initialization,
* so xz_dec_run() cannot return XZ_MEM_ERROR.
* @XZ_PREALLOC: Multi-call mode with preallocated LZMA2
* dictionary buffer. All data structures are
* allocated at initialization, so xz_dec_run()
* cannot return XZ_MEM_ERROR.
* @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is
* allocated once the required size has been
* parsed from the stream headers. If the
* allocation fails, xz_dec_run() will return
* XZ_MEM_ERROR.
*
* It is possible to enable support only for a subset of the above
* modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
* or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
* with support for all operation modes, but the preboot code may
* be built with fewer features to minimize code size.
*/
enum xz_mode {
XZ_SINGLE,
XZ_PREALLOC,
XZ_DYNALLOC
};
/**
* enum xz_ret - Return codes
* @XZ_OK: Everything is OK so far. More input or more
* output space is required to continue. This
* return code is possible only in multi-call mode
* (XZ_PREALLOC or XZ_DYNALLOC).
* @XZ_STREAM_END: Operation finished successfully.
* @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding
* is still possible in multi-call mode by simply
* calling xz_dec_run() again.
* Note that this return value is used only if
* XZ_DEC_ANY_CHECK was defined at build time,
* which is not used in the kernel. Unsupported
* check types return XZ_OPTIONS_ERROR if
* XZ_DEC_ANY_CHECK was not defined at build time.
* @XZ_MEM_ERROR: Allocating memory failed. This return code is
* possible only if the decoder was initialized
* with XZ_DYNALLOC. The amount of memory that was
* tried to be allocated was no more than the
* dict_max argument given to xz_dec_init().
* @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than
* allowed by the dict_max argument given to
* xz_dec_init(). This return value is possible
* only in multi-call mode (XZ_PREALLOC or
* XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
* ignores the dict_max argument.
* @XZ_FORMAT_ERROR: File format was not recognized (wrong magic
* bytes).
* @XZ_OPTIONS_ERROR: This implementation doesn't support the requested
* compression options. In the decoder this means
* that the header CRC32 matches, but the header
* itself specifies something that we don't support.
* @XZ_DATA_ERROR: Compressed data is corrupt.
* @XZ_BUF_ERROR: Cannot make any progress. Details are slightly
* different between multi-call and single-call
* mode; more information below.
*
* In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
* to XZ code cannot consume any input and cannot produce any new output.
* This happens when there is no new input available, or the output buffer
* is full while at least one output byte is still pending. Assuming your
* code is not buggy, you can get this error only when decoding a compressed
* stream that is truncated or otherwise corrupt.
*
* In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
* is too small or the compressed input is corrupt in a way that makes the
* decoder produce more output than the caller expected. When it is
* (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
* is used instead of XZ_BUF_ERROR.
*/
enum xz_ret {
XZ_OK,
XZ_STREAM_END,
XZ_UNSUPPORTED_CHECK,
XZ_MEM_ERROR,
XZ_MEMLIMIT_ERROR,
XZ_FORMAT_ERROR,
XZ_OPTIONS_ERROR,
XZ_DATA_ERROR,
XZ_BUF_ERROR
};
/**
* struct xz_buf - Passing input and output buffers to XZ code
* @in: Beginning of the input buffer. This may be NULL if and only
* if in_pos is equal to in_size.
* @in_pos: Current position in the input buffer. This must not exceed
* in_size.
* @in_size: Size of the input buffer
* @out: Beginning of the output buffer. This may be NULL if and only
* if out_pos is equal to out_size.
* @out_pos: Current position in the output buffer. This must not exceed
* out_size.
* @out_size: Size of the output buffer
*
* Only the contents of the output buffer from out[out_pos] onward, and
* the variables in_pos and out_pos are modified by the XZ code.
*/
struct xz_buf {
const uint8_t *in;
size_t in_pos;
size_t in_size;
uint8_t *out;
size_t out_pos;
size_t out_size;
};
/*
* struct xz_dec - Opaque type to hold the XZ decoder state
*/
struct xz_dec;
/**
* xz_dec_init() - Allocate and initialize a XZ decoder state
* @mode: Operation mode
* @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for
* multi-call decoding. This is ignored in single-call mode
* (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
* or 2^n + 2^(n-1) bytes (the latter sizes are less common
* in practice), so other values for dict_max don't make sense.
* In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
* 512 KiB, and 1 MiB are probably the only reasonable values,
* except for kernel and initramfs images where a bigger
* dictionary can be fine and useful.
*
* Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
* once. The caller must provide enough output space or the decoding will
* fail. The output space is used as the dictionary buffer, which is why
* there is no need to allocate the dictionary as part of the decoder's
* internal state.
*
* Because the output buffer is used as the workspace, streams encoded using
* a big dictionary are not a problem in single-call mode. It is enough that
* the output buffer is big enough to hold the actual uncompressed data; it
* can be smaller than the dictionary size stored in the stream headers.
*
* Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
* of memory is preallocated for the LZMA2 dictionary. This way there is no
* risk that xz_dec_run() could run out of memory, since xz_dec_run() will
* never allocate any memory. Instead, if the preallocated dictionary is too
* small for decoding the given input stream, xz_dec_run() will return
* XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
* decoded to avoid allocating excessive amount of memory for the dictionary.
*
* Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
* dict_max specifies the maximum allowed dictionary size that xz_dec_run()
* may allocate once it has parsed the dictionary size from the stream
* headers. This way excessive allocations can be avoided while still
* limiting the maximum memory usage to a sane value to prevent running the
* system out of memory when decompressing streams from untrusted sources.
*
* On success, xz_dec_init() returns a pointer to struct xz_dec, which is
* ready to be used with xz_dec_run(). If memory allocation fails,
* xz_dec_init() returns NULL.
*/
XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
/**
* xz_dec_run() - Run the XZ decoder for a single XZ stream
* @s: Decoder state allocated using xz_dec_init()
* @b: Input and output buffers
*
* The possible return values depend on build options and operation mode.
* See enum xz_ret for details.
*
* Note that if an error occurs in single-call mode (return value is not
* XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
* contents of the output buffer from b->out[b->out_pos] onward are
* undefined. This is true even after XZ_BUF_ERROR, because with some filter
* chains, there may be a second pass over the output buffer, and this pass
* cannot be properly done if the output buffer is truncated. Thus, you
* cannot give the single-call decoder a too small buffer and then expect to
* get that amount valid data from the beginning of the stream. You must use
* the multi-call decoder if you don't want to uncompress the whole stream.
*
* Use xz_dec_run() when XZ data is stored inside some other file format.
* The decoding will stop after one XZ stream has been decompressed. To
* decompress regular .xz files which might have multiple concatenated
* streams, use xz_dec_catrun() instead.
*/
XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
/**
* xz_dec_catrun() - Run the XZ decoder with support for concatenated streams
* @s: Decoder state allocated using xz_dec_init()
* @b: Input and output buffers
* @finish: This is an int instead of bool to avoid requiring stdbool.h.
* As long as more input might be coming, finish must be false.
* When the caller knows that it has provided all the input to
* the decoder (some possibly still in b->in), it must set finish
* to true. Only when finish is true can this function return
* XZ_STREAM_END to indicate successful decompression of the
* file. In single-call mode (XZ_SINGLE) finish is assumed to
* always be true; the caller-provided value is ignored.
*
* This is like xz_dec_run() except that this makes it easy to decode .xz
* files with multiple streams (multiple .xz files concatenated as is).
* The rarely-used Stream Padding feature is supported too, that is, there
* can be null bytes after or between the streams. The number of null bytes
* must be a multiple of four.
*
* When finish is false and b->in_pos == b->in_size, it is possible that
* XZ_BUF_ERROR isn't returned even when no progress is possible (XZ_OK is
* returned instead). This shouldn't matter because in this situation a
* reasonable caller will attempt to provide more input or set finish to
* true for the next xz_dec_catrun() call anyway.
*
* For any struct xz_dec that has been initialized for multi-call mode:
* Once decoding has been started with xz_dec_run() or xz_dec_catrun(),
* the same function must be used until xz_dec_reset() or xz_dec_end().
* Switching between the two decoding functions without resetting results
* in undefined behavior.
*
* xz_dec_catrun() is only available if XZ_DEC_CONCATENATED was defined
* at compile time.
*/
XZ_EXTERN enum xz_ret xz_dec_catrun(struct xz_dec *s, struct xz_buf *b,
int finish);
/**
* xz_dec_reset() - Reset an already allocated decoder state
* @s: Decoder state allocated using xz_dec_init()
*
* This function can be used to reset the multi-call decoder state without
* freeing and reallocating memory with xz_dec_end() and xz_dec_init().
*
* In single-call mode, xz_dec_reset() is always called in the beginning of
* xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
* multi-call mode.
*/
XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
/**
* xz_dec_end() - Free the memory allocated for the decoder state
* @s: Decoder state allocated using xz_dec_init(). If s is NULL,
* this function does nothing.
*/
XZ_EXTERN void xz_dec_end(struct xz_dec *s);
/**
* DOC: MicroLZMA decompressor
*
* This MicroLZMA header format was created for use in EROFS but may be used
* by others too. **In most cases one needs the XZ APIs above instead.**
*
* The compressed format supported by this decoder is a raw LZMA stream
* whose first byte (always 0x00) has been replaced with bitwise-negation
* of the LZMA properties (lc/lp/pb) byte. For example, if lc/lp/pb is
* 3/0/2, the first byte is 0xA2. This way the first byte can never be 0x00.
* Just like with LZMA2, lc + lp <= 4 must be true. The LZMA end-of-stream
* marker must not be used. The unused values are reserved for future use.
*
* These functions aren't used or available in preboot code and thus aren't
* marked with XZ_EXTERN. This avoids warnings about static functions that
* are never defined.
*/
/*
* struct xz_dec_microlzma - Opaque type to hold the MicroLZMA decoder state
*/
struct xz_dec_microlzma;
/**
* xz_dec_microlzma_alloc() - Allocate memory for the MicroLZMA decoder
* @mode: XZ_SINGLE or XZ_PREALLOC
* @dict_size: LZMA dictionary size. This must be at least 4 KiB and
* at most 3 GiB.
*
* In contrast to xz_dec_init(), this function only allocates the memory
* and remembers the dictionary size. xz_dec_microlzma_reset() must be used
* before calling xz_dec_microlzma_run().
*
* The amount of allocated memory is a little less than 30 KiB with XZ_SINGLE.
* With XZ_PREALLOC also a dictionary buffer of dict_size bytes is allocated.
*
* On success, xz_dec_microlzma_alloc() returns a pointer to
* struct xz_dec_microlzma. If memory allocation fails or
* dict_size is invalid, NULL is returned.
*/
extern struct xz_dec_microlzma *xz_dec_microlzma_alloc(enum xz_mode mode,
uint32_t dict_size);
/**
* xz_dec_microlzma_reset() - Reset the MicroLZMA decoder state
* @s: Decoder state allocated using xz_dec_microlzma_alloc()
* @comp_size: Compressed size of the input stream
* @uncomp_size: Uncompressed size of the input stream. A value smaller
* than the real uncompressed size of the input stream can
* be specified if uncomp_size_is_exact is set to false.
* uncomp_size can never be set to a value larger than the
* expected real uncompressed size because it would eventually
* result in XZ_DATA_ERROR.
* @uncomp_size_is_exact: This is an int instead of bool to avoid
* requiring stdbool.h. This should normally be set to true.
* When this is set to false, error detection is weaker.
*/
extern void xz_dec_microlzma_reset(struct xz_dec_microlzma *s,
uint32_t comp_size, uint32_t uncomp_size,
int uncomp_size_is_exact);
/**
* xz_dec_microlzma_run() - Run the MicroLZMA decoder
* @s: Decoder state initialized using xz_dec_microlzma_reset()
* @b: Input and output buffers
*
* This works similarly to xz_dec_run() with a few important differences.
* Only the differences are documented here.
*
* The only possible return values are XZ_OK, XZ_STREAM_END, and
* XZ_DATA_ERROR. This function cannot return XZ_BUF_ERROR: if no progress
* is possible due to lack of input data or output space, this function will
* keep returning XZ_OK. Thus, the calling code must be written so that it
* will eventually provide input and output space matching (or exceeding)
* comp_size and uncomp_size arguments given to xz_dec_microlzma_reset().
* If the caller cannot do this (for example, if the input file is truncated
* or otherwise corrupt), the caller must detect this error by itself to
* avoid an infinite loop.
*
* If the compressed data seems to be corrupt, XZ_DATA_ERROR is returned.
* This can happen also when incorrect dictionary, uncompressed, or
* compressed sizes have been specified.
*
* With XZ_PREALLOC only: As an extra feature, b->out may be NULL to skip over
* uncompressed data. This way the caller doesn't need to provide a temporary
* output buffer for the bytes that will be ignored.
*
* With XZ_SINGLE only: In contrast to xz_dec_run(), the return value XZ_OK
* is also possible and thus XZ_SINGLE is actually a limited multi-call mode.
* After XZ_OK the bytes decoded so far may be read from the output buffer.
* It is possible to continue decoding but the variables b->out and b->out_pos
* MUST NOT be changed by the caller. Increasing the value of b->out_size is
* allowed to make more output space available; one doesn't need to provide
* space for the whole uncompressed data on the first call. The input buffer
* may be changed normally like with XZ_PREALLOC. This way input data can be
* provided from non-contiguous memory.
*/
extern enum xz_ret xz_dec_microlzma_run(struct xz_dec_microlzma *s,
struct xz_buf *b);
/**
* xz_dec_microlzma_end() - Free the memory allocated for the decoder state
* @s: Decoder state allocated using xz_dec_microlzma_alloc().
* If s is NULL, this function does nothing.
*/
extern void xz_dec_microlzma_end(struct xz_dec_microlzma *s);
/*
* Standalone build (userspace build or in-kernel build for boot time use)
* needs a CRC32 implementation. For normal in-kernel use, kernel's own
* CRC32 module is used instead, and users of this module don't need to
* care about the functions below.
*/
#ifndef XZ_INTERNAL_CRC32
# ifdef __KERNEL__
# define XZ_INTERNAL_CRC32 0
# else
# define XZ_INTERNAL_CRC32 1
# endif
#endif
/*
* If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64
* implementation is needed too.
*/
#ifndef XZ_USE_CRC64
# undef XZ_INTERNAL_CRC64
# define XZ_INTERNAL_CRC64 0
#endif
#ifndef XZ_INTERNAL_CRC64
# ifdef __KERNEL__
# error Using CRC64 in the kernel has not been implemented.
# else
# define XZ_INTERNAL_CRC64 1
# endif
#endif
#if XZ_INTERNAL_CRC32
/*
* This must be called before any other xz_* function to initialize
* the CRC32 lookup table.
*/
XZ_EXTERN void xz_crc32_init(void);
/*
* Update CRC32 value using the polynomial from IEEE-802.3. To start a new
* calculation, the third argument must be zero. To continue the calculation,
* the previously returned value is passed as the third argument.
*/
XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);
#endif
#if XZ_INTERNAL_CRC64
/*
* This must be called before any other xz_* function (except xz_crc32_init())
* to initialize the CRC64 lookup table.
*/
XZ_EXTERN void xz_crc64_init(void);
/*
* Update CRC64 value using the polynomial from ECMA-182. To start a new
* calculation, the third argument must be zero. To continue the calculation,
* the previously returned value is passed as the third argument.
*/
XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);
#endif
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,134 @@
/* SPDX-License-Identifier: 0BSD */
/*
* Private includes and definitions for userspace use of XZ Embedded
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*/
#ifndef XZ_CONFIG_H
#define XZ_CONFIG_H
/* Uncomment to enable building of xz_dec_catrun(). */
/* #define XZ_DEC_CONCATENATED */
/* Uncomment to enable CRC64 support. */
/* #define XZ_USE_CRC64 */
/* Uncomment as needed to enable BCJ filter decoders. */
/* #define XZ_DEC_X86 */
/* #define XZ_DEC_ARM */
/* #define XZ_DEC_ARMTHUMB */
/* #define XZ_DEC_ARM64 */
/* #define XZ_DEC_RISCV */
/* #define XZ_DEC_POWERPC */
/* #define XZ_DEC_IA64 */
/* #define XZ_DEC_SPARC */
/*
* Visual Studio 2013 update 2 supports only __inline, not inline.
* MSVC v19.0 / VS 2015 and newer support both.
*/
#if defined(_MSC_VER) && _MSC_VER < 1900 && !defined(inline)
# define inline __inline
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "xz.h"
#define kmalloc(size, flags) malloc(size)
#define kfree(ptr) free(ptr)
#define vmalloc(size) malloc(size)
#define vfree(ptr) free(ptr)
#define memeq(a, b, size) (memcmp(a, b, size) == 0)
#define memzero(buf, size) memset(buf, 0, size)
#ifndef min
# define min(x, y) ((x) < (y) ? (x) : (y))
#endif
#define min_t(type, x, y) min(x, y)
#ifndef fallthrough
# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000
# define fallthrough [[fallthrough]]
# elif defined(__GNUC__) && __GNUC__ >= 7
# define fallthrough __attribute__((__fallthrough__))
# else
# define fallthrough do {} while (0)
# endif
#endif
/*
* Some functions have been marked with __always_inline to keep the
* performance reasonable even when the compiler is optimizing for
* small code size. You may be able to save a few bytes by #defining
* __always_inline to plain inline, but don't complain if the code
* becomes slow.
*
* NOTE: System headers on GNU/Linux may #define this macro already,
* so if you want to change it, you need to #undef it first.
*/
#ifndef __always_inline
# ifdef __GNUC__
# define __always_inline \
inline __attribute__((__always_inline__))
# else
# define __always_inline inline
# endif
#endif
/* Inline functions to access unaligned unsigned 32-bit integers */
#ifndef get_unaligned_le32
static inline uint32_t get_unaligned_le32(const uint8_t *buf)
{
return (uint32_t)buf[0]
| ((uint32_t)buf[1] << 8)
| ((uint32_t)buf[2] << 16)
| ((uint32_t)buf[3] << 24);
}
#endif
#ifndef get_unaligned_be32
static inline uint32_t get_unaligned_be32(const uint8_t *buf)
{
return (uint32_t)(buf[0] << 24)
| ((uint32_t)buf[1] << 16)
| ((uint32_t)buf[2] << 8)
| (uint32_t)buf[3];
}
#endif
#ifndef put_unaligned_le32
static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
{
buf[0] = (uint8_t)val;
buf[1] = (uint8_t)(val >> 8);
buf[2] = (uint8_t)(val >> 16);
buf[3] = (uint8_t)(val >> 24);
}
#endif
#ifndef put_unaligned_be32
static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
{
buf[0] = (uint8_t)(val >> 24);
buf[1] = (uint8_t)(val >> 16);
buf[2] = (uint8_t)(val >> 8);
buf[3] = (uint8_t)val;
}
#endif
/*
* Use get_unaligned_le32() also for aligned access for simplicity. On
* little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
* could save a few bytes in code size.
*/
#ifndef get_le32
# define get_le32 get_unaligned_le32
#endif
#endif

@ -0,0 +1,58 @@
// SPDX-License-Identifier: 0BSD
/*
* CRC32 using the polynomial from IEEE-802.3
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
*/
/*
* This is not the fastest implementation, but it is pretty compact.
* The fastest versions of xz_crc32() on modern CPUs without hardware
* accelerated CRC instruction are 3-5 times as fast as this version,
* but they are bigger and use more memory for the lookup table.
*/
#include "xz_private.h"
/*
* STATIC_RW_DATA is used in the pre-boot environment on some architectures.
* See <linux/decompress/mm.h> for details.
*/
#ifndef STATIC_RW_DATA
# define STATIC_RW_DATA static
#endif
STATIC_RW_DATA uint32_t xz_crc32_table[256];
XZ_EXTERN void xz_crc32_init(void)
{
const uint32_t poly = 0xEDB88320;
uint32_t i;
uint32_t j;
uint32_t r;
for (i = 0; i < 256; ++i) {
r = i;
for (j = 0; j < 8; ++j)
r = (r >> 1) ^ (poly & ~((r & 1) - 1));
xz_crc32_table[i] = r;
}
return;
}
XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
{
crc = ~crc;
while (size != 0) {
crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
--size;
}
return ~crc;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,940 @@
// SPDX-License-Identifier: 0BSD
/*
* .xz Stream decoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*/
#include "xz_private.h"
#include "xz_stream.h"
#ifdef XZ_USE_CRC64
# define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
#else
# define IS_CRC64(check_type) false
#endif
/* Hash used to validate the Index field */
struct xz_dec_hash {
vli_type unpadded;
vli_type uncompressed;
uint32_t crc32;
};
struct xz_dec {
/* Position in dec_main() */
enum {
SEQ_STREAM_HEADER,
SEQ_BLOCK_START,
SEQ_BLOCK_HEADER,
SEQ_BLOCK_UNCOMPRESS,
SEQ_BLOCK_PADDING,
SEQ_BLOCK_CHECK,
SEQ_INDEX,
SEQ_INDEX_PADDING,
SEQ_INDEX_CRC32,
SEQ_STREAM_FOOTER,
SEQ_STREAM_PADDING
} sequence;
/* Position in variable-length integers and Check fields */
uint32_t pos;
/* Variable-length integer decoded by dec_vli() */
vli_type vli;
/* Saved in_pos and out_pos */
size_t in_start;
size_t out_start;
#ifdef XZ_USE_CRC64
/* CRC32 or CRC64 value in Block or CRC32 value in Index */
uint64_t crc;
#else
/* CRC32 value in Block or Index */
uint32_t crc;
#endif
/* Type of the integrity check calculated from uncompressed data */
enum xz_check check_type;
/* Operation mode */
enum xz_mode mode;
/*
* True if the next call to xz_dec_run() is allowed to return
* XZ_BUF_ERROR.
*/
bool allow_buf_error;
/* Information stored in Block Header */
struct {
/*
* Value stored in the Compressed Size field, or
* VLI_UNKNOWN if Compressed Size is not present.
*/
vli_type compressed;
/*
* Value stored in the Uncompressed Size field, or
* VLI_UNKNOWN if Uncompressed Size is not present.
*/
vli_type uncompressed;
/* Size of the Block Header field */
uint32_t size;
} block_header;
/* Information collected when decoding Blocks */
struct {
/* Observed compressed size of the current Block */
vli_type compressed;
/* Observed uncompressed size of the current Block */
vli_type uncompressed;
/* Number of Blocks decoded so far */
vli_type count;
/*
* Hash calculated from the Block sizes. This is used to
* validate the Index field.
*/
struct xz_dec_hash hash;
} block;
/* Variables needed when verifying the Index field */
struct {
/* Position in dec_index() */
enum {
SEQ_INDEX_COUNT,
SEQ_INDEX_UNPADDED,
SEQ_INDEX_UNCOMPRESSED
} sequence;
/* Size of the Index in bytes */
vli_type size;
/* Number of Records (matches block.count in valid files) */
vli_type count;
/*
* Hash calculated from the Records (matches block.hash in
* valid files).
*/
struct xz_dec_hash hash;
} index;
/*
* Temporary buffer needed to hold Stream Header, Block Header,
* and Stream Footer. The Block Header is the biggest (1 KiB)
* so we reserve space according to that. buf[] has to be aligned
* to a multiple of four bytes; the size_t variables before it
* should guarantee this.
*/
struct {
size_t pos;
size_t size;
uint8_t buf[1024];
} temp;
struct xz_dec_lzma2 *lzma2;
#ifdef XZ_DEC_BCJ
struct xz_dec_bcj *bcj;
bool bcj_active;
#endif
};
#ifdef XZ_DEC_ANY_CHECK
/* Sizes of the Check field with different Check IDs */
static const uint8_t check_sizes[16] = {
0,
4, 4, 4,
8, 8, 8,
16, 16, 16,
32, 32, 32,
64, 64, 64
};
#endif
/*
* Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
* must have set s->temp.pos to indicate how much data we are supposed
* to copy into s->temp.buf. Return true once s->temp.pos has reached
* s->temp.size.
*/
static bool fill_temp(struct xz_dec *s, struct xz_buf *b)
{
size_t copy_size = min_t(size_t,
b->in_size - b->in_pos, s->temp.size - s->temp.pos);
memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
b->in_pos += copy_size;
s->temp.pos += copy_size;
if (s->temp.pos == s->temp.size) {
s->temp.pos = 0;
return true;
}
return false;
}
/* Decode a variable-length integer (little-endian base-128 encoding) */
static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,
size_t *in_pos, size_t in_size)
{
uint8_t byte;
if (s->pos == 0)
s->vli = 0;
while (*in_pos < in_size) {
byte = in[*in_pos];
++*in_pos;
s->vli |= (vli_type)(byte & 0x7F) << s->pos;
if ((byte & 0x80) == 0) {
/* Don't allow non-minimal encodings. */
if (byte == 0 && s->pos != 0)
return XZ_DATA_ERROR;
s->pos = 0;
return XZ_STREAM_END;
}
s->pos += 7;
if (s->pos == 7 * VLI_BYTES_MAX)
return XZ_DATA_ERROR;
}
return XZ_OK;
}
/*
* Decode the Compressed Data field from a Block. Update and validate
* the observed compressed and uncompressed sizes of the Block so that
* they don't exceed the values possibly stored in the Block Header
* (validation assumes that no integer overflow occurs, since vli_type
* is normally uint64_t). Update the CRC32 or CRC64 value if presence of
* the CRC32 or CRC64 field was indicated in Stream Header.
*
* Once the decoding is finished, validate that the observed sizes match
* the sizes possibly stored in the Block Header. Update the hash and
* Block count, which are later used to validate the Index field.
*/
static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
{
enum xz_ret ret;
s->in_start = b->in_pos;
s->out_start = b->out_pos;
#ifdef XZ_DEC_BCJ
if (s->bcj_active)
ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
else
#endif
ret = xz_dec_lzma2_run(s->lzma2, b);
s->block.compressed += b->in_pos - s->in_start;
s->block.uncompressed += b->out_pos - s->out_start;
/*
* There is no need to separately check for VLI_UNKNOWN, since
* the observed sizes are always smaller than VLI_UNKNOWN.
*/
if (s->block.compressed > s->block_header.compressed
|| s->block.uncompressed
> s->block_header.uncompressed)
return XZ_DATA_ERROR;
if (s->check_type == XZ_CHECK_CRC32)
s->crc = xz_crc32(b->out + s->out_start,
b->out_pos - s->out_start, s->crc);
#ifdef XZ_USE_CRC64
else if (s->check_type == XZ_CHECK_CRC64)
s->crc = xz_crc64(b->out + s->out_start,
b->out_pos - s->out_start, s->crc);
#endif
if (ret == XZ_STREAM_END) {
if (s->block_header.compressed != VLI_UNKNOWN
&& s->block_header.compressed
!= s->block.compressed)
return XZ_DATA_ERROR;
if (s->block_header.uncompressed != VLI_UNKNOWN
&& s->block_header.uncompressed
!= s->block.uncompressed)
return XZ_DATA_ERROR;
s->block.hash.unpadded += s->block_header.size
+ s->block.compressed;
#ifdef XZ_DEC_ANY_CHECK
s->block.hash.unpadded += check_sizes[s->check_type];
#else
if (s->check_type == XZ_CHECK_CRC32)
s->block.hash.unpadded += 4;
else if (IS_CRC64(s->check_type))
s->block.hash.unpadded += 8;
#endif
s->block.hash.uncompressed += s->block.uncompressed;
s->block.hash.crc32 = xz_crc32(
(const uint8_t *)&s->block.hash,
sizeof(s->block.hash), s->block.hash.crc32);
++s->block.count;
}
return ret;
}
/* Update the Index size and the CRC32 value. */
static void index_update(struct xz_dec *s, const struct xz_buf *b)
{
size_t in_used = b->in_pos - s->in_start;
s->index.size += in_used;
s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
}
/*
* Decode the Number of Records, Unpadded Size, and Uncompressed Size
* fields from the Index field. That is, Index Padding and CRC32 are not
* decoded by this function.
*
* This can return XZ_OK (more input needed), XZ_STREAM_END (everything
* successfully decoded), or XZ_DATA_ERROR (input is corrupt).
*/
static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
{
enum xz_ret ret;
do {
ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
if (ret != XZ_STREAM_END) {
index_update(s, b);
return ret;
}
switch (s->index.sequence) {
case SEQ_INDEX_COUNT:
s->index.count = s->vli;
/*
* Validate that the Number of Records field
* indicates the same number of Records as
* there were Blocks in the Stream.
*/
if (s->index.count != s->block.count)
return XZ_DATA_ERROR;
s->index.sequence = SEQ_INDEX_UNPADDED;
break;
case SEQ_INDEX_UNPADDED:
s->index.hash.unpadded += s->vli;
s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
break;
case SEQ_INDEX_UNCOMPRESSED:
s->index.hash.uncompressed += s->vli;
s->index.hash.crc32 = xz_crc32(
(const uint8_t *)&s->index.hash,
sizeof(s->index.hash),
s->index.hash.crc32);
--s->index.count;
s->index.sequence = SEQ_INDEX_UNPADDED;
break;
}
} while (s->index.count > 0);
return XZ_STREAM_END;
}
/*
* Validate that the next four or eight input bytes match the value
* of s->crc. s->pos must be zero when starting to validate the first byte.
* The "bits" argument allows using the same code for both CRC32 and CRC64.
*/
static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,
uint32_t bits)
{
do {
if (b->in_pos == b->in_size)
return XZ_OK;
if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
return XZ_DATA_ERROR;
s->pos += 8;
} while (s->pos < bits);
s->crc = 0;
s->pos = 0;
return XZ_STREAM_END;
}
#ifdef XZ_DEC_ANY_CHECK
/*
* Skip over the Check field when the Check ID is not supported.
* Returns true once the whole Check field has been skipped over.
*/
static bool check_skip(struct xz_dec *s, struct xz_buf *b)
{
while (s->pos < check_sizes[s->check_type]) {
if (b->in_pos == b->in_size)
return false;
++b->in_pos;
++s->pos;
}
s->pos = 0;
return true;
}
#endif
/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
static enum xz_ret dec_stream_header(struct xz_dec *s)
{
if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
return XZ_FORMAT_ERROR;
if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
!= get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
return XZ_DATA_ERROR;
if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
return XZ_OPTIONS_ERROR;
/*
* Of integrity checks, we support none (Check ID = 0),
* CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
* However, if XZ_DEC_ANY_CHECK is defined, we will accept other
* check types too, but then the check won't be verified and
* a warning (XZ_UNSUPPORTED_CHECK) will be given.
*/
if (s->temp.buf[HEADER_MAGIC_SIZE + 1] > XZ_CHECK_MAX)
return XZ_OPTIONS_ERROR;
s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
#ifdef XZ_DEC_ANY_CHECK
if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
return XZ_UNSUPPORTED_CHECK;
#else
if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
return XZ_OPTIONS_ERROR;
#endif
return XZ_OK;
}
/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
static enum xz_ret dec_stream_footer(struct xz_dec *s)
{
if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
return XZ_DATA_ERROR;
if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
return XZ_DATA_ERROR;
/*
* Validate Backward Size. Note that we never added the size of the
* Index CRC32 field to s->index.size, thus we use s->index.size / 4
* instead of s->index.size / 4 - 1.
*/
if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
return XZ_DATA_ERROR;
if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
return XZ_DATA_ERROR;
/*
* Use XZ_STREAM_END instead of XZ_OK to be more convenient
* for the caller.
*/
return XZ_STREAM_END;
}
/* Decode the Block Header and initialize the filter chain. */
static enum xz_ret dec_block_header(struct xz_dec *s)
{
enum xz_ret ret;
/*
* Validate the CRC32. We know that the temp buffer is at least
* eight bytes so this is safe.
*/
s->temp.size -= 4;
if (xz_crc32(s->temp.buf, s->temp.size, 0)
!= get_le32(s->temp.buf + s->temp.size))
return XZ_DATA_ERROR;
s->temp.pos = 2;
/*
* Catch unsupported Block Flags. We support only one or two filters
* in the chain, so we catch that with the same test.
*/
#ifdef XZ_DEC_BCJ
if (s->temp.buf[1] & 0x3E)
#else
if (s->temp.buf[1] & 0x3F)
#endif
return XZ_OPTIONS_ERROR;
/* Compressed Size */
if (s->temp.buf[1] & 0x40) {
if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
!= XZ_STREAM_END)
return XZ_DATA_ERROR;
s->block_header.compressed = s->vli;
} else {
s->block_header.compressed = VLI_UNKNOWN;
}
/* Uncompressed Size */
if (s->temp.buf[1] & 0x80) {
if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
!= XZ_STREAM_END)
return XZ_DATA_ERROR;
s->block_header.uncompressed = s->vli;
} else {
s->block_header.uncompressed = VLI_UNKNOWN;
}
#ifdef XZ_DEC_BCJ
/* If there are two filters, the first one must be a BCJ filter. */
s->bcj_active = s->temp.buf[1] & 0x01;
if (s->bcj_active) {
if (s->temp.size - s->temp.pos < 2)
return XZ_OPTIONS_ERROR;
ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
if (ret != XZ_OK)
return ret;
/*
* We don't support custom start offset,
* so Size of Properties must be zero.
*/
if (s->temp.buf[s->temp.pos++] != 0x00)
return XZ_OPTIONS_ERROR;
}
#endif
/* Valid Filter Flags always take at least two bytes. */
if (s->temp.size - s->temp.pos < 2)
return XZ_DATA_ERROR;
/* Filter ID = LZMA2 */
if (s->temp.buf[s->temp.pos++] != 0x21)
return XZ_OPTIONS_ERROR;
/* Size of Properties = 1-byte Filter Properties */
if (s->temp.buf[s->temp.pos++] != 0x01)
return XZ_OPTIONS_ERROR;
/* Filter Properties contains LZMA2 dictionary size. */
if (s->temp.size - s->temp.pos < 1)
return XZ_DATA_ERROR;
ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
if (ret != XZ_OK)
return ret;
/* The rest must be Header Padding. */
while (s->temp.pos < s->temp.size)
if (s->temp.buf[s->temp.pos++] != 0x00)
return XZ_OPTIONS_ERROR;
s->temp.pos = 0;
s->block.compressed = 0;
s->block.uncompressed = 0;
return XZ_OK;
}
static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
{
enum xz_ret ret;
/*
* Store the start position for the case when we are in the middle
* of the Index field.
*/
s->in_start = b->in_pos;
while (true) {
switch (s->sequence) {
case SEQ_STREAM_HEADER:
/*
* Stream Header is copied to s->temp, and then
* decoded from there. This way if the caller
* gives us only little input at a time, we can
* still keep the Stream Header decoding code
* simple. Similar approach is used in many places
* in this file.
*/
if (!fill_temp(s, b))
return XZ_OK;
/*
* If dec_stream_header() returns
* XZ_UNSUPPORTED_CHECK, it is still possible
* to continue decoding if working in multi-call
* mode. Thus, update s->sequence before calling
* dec_stream_header().
*/
s->sequence = SEQ_BLOCK_START;
ret = dec_stream_header(s);
if (ret != XZ_OK)
return ret;
fallthrough;
case SEQ_BLOCK_START:
/* We need one byte of input to continue. */
if (b->in_pos == b->in_size)
return XZ_OK;
/* See if this is the beginning of the Index field. */
if (b->in[b->in_pos] == 0) {
s->in_start = b->in_pos++;
s->sequence = SEQ_INDEX;
break;
}
/*
* Calculate the size of the Block Header and
* prepare to decode it.
*/
s->block_header.size
= ((uint32_t)b->in[b->in_pos] + 1) * 4;
s->temp.size = s->block_header.size;
s->temp.pos = 0;
s->sequence = SEQ_BLOCK_HEADER;
fallthrough;
case SEQ_BLOCK_HEADER:
if (!fill_temp(s, b))
return XZ_OK;
ret = dec_block_header(s);
if (ret != XZ_OK)
return ret;
s->sequence = SEQ_BLOCK_UNCOMPRESS;
fallthrough;
case SEQ_BLOCK_UNCOMPRESS:
ret = dec_block(s, b);
if (ret != XZ_STREAM_END)
return ret;
s->sequence = SEQ_BLOCK_PADDING;
fallthrough;
case SEQ_BLOCK_PADDING:
/*
* Size of Compressed Data + Block Padding
* must be a multiple of four. We don't need
* s->block.compressed for anything else
* anymore, so we use it here to test the size
* of the Block Padding field.
*/
while (s->block.compressed & 3) {
if (b->in_pos == b->in_size)
return XZ_OK;
if (b->in[b->in_pos++] != 0)
return XZ_DATA_ERROR;
++s->block.compressed;
}
s->sequence = SEQ_BLOCK_CHECK;
fallthrough;
case SEQ_BLOCK_CHECK:
if (s->check_type == XZ_CHECK_CRC32) {
ret = crc_validate(s, b, 32);
if (ret != XZ_STREAM_END)
return ret;
}
else if (IS_CRC64(s->check_type)) {
ret = crc_validate(s, b, 64);
if (ret != XZ_STREAM_END)
return ret;
}
#ifdef XZ_DEC_ANY_CHECK
else if (!check_skip(s, b)) {
return XZ_OK;
}
#endif
s->sequence = SEQ_BLOCK_START;
break;
case SEQ_INDEX:
ret = dec_index(s, b);
if (ret != XZ_STREAM_END)
return ret;
s->sequence = SEQ_INDEX_PADDING;
fallthrough;
case SEQ_INDEX_PADDING:
while ((s->index.size + (b->in_pos - s->in_start))
& 3) {
if (b->in_pos == b->in_size) {
index_update(s, b);
return XZ_OK;
}
if (b->in[b->in_pos++] != 0)
return XZ_DATA_ERROR;
}
/* Finish the CRC32 value and Index size. */
index_update(s, b);
/* Compare the hashes to validate the Index field. */
if (!memeq(&s->block.hash, &s->index.hash,
sizeof(s->block.hash)))
return XZ_DATA_ERROR;
s->sequence = SEQ_INDEX_CRC32;
fallthrough;
case SEQ_INDEX_CRC32:
ret = crc_validate(s, b, 32);
if (ret != XZ_STREAM_END)
return ret;
s->temp.size = STREAM_HEADER_SIZE;
s->sequence = SEQ_STREAM_FOOTER;
fallthrough;
case SEQ_STREAM_FOOTER:
if (!fill_temp(s, b))
return XZ_OK;
return dec_stream_footer(s);
case SEQ_STREAM_PADDING:
/* Never reached, only silencing a warning */
break;
}
}
/* Never reached */
}
/*
* xz_dec_run() is a wrapper for dec_main() to handle some special cases in
* multi-call and single-call decoding.
*
* In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
* are not going to make any progress anymore. This is to prevent the caller
* from calling us infinitely when the input file is truncated or otherwise
* corrupt. Since zlib-style API allows that the caller fills the input buffer
* only when the decoder doesn't produce any new output, we have to be careful
* to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
* after the second consecutive call to xz_dec_run() that makes no progress.
*
* In single-call mode, if we couldn't decode everything and no error
* occurred, either the input is truncated or the output buffer is too small.
* Since we know that the last input byte never produces any output, we know
* that if all the input was consumed and decoding wasn't finished, the file
* must be corrupt. Otherwise the output buffer has to be too small or the
* file is corrupt in a way that decoding it produces too big output.
*
* If single-call decoding fails, we reset b->in_pos and b->out_pos back to
* their original values. This is because with some filter chains there won't
* be any valid uncompressed data in the output buffer unless the decoding
* actually succeeds (that's the price to pay of using the output buffer as
* the workspace).
*/
XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
{
size_t in_start;
size_t out_start;
enum xz_ret ret;
if (DEC_IS_SINGLE(s->mode))
xz_dec_reset(s);
in_start = b->in_pos;
out_start = b->out_pos;
ret = dec_main(s, b);
if (DEC_IS_SINGLE(s->mode)) {
if (ret == XZ_OK)
ret = b->in_pos == b->in_size
? XZ_DATA_ERROR : XZ_BUF_ERROR;
if (ret != XZ_STREAM_END) {
b->in_pos = in_start;
b->out_pos = out_start;
}
} else if (ret == XZ_OK && in_start == b->in_pos
&& out_start == b->out_pos) {
if (s->allow_buf_error)
ret = XZ_BUF_ERROR;
s->allow_buf_error = true;
} else {
s->allow_buf_error = false;
}
return ret;
}
#ifdef XZ_DEC_CONCATENATED
XZ_EXTERN enum xz_ret xz_dec_catrun(struct xz_dec *s, struct xz_buf *b,
int finish)
{
enum xz_ret ret;
if (DEC_IS_SINGLE(s->mode)) {
xz_dec_reset(s);
finish = true;
}
while (true) {
if (s->sequence == SEQ_STREAM_PADDING) {
/*
* Skip Stream Padding. Its size must be a multiple
* of four bytes which is tracked with s->pos.
*/
while (true) {
if (b->in_pos == b->in_size) {
/*
* Note that if we are repeatedly
* given no input and finish is false,
* we will keep returning XZ_OK even
* though no progress is being made.
* The lack of XZ_BUF_ERROR support
* isn't a problem here because a
* reasonable caller will eventually
* provide more input or set finish
* to true.
*/
if (!finish)
return XZ_OK;
if (s->pos != 0)
return XZ_DATA_ERROR;
return XZ_STREAM_END;
}
if (b->in[b->in_pos] != 0x00) {
if (s->pos != 0)
return XZ_DATA_ERROR;
break;
}
++b->in_pos;
s->pos = (s->pos + 1) & 3;
}
/*
* More input remains. It should be a new Stream.
*
* In single-call mode xz_dec_run() will always call
* xz_dec_reset(). Thus, we need to do it here only
* in multi-call mode.
*/
if (DEC_IS_MULTI(s->mode))
xz_dec_reset(s);
}
ret = xz_dec_run(s, b);
if (ret != XZ_STREAM_END)
break;
s->sequence = SEQ_STREAM_PADDING;
}
return ret;
}
#endif
XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
{
struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (s == NULL)
return NULL;
s->mode = mode;
#ifdef XZ_DEC_BCJ
s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
if (s->bcj == NULL)
goto error_bcj;
#endif
s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
if (s->lzma2 == NULL)
goto error_lzma2;
xz_dec_reset(s);
return s;
error_lzma2:
#ifdef XZ_DEC_BCJ
xz_dec_bcj_end(s->bcj);
error_bcj:
#endif
kfree(s);
return NULL;
}
XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
{
s->sequence = SEQ_STREAM_HEADER;
s->allow_buf_error = false;
s->pos = 0;
s->crc = 0;
memzero(&s->block, sizeof(s->block));
memzero(&s->index, sizeof(s->index));
s->temp.pos = 0;
s->temp.size = STREAM_HEADER_SIZE;
}
XZ_EXTERN void xz_dec_end(struct xz_dec *s)
{
if (s != NULL) {
xz_dec_lzma2_end(s->lzma2);
#ifdef XZ_DEC_BCJ
xz_dec_bcj_end(s->bcj);
#endif
kfree(s);
}
}

@ -0,0 +1,203 @@
/* SPDX-License-Identifier: 0BSD */
/*
* LZMA2 definitions
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
*/
#ifndef XZ_LZMA2_H
#define XZ_LZMA2_H
/* Range coder constants */
#define RC_SHIFT_BITS 8
#define RC_TOP_BITS 24
#define RC_TOP_VALUE (1 << RC_TOP_BITS)
#define RC_BIT_MODEL_TOTAL_BITS 11
#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
#define RC_MOVE_BITS 5
/*
* Maximum number of position states. A position state is the lowest pb
* number of bits of the current uncompressed offset. In some places there
* are different sets of probabilities for different position states.
*/
#define POS_STATES_MAX (1 << 4)
/*
* This enum is used to track which LZMA symbols have occurred most recently
* and in which order. This information is used to predict the next symbol.
*
* Symbols:
* - Literal: One 8-bit byte
* - Match: Repeat a chunk of data at some distance
* - Long repeat: Multi-byte match at a recently seen distance
* - Short repeat: One-byte repeat at a recently seen distance
*
* The symbol names are in from STATE_oldest_older_previous. REP means
* either short or long repeated match, and NONLIT means any non-literal.
*/
enum lzma_state {
STATE_LIT_LIT,
STATE_MATCH_LIT_LIT,
STATE_REP_LIT_LIT,
STATE_SHORTREP_LIT_LIT,
STATE_MATCH_LIT,
STATE_REP_LIT,
STATE_SHORTREP_LIT,
STATE_LIT_MATCH,
STATE_LIT_LONGREP,
STATE_LIT_SHORTREP,
STATE_NONLIT_MATCH,
STATE_NONLIT_REP
};
/* Total number of states */
#define STATES 12
/* The lowest 7 states indicate that the previous state was a literal. */
#define LIT_STATES 7
/* Indicate that the latest symbol was a literal. */
static inline void lzma_state_literal(enum lzma_state *state)
{
if (*state <= STATE_SHORTREP_LIT_LIT)
*state = STATE_LIT_LIT;
else if (*state <= STATE_LIT_SHORTREP)
*state -= 3;
else
*state -= 6;
}
/* Indicate that the latest symbol was a match. */
static inline void lzma_state_match(enum lzma_state *state)
{
*state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
}
/* Indicate that the latest state was a long repeated match. */
static inline void lzma_state_long_rep(enum lzma_state *state)
{
*state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
}
/* Indicate that the latest symbol was a short match. */
static inline void lzma_state_short_rep(enum lzma_state *state)
{
*state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
}
/* Test if the previous symbol was a literal. */
static inline bool lzma_state_is_literal(enum lzma_state state)
{
return state < LIT_STATES;
}
/* Each literal coder is divided in three sections:
* - 0x001-0x0FF: Without match byte
* - 0x101-0x1FF: With match byte; match bit is 0
* - 0x201-0x2FF: With match byte; match bit is 1
*
* Match byte is used when the previous LZMA symbol was something else than
* a literal (that is, it was some kind of match).
*/
#define LITERAL_CODER_SIZE 0x300
/* Maximum number of literal coders */
#define LITERAL_CODERS_MAX (1 << 4)
/* Minimum length of a match is two bytes. */
#define MATCH_LEN_MIN 2
/* Match length is encoded with 4, 5, or 10 bits.
*
* Length Bits
* 2-9 4 = Choice=0 + 3 bits
* 10-17 5 = Choice=1 + Choice2=0 + 3 bits
* 18-273 10 = Choice=1 + Choice2=1 + 8 bits
*/
#define LEN_LOW_BITS 3
#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
#define LEN_MID_BITS 3
#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
#define LEN_HIGH_BITS 8
#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
/*
* Maximum length of a match is 273 which is a result of the encoding
* described above.
*/
#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
/*
* Different sets of probabilities are used for match distances that have
* very short match length: Lengths of 2, 3, and 4 bytes have a separate
* set of probabilities for each length. The matches with longer length
* use a shared set of probabilities.
*/
#define DIST_STATES 4
/*
* Get the index of the appropriate probability array for decoding
* the distance slot.
*/
static inline uint32_t lzma_get_dist_state(uint32_t len)
{
return len < DIST_STATES + MATCH_LEN_MIN
? len - MATCH_LEN_MIN : DIST_STATES - 1;
}
/*
* The highest two bits of a 32-bit match distance are encoded using six bits.
* This six-bit value is called a distance slot. This way encoding a 32-bit
* value takes 6-36 bits, larger values taking more bits.
*/
#define DIST_SLOT_BITS 6
#define DIST_SLOTS (1 << DIST_SLOT_BITS)
/* Match distances up to 127 are fully encoded using probabilities. Since
* the highest two bits (distance slot) are always encoded using six bits,
* the distances 0-3 don't need any additional bits to encode, since the
* distance slot itself is the same as the actual distance. DIST_MODEL_START
* indicates the first distance slot where at least one additional bit is
* needed.
*/
#define DIST_MODEL_START 4
/*
* Match distances greater than 127 are encoded in three pieces:
* - distance slot: the highest two bits
* - direct bits: 2-26 bits below the highest two bits
* - alignment bits: four lowest bits
*
* Direct bits don't use any probabilities.
*
* The distance slot value of 14 is for distances 128-191.
*/
#define DIST_MODEL_END 14
/* Distance slots that indicate a distance <= 127. */
#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
/*
* For match distances greater than 127, only the highest two bits and the
* lowest four bits (alignment) is encoded using probabilities.
*/
#define ALIGN_BITS 4
#define ALIGN_SIZE (1 << ALIGN_BITS)
#define ALIGN_MASK (ALIGN_SIZE - 1)
/* Total number of all probability variables */
#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
/*
* LZMA remembers the four most recent match distances. Reusing these
* distances tends to take less space than re-encoding the actual
* distance value.
*/
#define REPS 4
#endif

@ -0,0 +1,165 @@
/* SPDX-License-Identifier: 0BSD */
/*
* Private includes and definitions
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*/
#ifndef XZ_PRIVATE_H
#define XZ_PRIVATE_H
#ifdef __KERNEL__
# include <linux/xz.h>
# include <linux/kernel.h>
# include <asm/unaligned.h>
/* XZ_PREBOOT may be defined only via decompress_unxz.c. */
# ifndef XZ_PREBOOT
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/string.h>
# ifdef CONFIG_XZ_DEC_X86
# define XZ_DEC_X86
# endif
# ifdef CONFIG_XZ_DEC_POWERPC
# define XZ_DEC_POWERPC
# endif
# ifdef CONFIG_XZ_DEC_IA64
# define XZ_DEC_IA64
# endif
# ifdef CONFIG_XZ_DEC_ARM
# define XZ_DEC_ARM
# endif
# ifdef CONFIG_XZ_DEC_ARMTHUMB
# define XZ_DEC_ARMTHUMB
# endif
# ifdef CONFIG_XZ_DEC_SPARC
# define XZ_DEC_SPARC
# endif
# ifdef CONFIG_XZ_DEC_ARM64
# define XZ_DEC_ARM64
# endif
# ifdef CONFIG_XZ_DEC_RISCV
# define XZ_DEC_RISCV
# endif
# ifdef CONFIG_XZ_DEC_MICROLZMA
# define XZ_DEC_MICROLZMA
# endif
# define memeq(a, b, size) (memcmp(a, b, size) == 0)
# define memzero(buf, size) memset(buf, 0, size)
# endif
# define get_le32(p) le32_to_cpup((const uint32_t *)(p))
#else
/*
* For userspace builds, use a separate header to define the required
* macros and functions. This makes it easier to adapt the code into
* different environments and avoids clutter in the Linux kernel tree.
*/
# include "xz_config.h"
#endif
/* If no specific decoding mode is requested, enable support for all modes. */
#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
&& !defined(XZ_DEC_DYNALLOC)
# define XZ_DEC_SINGLE
# define XZ_DEC_PREALLOC
# define XZ_DEC_DYNALLOC
#endif
/*
* The DEC_IS_foo(mode) macros are used in "if" statements. If only some
* of the supported modes are enabled, these macros will evaluate to true or
* false at compile time and thus allow the compiler to omit unneeded code.
*/
#ifdef XZ_DEC_SINGLE
# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
#else
# define DEC_IS_SINGLE(mode) (false)
#endif
#ifdef XZ_DEC_PREALLOC
# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
#else
# define DEC_IS_PREALLOC(mode) (false)
#endif
#ifdef XZ_DEC_DYNALLOC
# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
#else
# define DEC_IS_DYNALLOC(mode) (false)
#endif
#if !defined(XZ_DEC_SINGLE)
# define DEC_IS_MULTI(mode) (true)
#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
#else
# define DEC_IS_MULTI(mode) (false)
#endif
/*
* If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
* XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
*/
#ifndef XZ_DEC_BCJ
# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
|| defined(XZ_DEC_IA64) \
|| defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
|| defined(XZ_DEC_SPARC) || defined(XZ_DEC_ARM64) \
|| defined(XZ_DEC_RISCV)
# define XZ_DEC_BCJ
# endif
#endif
/*
* Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
* before calling xz_dec_lzma2_run().
*/
XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
uint32_t dict_max);
/*
* Decode the LZMA2 properties (one byte) and reset the decoder. Return
* XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
* big enough, and XZ_OPTIONS_ERROR if props indicates something that this
* decoder doesn't support.
*/
XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
uint8_t props);
/* Decode raw LZMA2 stream from b->in to b->out. */
XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
struct xz_buf *b);
/* Free the memory allocated for the LZMA2 decoder. */
XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
#ifdef XZ_DEC_BCJ
/*
* Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
* calling xz_dec_bcj_run().
*/
XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
/*
* Decode the Filter ID of a BCJ filter. This implementation doesn't
* support custom start offsets, so no decoding of Filter Properties
* is needed. Returns XZ_OK if the given Filter ID is supported.
* Otherwise XZ_OPTIONS_ERROR is returned.
*/
XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
/*
* Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
* a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
* must be called directly.
*/
XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
struct xz_dec_lzma2 *lzma2,
struct xz_buf *b);
/* Free the memory allocated for the BCJ filters. */
#define xz_dec_bcj_end(s) kfree(s)
#endif
#endif

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: 0BSD */
/*
* Definitions for handling the .xz file format
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*/
#ifndef XZ_STREAM_H
#define XZ_STREAM_H
#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
# include <linux/crc32.h>
# undef crc32
# define xz_crc32(buf, size, crc) \
(~crc32_le(~(uint32_t)(crc), buf, size))
#endif
/*
* See the .xz file format specification at
* https://tukaani.org/xz/xz-file-format.txt
* to understand the container format.
*/
#define STREAM_HEADER_SIZE 12
#define HEADER_MAGIC "\3757zXZ"
#define HEADER_MAGIC_SIZE 6
#define FOOTER_MAGIC "YZ"
#define FOOTER_MAGIC_SIZE 2
/*
* Variable-length integer can hold a 63-bit unsigned integer or a special
* value indicating that the value is unknown.
*
* Experimental: vli_type can be defined to uint32_t to save a few bytes
* in code size (no effect on speed). Doing so limits the uncompressed and
* compressed size of the file to less than 256 MiB and may also weaken
* error detection slightly.
*/
typedef uint64_t vli_type;
#define VLI_MAX ((vli_type)-1 / 2)
#define VLI_UNKNOWN ((vli_type)-1)
/* Maximum encoded size of a VLI */
#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
/* Integrity Check types */
enum xz_check {
XZ_CHECK_NONE = 0,
XZ_CHECK_CRC32 = 1,
XZ_CHECK_CRC64 = 4,
XZ_CHECK_SHA256 = 10
};
/* Maximum possible Check ID */
#define XZ_CHECK_MAX 15
#endif

@ -0,0 +1,180 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
addToLibrary({
getJSPISupported: function() {
return (typeof WebAssembly.Suspending !== "undefined");
},
getEPWLength: function() {
return epwFile.byteLength;
},
memcpyFromEPW: function(dest, off, len) {
HEAPU8.set(new Uint8Array(epwFile, off, len), dest);
},
initResult: function(bufLen) {
const id = results.length;
results.push(new Uint8Array(bufLen));
return id;
},
memcpyToResult: function(bufId, src, off, len) {
results[bufId].set(new Uint8Array(HEAPU8.buffer, src, len), off);
},
memcpyFromEPWToResult: function(bufId, dest, off, len) {
results[bufId].set(new Uint8Array(epwFile, off, len), dest);
},
initEPWStringResult: function(off, len) {
const id = results.length;
results.push(UTF8Decoder.decode(new Uint8Array(epwFile, off, len)));
return id;
},
resultFailed: function(msg) {
const parentElement = createCrashParentElement();
const messageContainer = document.createElement("div");
messageContainer.setAttribute("style", "z-index:100;position:absolute;top:10%;left:10%;right:10%;bottom:10%;background-color:white;border:2px solid #cccccc;overflow-x:hidden;overflow-y:scroll;");
messageContainer.classList.add("_eaglercraftX_loader_failed_container");
const failMsg = UTF8ToString(msg);
console.error("LoaderMain: [FAILED] " + failMsg);
const failureMsgElement = document.createElement("h2");
failureMsgElement.style.color = "#AA0000";
failureMsgElement.style.padding = "25px";
failureMsgElement.style.fontFamily = "sans-serif";
failureMsgElement.style["marginBlock"] = "0px";
failureMsgElement.appendChild(document.createTextNode(failMsg));
messageContainer.appendChild(failureMsgElement);
const failureMsgElement2 = document.createElement("h4");
failureMsgElement2.style.color = "#AA0000";
failureMsgElement2.style.padding = "25px";
failureMsgElement2.style.fontFamily = "sans-serif";
failureMsgElement2.style["marginBlock"] = "0px";
failureMsgElement2.appendChild(document.createTextNode("Try again later"));
messageContainer.appendChild(failureMsgElement2);
parentElement.appendChild(messageContainer);
},
resultSuccess: function(result) {
const idx = result >> 2;
const eagRuntimeJSURL = URL.createObjectURL(new Blob([results[HEAP32[idx]]], { type: "text/javascript;charset=utf-8" }));
const classesWASMURL = URL.createObjectURL(new Blob([results[HEAP32[idx + 1]]], { type: "application/wasm" }));
const classesDeobfTEADBGURL = URL.createObjectURL(new Blob([results[HEAP32[idx + 2]]], { type: "application/octet-stream" }));
const classesDeobfWASMURL = URL.createObjectURL(new Blob([results[HEAP32[idx + 3]]], { type: "application/wasm" }));
const pressAnyKey = URL.createObjectURL(new Blob([results[HEAP32[idx + 4]]], { type: results[HEAP32[idx + 5]] }));
const crashImg = URL.createObjectURL(new Blob([results[HEAP32[idx + 6]]], { type: results[HEAP32[idx + 7]] }));
const faviconImg = URL.createObjectURL(new Blob([results[HEAP32[idx + 8]]], { type: results[HEAP32[idx + 9]] }));
const numEPKs = HEAP32[idx + 10];
const epkFiles = new Array(numEPKs);
for(var i = 0, j; i < numEPKs; ++i) {
j = idx + 11 + i * 3;
epkFiles[i] = {
data: results[HEAP32[j]],
name: results[HEAP32[j + 1]],
path: results[HEAP32[j + 2]]
};
}
results.length = 0;
window.__eaglercraftXLoaderContext = {
getEaglercraftXOpts: function() {
return optsObj;
},
getEagRuntimeJSURL: function() {
return eagRuntimeJSURL;
},
getClassesWASMURL: function() {
return classesWASMURL;
},
getClassesDeobfWASMURL: function() {
return classesDeobfWASMURL;
},
getClassesTEADBGURL: function() {
return classesDeobfTEADBGURL;
},
getEPKFiles: function() {
return epkFiles;
},
getRootElement: function() {
return rootElement;
},
getMainArgs: function() {
return [];
},
getImageURL: function(idx) {
switch(idx) {
case 0:
return splashURL;
case 1:
return pressAnyKey;
case 2:
return crashImg;
case 3:
return faviconImg;
default:
return null;
}
},
runMain: function(fn) {
setTimeout(fn, 10);
}
};
const scriptElement = document.createElement("script");
scriptElement.type = "text/javascript";
scriptElement.src = eagRuntimeJSURL;
document.head.appendChild(scriptElement);
},
resultJSPIUnsupported: function(result) {
const idx = result >> 2;
const crashImgData = results[HEAP32[idx]];
const crashImgMIME = results[HEAP32[idx + 1]];
const crashImg = crashImgData ? URL.createObjectURL(new Blob([crashImgData], { type: (crashImgMIME || "image/png") })) : null;
const markupData = results[HEAP32[idx + 2]];
const markup = markupData ? UTF8Decoder.decode(markupData) : "<h1>Failed to load error screen</h1>";
const parentElement = createCrashParentElement();
const img = document.createElement("img");
img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");
img.src = crashImg;
parentElement.appendChild(img);
const iframeContainer = document.createElement("div");
iframeContainer.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:2px solid #cccccc;");
iframeContainer.classList.add("_eaglercraftX_jspi_unsupported_container");
const iframe = document.createElement("iframe");
iframe.classList.add("_eaglercraftX_jspi_unsupported_frame");
iframe.setAttribute("style", "border:none;width:100%;height:100%;");
iframe.srcdoc = markup;
iframeContainer.appendChild(iframe);
parentElement.appendChild(iframeContainer);
},
dbgLog: function(msg) {
console.log("LoaderMain: [INFO] " + UTF8ToString(msg));
},
dbgErr: function(msg) {
console.error("LoaderMain: [ERROR] " + UTF8ToString(msg));
}
});

@ -0,0 +1,61 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* 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 HOLDER 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.
*
*/
Module["locateFile"] = function(path) {
if(path === "loader.wasm") {
return window.__eaglercraftXLoaderContextPre.loaderWASMURL;
}else {
return path;
}
};
const rootElement = window.__eaglercraftXLoaderContextPre.rootElement;
const optsObj = window.__eaglercraftXLoaderContextPre.eaglercraftXOpts;
const epwFile = window.__eaglercraftXLoaderContextPre.theEPWFileBuffer;
const splashURL = window.__eaglercraftXLoaderContextPre.splashURL;
const results = [ null ];
function createCrashParentElement() {
var oldSplash = null;
var node;
while(node = rootElement.lastChild) {
if(!oldSplash) {
oldSplash = node;
}
rootElement.removeChild(node);
}
const parentElement = document.createElement("div");
parentElement.classList.add("_eaglercraftX_wrapper_element");
parentElement.setAttribute("style", "position:relative;width:100%;height:100%;overflow:hidden;");
if(oldSplash) {
oldSplash.classList.add("_eaglercraftX_early_splash_element");
oldSplash.style.position = "absolute";
oldSplash.style.top = "0px";
oldSplash.style.left = "0px";
oldSplash.style.right = "0px";
oldSplash.style.bottom = "0px";
oldSplash.style.zIndex = "2";
parentElement.appendChild(oldSplash);
}
rootElement.appendChild(parentElement);
return parentElement;
}

@ -0,0 +1,293 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class Buffer {
private static final int BUFFER_INCREMENT = 256;
private static final int[] mask = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f,
0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff,
0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
0xffffffff };
int ptr = 0;
byte[] buffer = null;
int endbit = 0;
int endbyte = 0;
int storage = 0;
public void writeinit() {
buffer = new byte[BUFFER_INCREMENT];
ptr = 0;
buffer[0] = (byte) '\0';
storage = BUFFER_INCREMENT;
}
public void write(byte[] s) {
for (int i = 0; i < s.length; i++) {
if (s[i] == 0)
break;
write(s[i], 8);
}
}
public void read(byte[] s, int bytes) {
int i = 0;
while (bytes-- != 0) {
s[i++] = (byte) (read(8));
}
}
void reset() {
ptr = 0;
buffer[0] = (byte) '\0';
endbit = endbyte = 0;
}
public void writeclear() {
buffer = null;
}
public void readinit(byte[] buf, int bytes) {
readinit(buf, 0, bytes);
}
public void readinit(byte[] buf, int start, int bytes) {
ptr = start;
buffer = buf;
endbit = endbyte = 0;
storage = bytes;
}
public void write(int value, int bits) {
if (endbyte + 4 >= storage) {
byte[] foo = new byte[storage + BUFFER_INCREMENT];
System.arraycopy(buffer, 0, foo, 0, storage);
buffer = foo;
storage += BUFFER_INCREMENT;
}
value &= mask[bits];
bits += endbit;
buffer[ptr] |= (byte) (value << endbit);
if (bits >= 8) {
buffer[ptr + 1] = (byte) (value >>> (8 - endbit));
if (bits >= 16) {
buffer[ptr + 2] = (byte) (value >>> (16 - endbit));
if (bits >= 24) {
buffer[ptr + 3] = (byte) (value >>> (24 - endbit));
if (bits >= 32) {
if (endbit > 0)
buffer[ptr + 4] = (byte) (value >>> (32 - endbit));
else
buffer[ptr + 4] = 0;
}
}
}
}
endbyte += bits / 8;
ptr += bits / 8;
endbit = bits & 7;
}
public int look(int bits) {
int ret;
int m = mask[bits];
bits += endbit;
if (endbyte + 4 >= storage) {
if (endbyte + (bits - 1) / 8 >= storage)
return (-1);
}
ret = ((buffer[ptr]) & 0xff) >>> endbit;
if (bits > 8) {
ret |= ((buffer[ptr + 1]) & 0xff) << (8 - endbit);
if (bits > 16) {
ret |= ((buffer[ptr + 2]) & 0xff) << (16 - endbit);
if (bits > 24) {
ret |= ((buffer[ptr + 3]) & 0xff) << (24 - endbit);
if (bits > 32 && endbit != 0) {
ret |= ((buffer[ptr + 4]) & 0xff) << (32 - endbit);
}
}
}
}
return (m & ret);
}
public int look1() {
if (endbyte >= storage)
return (-1);
return ((buffer[ptr] >> endbit) & 1);
}
public void adv(int bits) {
bits += endbit;
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
}
public void adv1() {
++endbit;
if (endbit > 7) {
endbit = 0;
ptr++;
endbyte++;
}
}
public int read(int bits) {
int ret;
int m = mask[bits];
bits += endbit;
if (endbyte + 4 >= storage) {
ret = -1;
if (endbyte + (bits - 1) / 8 >= storage) {
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
}
ret = ((buffer[ptr]) & 0xff) >>> endbit;
if (bits > 8) {
ret |= ((buffer[ptr + 1]) & 0xff) << (8 - endbit);
if (bits > 16) {
ret |= ((buffer[ptr + 2]) & 0xff) << (16 - endbit);
if (bits > 24) {
ret |= ((buffer[ptr + 3]) & 0xff) << (24 - endbit);
if (bits > 32 && endbit != 0) {
ret |= ((buffer[ptr + 4]) & 0xff) << (32 - endbit);
}
}
}
}
ret &= m;
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
public int readB(int bits) {
int ret;
int m = 32 - bits;
bits += endbit;
if (endbyte + 4 >= storage) {
/* not the main path */
ret = -1;
if (endbyte * 8 + bits > storage * 8) {
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
}
ret = (buffer[ptr] & 0xff) << (24 + endbit);
if (bits > 8) {
ret |= (buffer[ptr + 1] & 0xff) << (16 + endbit);
if (bits > 16) {
ret |= (buffer[ptr + 2] & 0xff) << (8 + endbit);
if (bits > 24) {
ret |= (buffer[ptr + 3] & 0xff) << (endbit);
if (bits > 32 && (endbit != 0))
ret |= (buffer[ptr + 4] & 0xff) >> (8 - endbit);
}
}
}
ret = (ret >>> (m >> 1)) >>> ((m + 1) >> 1);
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
public int read1() {
int ret;
if (endbyte >= storage) {
ret = -1;
endbit++;
if (endbit > 7) {
endbit = 0;
ptr++;
endbyte++;
}
return (ret);
}
ret = (buffer[ptr] >> endbit) & 1;
endbit++;
if (endbit > 7) {
endbit = 0;
ptr++;
endbyte++;
}
return (ret);
}
public int bytes() {
return (endbyte + (endbit + 7) / 8);
}
public int bits() {
return (endbyte * 8 + endbit);
}
public byte[] buffer() {
return (buffer);
}
public static int ilog(int v) {
int ret = 0;
while (v > 0) {
ret++;
v >>>= 1;
}
return (ret);
}
public static void report(String in) {
System.err.println(in);
System.exit(1);
}
}

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class Packet {
public byte[] packet_base;
public int packet;
public int bytes;
public int b_o_s;
public int e_o_s;
public long granulepos;
/**
* sequence number for decode; the framing knows where there's a hole in the
* data, but we need coupling so that the codec (which is in a seperate
* abstraction layer) also knows about the gap
*/
public long packetno;
}

@ -0,0 +1,130 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class Page {
private static int[] crc_lookup = new int[256];
static {
for (int i = 0; i < crc_lookup.length; i++) {
crc_lookup[i] = crc_entry(i);
}
}
private static int crc_entry(int index) {
int r = index << 24;
for (int i = 0; i < 8; i++) {
if ((r & 0x80000000) != 0) {
r = (r << 1) ^ 0x04c11db7; /*
* The same as the ethernet generator polynomial, although we use an
* unreflected alg and an init/final of 0, not 0xffffffff
*/
} else {
r <<= 1;
}
}
return (r & 0xffffffff);
}
public byte[] header_base;
public int header;
public int header_len;
public byte[] body_base;
public int body;
public int body_len;
int version() {
return header_base[header + 4] & 0xff;
}
int continued() {
return (header_base[header + 5] & 0x01);
}
public int bos() {
return (header_base[header + 5] & 0x02);
}
public int eos() {
return (header_base[header + 5] & 0x04);
}
public long granulepos() {
long foo = header_base[header + 13] & 0xff;
foo = (foo << 8) | (header_base[header + 12] & 0xff);
foo = (foo << 8) | (header_base[header + 11] & 0xff);
foo = (foo << 8) | (header_base[header + 10] & 0xff);
foo = (foo << 8) | (header_base[header + 9] & 0xff);
foo = (foo << 8) | (header_base[header + 8] & 0xff);
foo = (foo << 8) | (header_base[header + 7] & 0xff);
foo = (foo << 8) | (header_base[header + 6] & 0xff);
return (foo);
}
public int serialno() {
return (header_base[header + 14] & 0xff) | ((header_base[header + 15] & 0xff) << 8)
| ((header_base[header + 16] & 0xff) << 16) | ((header_base[header + 17] & 0xff) << 24);
}
int pageno() {
return (header_base[header + 18] & 0xff) | ((header_base[header + 19] & 0xff) << 8)
| ((header_base[header + 20] & 0xff) << 16) | ((header_base[header + 21] & 0xff) << 24);
}
void checksum() {
int crc_reg = 0;
for (int i = 0; i < header_len; i++) {
crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >>> 24) & 0xff) ^ (header_base[header + i] & 0xff)];
}
for (int i = 0; i < body_len; i++) {
crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >>> 24) & 0xff) ^ (body_base[body + i] & 0xff)];
}
header_base[header + 22] = (byte) crc_reg;
header_base[header + 23] = (byte) (crc_reg >>> 8);
header_base[header + 24] = (byte) (crc_reg >>> 16);
header_base[header + 25] = (byte) (crc_reg >>> 24);
}
public Page copy() {
return copy(new Page());
}
public Page copy(Page p) {
byte[] tmp = new byte[header_len];
System.arraycopy(header_base, header, tmp, 0, header_len);
p.header_len = header_len;
p.header_base = tmp;
p.header = 0;
tmp = new byte[body_len];
System.arraycopy(body_base, body, tmp, 0, body_len);
p.body_len = body_len;
p.body_base = tmp;
p.body = 0;
return p;
}
}

@ -0,0 +1,538 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class StreamState {
byte[] body_data; /* bytes from packet bodies */
int body_storage; /* storage elements allocated */
int body_fill; /* elements stored; fill mark */
private int body_returned; /* elements of fill returned */
int[] lacing_vals; /* The values that will go to the segment table */
long[] granule_vals; /*
* pcm_pos values for headers. Not compact this way, but it is simple coupled to
* the lacing fifo
*/
int lacing_storage;
int lacing_fill;
int lacing_packet;
int lacing_returned;
byte[] header = new byte[282]; /* working space for header encode */
int header_fill;
public int e_o_s; /*
* set when we have buffered the last packet in the logical bitstream
*/
int b_o_s; /*
* set after we've written the initial page of a logical bitstream
*/
int serialno;
int pageno;
long packetno; /*
* sequence number for decode; the framing knows where there's a hole in the
* data, but we need coupling so that the codec (which is in a seperate
* abstraction layer) also knows about the gap
*/
long granulepos;
public StreamState() {
init();
}
StreamState(int serialno) {
this();
init(serialno);
}
void init() {
body_storage = 16 * 1024;
body_data = new byte[body_storage];
lacing_storage = 1024;
lacing_vals = new int[lacing_storage];
granule_vals = new long[lacing_storage];
}
public void init(int serialno) {
if (body_data == null) {
init();
} else {
for (int i = 0; i < body_data.length; i++)
body_data[i] = 0;
for (int i = 0; i < lacing_vals.length; i++)
lacing_vals[i] = 0;
for (int i = 0; i < granule_vals.length; i++)
granule_vals[i] = 0;
}
this.serialno = serialno;
}
public void clear() {
body_data = null;
lacing_vals = null;
granule_vals = null;
}
void destroy() {
clear();
}
void body_expand(int needed) {
if (body_storage <= body_fill + needed) {
body_storage += (needed + 1024);
byte[] foo = new byte[body_storage];
System.arraycopy(body_data, 0, foo, 0, body_data.length);
body_data = foo;
}
}
void lacing_expand(int needed) {
if (lacing_storage <= lacing_fill + needed) {
lacing_storage += (needed + 32);
int[] foo = new int[lacing_storage];
System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length);
lacing_vals = foo;
long[] bar = new long[lacing_storage];
System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length);
granule_vals = bar;
}
}
/* submit data to the internal buffer of the framing engine */
public int packetin(Packet op) {
int lacing_val = op.bytes / 255 + 1;
if (body_returned != 0) {
/*
* advance packet data according to the body_returned pointer. We had to keep it
* around to return a pointer into the buffer last call
*/
body_fill -= body_returned;
if (body_fill != 0) {
System.arraycopy(body_data, body_returned, body_data, 0, body_fill);
}
body_returned = 0;
}
/* make sure we have the buffer storage */
body_expand(op.bytes);
lacing_expand(lacing_val);
/*
* Copy in the submitted packet. Yes, the copy is a waste; this is the liability
* of overly clean abstraction for the time being. It will actually be fairly
* easy to eliminate the extra copy in the future
*/
System.arraycopy(op.packet_base, op.packet, body_data, body_fill, op.bytes);
body_fill += op.bytes;
/* Store lacing vals for this packet */
int j;
for (j = 0; j < lacing_val - 1; j++) {
lacing_vals[lacing_fill + j] = 255;
granule_vals[lacing_fill + j] = granulepos;
}
lacing_vals[lacing_fill + j] = (op.bytes) % 255;
granulepos = granule_vals[lacing_fill + j] = op.granulepos;
/* flag the first segment as the beginning of the packet */
lacing_vals[lacing_fill] |= 0x100;
lacing_fill += lacing_val;
/* for the sake of completeness */
packetno++;
if (op.e_o_s != 0)
e_o_s = 1;
return (0);
}
public int packetout(Packet op) {
/*
* The last part of decode. We have the stream broken into packet segments. Now
* we need to group them into packets (or return the out of sync markers)
*/
int ptr = lacing_returned;
if (lacing_packet <= ptr) {
return (0);
}
if ((lacing_vals[ptr] & 0x400) != 0) {
/* We lost sync here; let the app know */
lacing_returned++;
/*
* we need to tell the codec there's a gap; it might need to handle previous
* packet dependencies.
*/
packetno++;
return (-1);
}
/* Gather the whole packet. We'll have no holes or a partial packet */
{
int size = lacing_vals[ptr] & 0xff;
int bytes = 0;
op.packet_base = body_data;
op.packet = body_returned;
op.e_o_s = lacing_vals[ptr] & 0x200; /* last packet of the stream? */
op.b_o_s = lacing_vals[ptr] & 0x100; /* first packet of the stream? */
bytes += size;
while (size == 255) {
int val = lacing_vals[++ptr];
size = val & 0xff;
if ((val & 0x200) != 0)
op.e_o_s = 0x200;
bytes += size;
}
op.packetno = packetno;
op.granulepos = granule_vals[ptr];
op.bytes = bytes;
body_returned += bytes;
lacing_returned = ptr + 1;
}
packetno++;
return (1);
}
// add the incoming page to the stream state; we decompose the page
// into packet segments here as well.
public int pagein(Page og) {
byte[] header_base = og.header_base;
int header = og.header;
byte[] body_base = og.body_base;
int body = og.body;
int bodysize = og.body_len;
int segptr = 0;
int version = og.version();
int continued = og.continued();
int bos = og.bos();
int eos = og.eos();
long granulepos = og.granulepos();
int _serialno = og.serialno();
int _pageno = og.pageno();
int segments = header_base[header + 26] & 0xff;
// clean up 'returned data'
{
int lr = lacing_returned;
int br = body_returned;
// body data
if (br != 0) {
body_fill -= br;
if (body_fill != 0) {
System.arraycopy(body_data, br, body_data, 0, body_fill);
}
body_returned = 0;
}
if (lr != 0) {
// segment table
if ((lacing_fill - lr) != 0) {
System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill - lr);
System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill - lr);
}
lacing_fill -= lr;
lacing_packet -= lr;
lacing_returned = 0;
}
}
// check the serial number
if (_serialno != serialno)
return (-1);
if (version > 0)
return (-1);
lacing_expand(segments + 1);
// are we in sequence?
if (_pageno != pageno) {
int i;
// unroll previous partial packet (if any)
for (i = lacing_packet; i < lacing_fill; i++) {
body_fill -= lacing_vals[i] & 0xff;
// System.out.println("??");
}
lacing_fill = lacing_packet;
// make a note of dropped data in segment table
if (pageno != -1) {
lacing_vals[lacing_fill++] = 0x400;
lacing_packet++;
}
// are we a 'continued packet' page? If so, we'll need to skip
// some segments
if (continued != 0) {
bos = 0;
for (; segptr < segments; segptr++) {
int val = (header_base[header + 27 + segptr] & 0xff);
body += val;
bodysize -= val;
if (val < 255) {
segptr++;
break;
}
}
}
}
if (bodysize != 0) {
body_expand(bodysize);
System.arraycopy(body_base, body, body_data, body_fill, bodysize);
body_fill += bodysize;
}
{
int saved = -1;
while (segptr < segments) {
int val = (header_base[header + 27 + segptr] & 0xff);
lacing_vals[lacing_fill] = val;
granule_vals[lacing_fill] = -1;
if (bos != 0) {
lacing_vals[lacing_fill] |= 0x100;
bos = 0;
}
if (val < 255)
saved = lacing_fill;
lacing_fill++;
segptr++;
if (val < 255)
lacing_packet = lacing_fill;
}
/* set the granulepos on the last pcmval of the last full packet */
if (saved != -1) {
granule_vals[saved] = granulepos;
}
}
if (eos != 0) {
e_o_s = 1;
if (lacing_fill > 0)
lacing_vals[lacing_fill - 1] |= 0x200;
}
pageno = _pageno + 1;
return (0);
}
/*
* This will flush remaining packets into a page (returning nonzero), even if
* there is not enough data to trigger a flush normally (undersized page). If
* there are no packets or partial packets to flush, ogg_stream_flush returns 0.
* Note that ogg_stream_flush will try to flush a normal sized page like
* ogg_stream_pageout; a call to ogg_stream_flush does not gurantee that all
* packets have flushed. Only a return value of 0 from ogg_stream_flush
* indicates all packet data is flushed into pages.
*
* ogg_stream_page will flush the last page in a stream even if it's undersized;
* you almost certainly want to use ogg_stream_pageout (and *not*
* ogg_stream_flush) unless you need to flush an undersized page in the middle
* of a stream for some reason.
*/
public int flush(Page og) {
int i;
int vals = 0;
int maxvals = (lacing_fill > 255 ? 255 : lacing_fill);
int bytes = 0;
int acc = 0;
long granule_pos = granule_vals[0];
if (maxvals == 0)
return (0);
/* construct a page */
/* decide how many segments to include */
/*
* If this is the initial header case, the first page must only include the
* initial header packet
*/
if (b_o_s == 0) { /* 'initial header page' case */
granule_pos = 0;
for (vals = 0; vals < maxvals; vals++) {
if ((lacing_vals[vals] & 0x0ff) < 255) {
vals++;
break;
}
}
} else {
for (vals = 0; vals < maxvals; vals++) {
if (acc > 4096)
break;
acc += (lacing_vals[vals] & 0x0ff);
granule_pos = granule_vals[vals];
}
}
/* construct the header in temp storage */
System.arraycopy("OggS".getBytes(), 0, header, 0, 4);
/* stream structure version */
header[4] = 0x00;
/* continued packet flag? */
header[5] = 0x00;
if ((lacing_vals[0] & 0x100) == 0)
header[5] |= 0x01;
/* first page flag? */
if (b_o_s == 0)
header[5] |= 0x02;
/* last page flag? */
if (e_o_s != 0 && lacing_fill == vals)
header[5] |= 0x04;
b_o_s = 1;
/* 64 bits of PCM position */
for (i = 6; i < 14; i++) {
header[i] = (byte) granule_pos;
granule_pos >>>= 8;
}
/* 32 bits of stream serial number */
{
int _serialno = serialno;
for (i = 14; i < 18; i++) {
header[i] = (byte) _serialno;
_serialno >>>= 8;
}
}
/*
* 32 bits of page counter (we have both counter and page header because this
* val can roll over)
*/
if (pageno == -1)
pageno = 0; /*
* because someone called stream_reset; this would be a strange thing to do in
* an encode stream, but it has plausible uses
*/
{
int _pageno = pageno++;
for (i = 18; i < 22; i++) {
header[i] = (byte) _pageno;
_pageno >>>= 8;
}
}
/* zero for computation; filled in later */
header[22] = 0;
header[23] = 0;
header[24] = 0;
header[25] = 0;
/* segment table */
header[26] = (byte) vals;
for (i = 0; i < vals; i++) {
header[i + 27] = (byte) lacing_vals[i];
bytes += (header[i + 27] & 0xff);
}
/* set pointers in the ogg_page struct */
og.header_base = header;
og.header = 0;
og.header_len = header_fill = vals + 27;
og.body_base = body_data;
og.body = body_returned;
og.body_len = bytes;
/* advance the lacing data and set the body_returned pointer */
lacing_fill -= vals;
System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill * 4);
System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill * 8);
body_returned += bytes;
/* calculate the checksum */
og.checksum();
/* done */
return (1);
}
/*
* This constructs pages from buffered packet segments. The pointers returned
* are to static buffers; do not free. The returned buffers are good only until
* the next call (using the same ogg_stream_state)
*/
public int pageout(Page og) {
if ((e_o_s != 0 && lacing_fill != 0) || /* 'were done, now flush' case */
body_fill - body_returned > 4096 || /* 'page nominal size' case */
lacing_fill >= 255 || /* 'segment table full' case */
(lacing_fill != 0 && b_o_s == 0)) { /* 'initial header page' case */
return flush(og);
}
return 0;
}
public int eof() {
return e_o_s;
}
public int reset() {
body_fill = 0;
body_returned = 0;
lacing_fill = 0;
lacing_packet = 0;
lacing_returned = 0;
header_fill = 0;
e_o_s = 0;
b_o_s = 0;
pageno = -1;
packetno = 0;
granulepos = 0;
return (0);
}
}

@ -0,0 +1,273 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
// DECODING PRIMITIVES: packet streaming layer
// This has two layers to place more of the multi-serialno and paging
// control in the application's hands. First, we expose a data buffer
// using ogg_decode_buffer(). The app either copies into the
// buffer, or passes it directly to read(), etc. We then call
// ogg_decode_wrote() to tell how many bytes we just added.
//
// Pages are returned (pointers into the buffer in ogg_sync_state)
// by ogg_decode_stream(). The page is then submitted to
// ogg_decode_page() along with the appropriate
// ogg_stream_state* (ie, matching serialno). We then get raw
// packets out calling ogg_stream_packet() with a
// ogg_stream_state. See the 'frame-prog.txt' docs for details and
// example code.
public class SyncState {
public byte[] data;
int storage;
int fill;
int returned;
int unsynced;
int headerbytes;
int bodybytes;
public int clear() {
data = null;
return (0);
}
public int buffer(int size) {
// first, clear out any space that has been previously returned
if (returned != 0) {
fill -= returned;
if (fill > 0) {
System.arraycopy(data, returned, data, 0, fill);
}
returned = 0;
}
if (size > storage - fill) {
// We need to extend the internal buffer
int newsize = size + fill + 4096; // an extra page to be nice
if (data != null) {
byte[] foo = new byte[newsize];
System.arraycopy(data, 0, foo, 0, data.length);
data = foo;
} else {
data = new byte[newsize];
}
storage = newsize;
}
return (fill);
}
public int wrote(int bytes) {
if (fill + bytes > storage)
return (-1);
fill += bytes;
return (0);
}
// sync the stream. This is meant to be useful for finding page
// boundaries.
//
// return values for this:
// -n) skipped n bytes
// 0) page not ready; more data (no bytes skipped)
// n) page synced at current location; page length n bytes
private Page pageseek = new Page();
private byte[] chksum = new byte[4];
public int pageseek(Page og) {
int page = returned;
int next;
int bytes = fill - returned;
if (headerbytes == 0) {
int _headerbytes, i;
if (bytes < 27)
return (0); // not enough for a header
/* verify capture pattern */
if (data[page] != 'O' || data[page + 1] != 'g' || data[page + 2] != 'g' || data[page + 3] != 'S') {
headerbytes = 0;
bodybytes = 0;
// search for possible capture
next = 0;
for (int ii = 0; ii < bytes - 1; ii++) {
if (data[page + 1 + ii] == 'O') {
next = page + 1 + ii;
break;
}
}
// next=memchr(page+1,'O',bytes-1);
if (next == 0)
next = fill;
returned = next;
return (-(next - page));
}
_headerbytes = (data[page + 26] & 0xff) + 27;
if (bytes < _headerbytes)
return (0); // not enough for header + seg table
// count up body length in the segment table
for (i = 0; i < (data[page + 26] & 0xff); i++) {
bodybytes += (data[page + 27 + i] & 0xff);
}
headerbytes = _headerbytes;
}
if (bodybytes + headerbytes > bytes)
return (0);
// The whole test page is buffered. Verify the checksum
synchronized (chksum) {
// Grab the checksum bytes, set the header field to zero
System.arraycopy(data, page + 22, chksum, 0, 4);
data[page + 22] = 0;
data[page + 23] = 0;
data[page + 24] = 0;
data[page + 25] = 0;
// set up a temp page struct and recompute the checksum
Page log = pageseek;
log.header_base = data;
log.header = page;
log.header_len = headerbytes;
log.body_base = data;
log.body = page + headerbytes;
log.body_len = bodybytes;
log.checksum();
// Compare
if (chksum[0] != data[page + 22] || chksum[1] != data[page + 23] || chksum[2] != data[page + 24]
|| chksum[3] != data[page + 25]) {
// D'oh. Mismatch! Corrupt page (or miscapture and not a page at all)
// replace the computed checksum with the one actually read in
System.arraycopy(chksum, 0, data, page + 22, 4);
// Bad checksum. Lose sync */
headerbytes = 0;
bodybytes = 0;
// search for possible capture
next = 0;
for (int ii = 0; ii < bytes - 1; ii++) {
if (data[page + 1 + ii] == 'O') {
next = page + 1 + ii;
break;
}
}
// next=memchr(page+1,'O',bytes-1);
if (next == 0)
next = fill;
returned = next;
return (-(next - page));
}
}
// yes, have a whole page all ready to go
{
page = returned;
if (og != null) {
og.header_base = data;
og.header = page;
og.header_len = headerbytes;
og.body_base = data;
og.body = page + headerbytes;
og.body_len = bodybytes;
}
unsynced = 0;
returned += (bytes = headerbytes + bodybytes);
headerbytes = 0;
bodybytes = 0;
return (bytes);
}
}
// sync the stream and get a page. Keep trying until we find a page.
// Supress 'sync errors' after reporting the first.
//
// return values:
// -1) recapture (hole in data)
// 0) need more data
// 1) page returned
//
// Returns pointers into buffered data; invalidated by next call to
// _stream, _clear, _init, or _buffer
public int pageout(Page og) {
// all we need to do is verify a page at the head of the stream
// buffer. If it doesn't verify, we look for the next potential
// frame
while (true) {
int ret = pageseek(og);
if (ret > 0) {
// have a page
return (1);
}
if (ret == 0) {
// need more data
return (0);
}
// head did not start a synced page... skipped some bytes
if (unsynced == 0) {
unsynced = 1;
return (-1);
}
// loop. keep looking
}
}
// clear things to an initial state. Good to call, eg, before seeking
public int reset() {
fill = 0;
returned = 0;
unsynced = 0;
headerbytes = 0;
bodybytes = 0;
return (0);
}
public void init() {
}
public int getDataOffset() {
return returned;
}
public int getBufferOffset() {
return fill;
}
}

@ -0,0 +1,126 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
public class Block {
/// necessary stream state for linking to the framing abstraction
float[][] pcm = new float[0][]; // this is a pointer into local storage
Buffer opb = new Buffer();
int lW;
int W;
int nW;
int pcmend;
int mode;
int eofflag;
long granulepos;
long sequence;
DspState vd; // For read-only access of configuration
// bitmetrics for the frame
int glue_bits;
int time_bits;
int floor_bits;
int res_bits;
public Block(DspState vd) {
this.vd = vd;
if (vd.analysisp != 0) {
opb.writeinit();
}
}
public void init(DspState vd) {
this.vd = vd;
}
public int clear() {
if (vd != null) {
if (vd.analysisp != 0) {
opb.writeclear();
}
}
return (0);
}
public int synthesis(Packet op) {
Info vi = vd.vi;
// first things first. Make sure decode is ready
opb.readinit(op.packet_base, op.packet, op.bytes);
// Check the packet type
if (opb.read(1) != 0) {
// Oops. This is not an audio data packet
return (-1);
}
// read our mode and pre/post windowsize
int _mode = opb.read(vd.modebits);
if (_mode == -1)
return (-1);
mode = _mode;
W = vi.mode_param[mode].blockflag;
if (W != 0) {
lW = opb.read(1);
nW = opb.read(1);
if (nW == -1)
return (-1);
} else {
lW = 0;
nW = 0;
}
// more setup
granulepos = op.granulepos;
sequence = op.packetno - 3; // first block is third packet
eofflag = op.e_o_s;
// alloc pcm passback storage
pcmend = vi.blocksizes[W];
if (pcm.length < vi.channels) {
pcm = new float[vi.channels][];
}
for (int i = 0; i < vi.channels; i++) {
if (pcm[i] == null || pcm[i].length < pcmend) {
pcm[i] = new float[pcmend];
} else {
for (int j = 0; j < pcmend; j++) {
pcm[i][j] = 0;
}
}
}
// unpack_header enforces range checking
int type = vi.map_type[vi.mode_param[mode].mapping];
return (FuncMapping.mapping_P[type].inverse(this, vd.mode[mode]));
}
}

@ -0,0 +1,471 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class CodeBook {
int dim; // codebook dimensions (elements per vector)
int entries; // codebook entries
StaticCodeBook c = new StaticCodeBook();
float[] valuelist; // list of dim*entries actual entry values
int[] codelist; // list of bitstream codewords for each entry
DecodeAux decode_tree;
// returns the number of bits
int encode(int a, Buffer b) {
b.write(codelist[a], c.lengthlist[a]);
return (c.lengthlist[a]);
}
// One the encode side, our vector writers are each designed for a
// specific purpose, and the encoder is not flexible without modification:
//
// The LSP vector coder uses a single stage nearest-match with no
// interleave, so no step and no error return. This is specced by floor0
// and doesn't change.
//
// Residue0 encoding interleaves, uses multiple stages, and each stage
// peels of a specific amount of resolution from a lattice (thus we want
// to match by threshhold, not nearest match). Residue doesn't *have* to
// be encoded that way, but to change it, one will need to add more
// infrastructure on the encode side (decode side is specced and simpler)
// floor0 LSP (single stage, non interleaved, nearest match)
// returns entry number and *modifies a* to the quantization value
int errorv(float[] a) {
int best = best(a, 1);
for (int k = 0; k < dim; k++) {
a[k] = valuelist[best * dim + k];
}
return (best);
}
// returns the number of bits and *modifies a* to the quantization value
int encodev(int best, float[] a, Buffer b) {
for (int k = 0; k < dim; k++) {
a[k] = valuelist[best * dim + k];
}
return (encode(best, b));
}
// res0 (multistage, interleave, lattice)
// returns the number of bits and *modifies a* to the remainder value
int encodevs(float[] a, Buffer b, int step, int addmul) {
int best = besterror(a, step, addmul);
return (encode(best, b));
}
private int[] t = new int[15]; // decodevs_add is synchronized for re-using t.
synchronized int decodevs_add(float[] a, int offset, Buffer b, int n) {
int step = n / dim;
int entry;
int i, j, o;
if (t.length < step) {
t = new int[step];
}
for (i = 0; i < step; i++) {
entry = decode(b);
if (entry == -1)
return (-1);
t[i] = entry * dim;
}
for (i = 0, o = 0; i < dim; i++, o += step) {
for (j = 0; j < step; j++) {
a[offset + o + j] += valuelist[t[j] + i];
}
}
return (0);
}
int decodev_add(float[] a, int offset, Buffer b, int n) {
int i, j, entry;
int t;
if (dim > 8) {
for (i = 0; i < n;) {
entry = decode(b);
if (entry == -1)
return (-1);
t = entry * dim;
for (j = 0; j < dim;) {
a[offset + (i++)] += valuelist[t + (j++)];
}
}
} else {
for (i = 0; i < n;) {
entry = decode(b);
if (entry == -1)
return (-1);
t = entry * dim;
j = 0;
switch (dim) {
case 8:
a[offset + (i++)] += valuelist[t + (j++)];
case 7:
a[offset + (i++)] += valuelist[t + (j++)];
case 6:
a[offset + (i++)] += valuelist[t + (j++)];
case 5:
a[offset + (i++)] += valuelist[t + (j++)];
case 4:
a[offset + (i++)] += valuelist[t + (j++)];
case 3:
a[offset + (i++)] += valuelist[t + (j++)];
case 2:
a[offset + (i++)] += valuelist[t + (j++)];
case 1:
a[offset + (i++)] += valuelist[t + (j++)];
case 0:
break;
}
}
}
return (0);
}
int decodev_set(float[] a, int offset, Buffer b, int n) {
int i, j, entry;
int t;
for (i = 0; i < n;) {
entry = decode(b);
if (entry == -1)
return (-1);
t = entry * dim;
for (j = 0; j < dim;) {
a[offset + i++] = valuelist[t + (j++)];
}
}
return (0);
}
int decodevv_add(float[][] a, int offset, int ch, Buffer b, int n) {
int i, j, entry;
int chptr = 0;
for (i = offset / ch; i < (offset + n) / ch;) {
entry = decode(b);
if (entry == -1)
return (-1);
int t = entry * dim;
for (j = 0; j < dim; j++) {
a[chptr++][i] += valuelist[t + j];
if (chptr == ch) {
chptr = 0;
i++;
}
}
}
return (0);
}
// Decode side is specced and easier, because we don't need to find
// matches using different criteria; we simply read and map. There are
// two things we need to do 'depending':
//
// We may need to support interleave. We don't really, but it's
// convenient to do it here rather than rebuild the vector later.
//
// Cascades may be additive or multiplicitive; this is not inherent in
// the codebook, but set in the code using the codebook. Like
// interleaving, it's easiest to do it here.
// stage==0 -> declarative (set the value)
// stage==1 -> additive
// stage==2 -> multiplicitive
// returns the entry number or -1 on eof
int decode(Buffer b) {
int ptr = 0;
DecodeAux t = decode_tree;
int lok = b.look(t.tabn);
if (lok >= 0) {
ptr = t.tab[lok];
b.adv(t.tabl[lok]);
if (ptr <= 0) {
return -ptr;
}
}
do {
switch (b.read1()) {
case 0:
ptr = t.ptr0[ptr];
break;
case 1:
ptr = t.ptr1[ptr];
break;
case -1:
default:
return (-1);
}
} while (ptr > 0);
return (-ptr);
}
// returns the entry number or -1 on eof
int decodevs(float[] a, int index, Buffer b, int step, int addmul) {
int entry = decode(b);
if (entry == -1)
return (-1);
switch (addmul) {
case -1:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[index + o] = valuelist[entry * dim + i];
break;
case 0:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[index + o] += valuelist[entry * dim + i];
break;
case 1:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[index + o] *= valuelist[entry * dim + i];
break;
default:
// System.err.println("CodeBook.decodeves: addmul="+addmul);
}
return (entry);
}
int best(float[] a, int step) {
// brute force it!
{
int besti = -1;
float best = 0.f;
int e = 0;
for (int i = 0; i < entries; i++) {
if (c.lengthlist[i] > 0) {
float _this = dist(dim, valuelist, e, a, step);
if (besti == -1 || _this < best) {
best = _this;
besti = i;
}
}
e += dim;
}
return (besti);
}
}
// returns the entry number and *modifies a* to the remainder value
int besterror(float[] a, int step, int addmul) {
int best = best(a, step);
switch (addmul) {
case 0:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[o] -= valuelist[best * dim + i];
break;
case 1:
for (int i = 0, o = 0; i < dim; i++, o += step) {
float val = valuelist[best * dim + i];
if (val == 0) {
a[o] = 0;
} else {
a[o] /= val;
}
}
break;
}
return (best);
}
void clear() {
}
private static float dist(int el, float[] ref, int index, float[] b, int step) {
float acc = (float) 0.;
for (int i = 0; i < el; i++) {
float val = (ref[index + i] - b[i * step]);
acc += val * val;
}
return (acc);
}
int init_decode(StaticCodeBook s) {
c = s;
entries = s.entries;
dim = s.dim;
valuelist = s.unquantize();
decode_tree = make_decode_tree();
if (decode_tree == null) {
clear();
return (-1);
}
return (0);
}
// given a list of word lengths, generate a list of codewords. Works
// for length ordered or unordered, always assigns the lowest valued
// codewords first. Extended to handle unused entries (length 0)
static int[] make_words(int[] l, int n) {
int[] marker = new int[33];
int[] r = new int[n];
for (int i = 0; i < n; i++) {
int length = l[i];
if (length > 0) {
int entry = marker[length];
// when we claim a node for an entry, we also claim the nodes
// below it (pruning off the imagined tree that may have dangled
// from it) as well as blocking the use of any nodes directly
// above for leaves
// update ourself
if (length < 32 && (entry >>> length) != 0) {
// error condition; the lengths must specify an overpopulated tree
// free(r);
return (null);
}
r[i] = entry;
// Look to see if the next shorter marker points to the node
// above. if so, update it and repeat.
{
for (int j = length; j > 0; j--) {
if ((marker[j] & 1) != 0) {
// have to jump branches
if (j == 1)
marker[1]++;
else
marker[j] = marker[j - 1] << 1;
break; // invariant says next upper marker would already
// have been moved if it was on the same path
}
marker[j]++;
}
}
// prune the tree; the implicit invariant says all the longer
// markers were dangling from our just-taken node. Dangle them
// from our *new* node.
for (int j = length + 1; j < 33; j++) {
if ((marker[j] >>> 1) == entry) {
entry = marker[j];
marker[j] = marker[j - 1] << 1;
} else {
break;
}
}
}
}
// bitreverse the words because our bitwise packer/unpacker is LSb
// endian
for (int i = 0; i < n; i++) {
int temp = 0;
for (int j = 0; j < l[i]; j++) {
temp <<= 1;
temp |= (r[i] >>> j) & 1;
}
r[i] = temp;
}
return (r);
}
// build the decode helper tree from the codewords
DecodeAux make_decode_tree() {
int top = 0;
DecodeAux t = new DecodeAux();
int[] ptr0 = t.ptr0 = new int[entries * 2];
int[] ptr1 = t.ptr1 = new int[entries * 2];
int[] codelist = make_words(c.lengthlist, c.entries);
if (codelist == null)
return (null);
t.aux = entries * 2;
for (int i = 0; i < entries; i++) {
if (c.lengthlist[i] > 0) {
int ptr = 0;
int j;
for (j = 0; j < c.lengthlist[i] - 1; j++) {
int bit = (codelist[i] >>> j) & 1;
if (bit == 0) {
if (ptr0[ptr] == 0) {
ptr0[ptr] = ++top;
}
ptr = ptr0[ptr];
} else {
if (ptr1[ptr] == 0) {
ptr1[ptr] = ++top;
}
ptr = ptr1[ptr];
}
}
if (((codelist[i] >>> j) & 1) == 0) {
ptr0[ptr] = -i;
} else {
ptr1[ptr] = -i;
}
}
}
t.tabn = Util.ilog(entries) - 4;
if (t.tabn < 5)
t.tabn = 5;
int n = 1 << t.tabn;
t.tab = new int[n];
t.tabl = new int[n];
for (int i = 0; i < n; i++) {
int p = 0;
int j = 0;
for (j = 0; j < t.tabn && (p > 0 || j == 0); j++) {
if ((i & (1 << j)) != 0) {
p = ptr1[p];
} else {
p = ptr0[p];
}
}
t.tab[i] = p; // -code
t.tabl[i] = j; // length
}
return (t);
}
class DecodeAux {
int[] tab;
int[] tabl;
int tabn;
int[] ptr0;
int[] ptr1;
int aux; // number of tree entries
}
}

@ -0,0 +1,240 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
// the comments are not part of vorbis_info so that vorbis_info can be
// static storage
public class Comment {
private static byte[] _vorbis = "vorbis".getBytes();
private static byte[] _vendor = "Xiphophorus libVorbis I 20000508".getBytes();
private static final int OV_EIMPL = -130;
// unlimited user comment fields.
public byte[][] user_comments;
public int[] comment_lengths;
public int comments;
public byte[] vendor;
public void init() {
user_comments = null;
comments = 0;
vendor = null;
}
public void add(String comment) {
add(comment.getBytes());
}
private void add(byte[] comment) {
byte[][] foo = new byte[comments + 2][];
if (user_comments != null) {
System.arraycopy(user_comments, 0, foo, 0, comments);
}
user_comments = foo;
int[] goo = new int[comments + 2];
if (comment_lengths != null) {
System.arraycopy(comment_lengths, 0, goo, 0, comments);
}
comment_lengths = goo;
byte[] bar = new byte[comment.length + 1];
System.arraycopy(comment, 0, bar, 0, comment.length);
user_comments[comments] = bar;
comment_lengths[comments] = comment.length;
comments++;
user_comments[comments] = null;
}
public void add_tag(String tag, String contents) {
if (contents == null)
contents = "";
add(tag + "=" + contents);
}
static boolean tagcompare(byte[] s1, byte[] s2, int n) {
int c = 0;
byte u1, u2;
while (c < n) {
u1 = s1[c];
u2 = s2[c];
if ('Z' >= u1 && u1 >= 'A')
u1 = (byte) (u1 - 'A' + 'a');
if ('Z' >= u2 && u2 >= 'A')
u2 = (byte) (u2 - 'A' + 'a');
if (u1 != u2) {
return false;
}
c++;
}
return true;
}
public String query(String tag) {
return query(tag, 0);
}
public String query(String tag, int count) {
int foo = query(tag.getBytes(), count);
if (foo == -1)
return null;
byte[] comment = user_comments[foo];
for (int i = 0; i < comment_lengths[foo]; i++) {
if (comment[i] == '=') {
return new String(comment, i + 1, comment_lengths[foo] - (i + 1));
}
}
return null;
}
private int query(byte[] tag, int count) {
int i = 0;
int found = 0;
int fulltaglen = tag.length + 1;
byte[] fulltag = new byte[fulltaglen];
System.arraycopy(tag, 0, fulltag, 0, tag.length);
fulltag[tag.length] = (byte) '=';
for (i = 0; i < comments; i++) {
if (tagcompare(user_comments[i], fulltag, fulltaglen)) {
if (count == found) {
// We return a pointer to the data, not a copy
// return user_comments[i] + taglen + 1;
return i;
} else {
found++;
}
}
}
return -1;
}
int unpack(Buffer opb) {
int vendorlen = opb.read(32);
if (vendorlen < 0) {
clear();
return (-1);
}
vendor = new byte[vendorlen + 1];
opb.read(vendor, vendorlen);
comments = opb.read(32);
if (comments < 0) {
clear();
return (-1);
}
user_comments = new byte[comments + 1][];
comment_lengths = new int[comments + 1];
for (int i = 0; i < comments; i++) {
int len = opb.read(32);
if (len < 0) {
clear();
return (-1);
}
comment_lengths[i] = len;
user_comments[i] = new byte[len + 1];
opb.read(user_comments[i], len);
}
if (opb.read(1) != 1) {
clear();
return (-1);
}
return (0);
}
int pack(Buffer opb) {
// preamble
opb.write(0x03, 8);
opb.write(_vorbis);
// vendor
opb.write(_vendor.length, 32);
opb.write(_vendor);
// comments
opb.write(comments, 32);
if (comments != 0) {
for (int i = 0; i < comments; i++) {
if (user_comments[i] != null) {
opb.write(comment_lengths[i], 32);
opb.write(user_comments[i]);
} else {
opb.write(0, 32);
}
}
}
opb.write(1, 1);
return (0);
}
public int header_out(Packet op) {
Buffer opb = new Buffer();
opb.writeinit();
if (pack(opb) != 0)
return OV_EIMPL;
op.packet_base = new byte[opb.bytes()];
op.packet = 0;
op.bytes = opb.bytes();
System.arraycopy(opb.buffer(), 0, op.packet_base, 0, op.bytes);
op.b_o_s = 0;
op.e_o_s = 0;
op.granulepos = 0;
return 0;
}
void clear() {
for (int i = 0; i < comments; i++)
user_comments[i] = null;
user_comments = null;
vendor = null;
}
public String getVendor() {
return new String(vendor, 0, vendor.length - 1);
}
public String getComment(int i) {
if (comments <= i)
return null;
return new String(user_comments[i], 0, user_comments[i].length - 1);
}
public String toString() {
String foo = "Vendor: " + new String(vendor, 0, vendor.length - 1);
for (int i = 0; i < comments; i++) {
foo = foo + "\nComment: " + new String(user_comments[i], 0, user_comments[i].length - 1);
}
foo = foo + "\n";
return foo;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,369 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
public class DspState {
static final float M_PI = 3.1415926539f;
static final int VI_TRANSFORMB = 1;
static final int VI_WINDOWB = 1;
int analysisp;
Info vi;
int modebits;
float[][] pcm;
int pcm_storage;
int pcm_current;
int pcm_returned;
float[] multipliers;
int envelope_storage;
int envelope_current;
int eofflag;
int lW;
int W;
int nW;
int centerW;
long granulepos;
long sequence;
long glue_bits;
long time_bits;
long floor_bits;
long res_bits;
// local lookup storage
float[][][][][] window; // block, leadin, leadout, type
Object[][] transform;
CodeBook[] fullbooks;
// backend lookups are tied to the mode, not the backend or naked mapping
Object[] mode;
// local storage, only used on the encoding side. This way the
// application does not need to worry about freeing some packets'
// memory and not others'; packet storage is always tracked.
// Cleared next call to a _dsp_ function
byte[] header;
byte[] header1;
byte[] header2;
public DspState() {
transform = new Object[2][];
window = new float[2][][][][];
window[0] = new float[2][][][];
window[0][0] = new float[2][][];
window[0][1] = new float[2][][];
window[0][0][0] = new float[2][];
window[0][0][1] = new float[2][];
window[0][1][0] = new float[2][];
window[0][1][1] = new float[2][];
window[1] = new float[2][][][];
window[1][0] = new float[2][][];
window[1][1] = new float[2][][];
window[1][0][0] = new float[2][];
window[1][0][1] = new float[2][];
window[1][1][0] = new float[2][];
window[1][1][1] = new float[2][];
}
static float[] window(int type, int window, int left, int right) {
float[] ret = new float[window];
switch (type) {
case 0:
// The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi)
{
int leftbegin = window / 4 - left / 2;
int rightbegin = window - window / 4 - right / 2;
for (int i = 0; i < left; i++) {
float x = (float) ((i + .5) / left * M_PI / 2.);
x = (float) Math.sin(x);
x *= x;
x *= M_PI / 2.;
x = (float) Math.sin(x);
ret[i + leftbegin] = x;
}
for (int i = leftbegin + left; i < rightbegin; i++) {
ret[i] = 1.f;
}
for (int i = 0; i < right; i++) {
float x = (float) ((right - i - .5) / right * M_PI / 2.);
x = (float) Math.sin(x);
x *= x;
x *= M_PI / 2.;
x = (float) Math.sin(x);
ret[i + rightbegin] = x;
}
}
break;
default:
// free(ret);
return (null);
}
return (ret);
}
// Analysis side code, but directly related to blocking. Thus it's
// here and not in analysis.c (which is for analysis transforms only).
// The init is here because some of it is shared
int init(Info vi, boolean encp) {
this.vi = vi;
modebits = Util.ilog2(vi.modes);
transform[0] = new Object[VI_TRANSFORMB];
transform[1] = new Object[VI_TRANSFORMB];
// MDCT is tranform 0
transform[0][0] = new Mdct();
transform[1][0] = new Mdct();
((Mdct) transform[0][0]).init(vi.blocksizes[0]);
((Mdct) transform[1][0]).init(vi.blocksizes[1]);
window[0][0][0] = new float[VI_WINDOWB][];
window[0][0][1] = window[0][0][0];
window[0][1][0] = window[0][0][0];
window[0][1][1] = window[0][0][0];
window[1][0][0] = new float[VI_WINDOWB][];
window[1][0][1] = new float[VI_WINDOWB][];
window[1][1][0] = new float[VI_WINDOWB][];
window[1][1][1] = new float[VI_WINDOWB][];
for (int i = 0; i < VI_WINDOWB; i++) {
window[0][0][0][i] = window(i, vi.blocksizes[0], vi.blocksizes[0] / 2, vi.blocksizes[0] / 2);
window[1][0][0][i] = window(i, vi.blocksizes[1], vi.blocksizes[0] / 2, vi.blocksizes[0] / 2);
window[1][0][1][i] = window(i, vi.blocksizes[1], vi.blocksizes[0] / 2, vi.blocksizes[1] / 2);
window[1][1][0][i] = window(i, vi.blocksizes[1], vi.blocksizes[1] / 2, vi.blocksizes[0] / 2);
window[1][1][1][i] = window(i, vi.blocksizes[1], vi.blocksizes[1] / 2, vi.blocksizes[1] / 2);
}
fullbooks = new CodeBook[vi.books];
for (int i = 0; i < vi.books; i++) {
fullbooks[i] = new CodeBook();
fullbooks[i].init_decode(vi.book_param[i]);
}
// initialize the storage vectors to a decent size greater than the
// minimum
pcm_storage = 8192; // we'll assume later that we have
// a minimum of twice the blocksize of
// accumulated samples in analysis
pcm = new float[vi.channels][];
{
for (int i = 0; i < vi.channels; i++) {
pcm[i] = new float[pcm_storage];
}
}
// all 1 (large block) or 0 (small block)
// explicitly set for the sake of clarity
lW = 0; // previous window size
W = 0; // current window size
// all vector indexes; multiples of samples_per_envelope_step
centerW = vi.blocksizes[1] / 2;
pcm_current = centerW;
// initialize all the mapping/backend lookups
mode = new Object[vi.modes];
for (int i = 0; i < vi.modes; i++) {
int mapnum = vi.mode_param[i].mapping;
int maptype = vi.map_type[mapnum];
mode[i] = FuncMapping.mapping_P[maptype].look(this, vi.mode_param[i], vi.map_param[mapnum]);
}
return (0);
}
public int synthesis_init(Info vi) {
init(vi, false);
// Adjust centerW to allow an easier mechanism for determining output
pcm_returned = centerW;
centerW -= vi.blocksizes[W] / 4 + vi.blocksizes[lW] / 4;
granulepos = -1;
sequence = -1;
return (0);
}
DspState(Info vi) {
this();
init(vi, false);
// Adjust centerW to allow an easier mechanism for determining output
pcm_returned = centerW;
centerW -= vi.blocksizes[W] / 4 + vi.blocksizes[lW] / 4;
granulepos = -1;
sequence = -1;
}
// Unike in analysis, the window is only partially applied for each
// block. The time domain envelope is not yet handled at the point of
// calling (as it relies on the previous block).
public int synthesis_blockin(Block vb) {
// Shift out any PCM/multipliers that we returned previously
// centerW is currently the center of the last block added
if (centerW > vi.blocksizes[1] / 2 && pcm_returned > 8192) {
// don't shift too much; we need to have a minimum PCM buffer of
// 1/2 long block
int shiftPCM = centerW - vi.blocksizes[1] / 2;
shiftPCM = (pcm_returned < shiftPCM ? pcm_returned : shiftPCM);
pcm_current -= shiftPCM;
centerW -= shiftPCM;
pcm_returned -= shiftPCM;
if (shiftPCM != 0) {
for (int i = 0; i < vi.channels; i++) {
System.arraycopy(pcm[i], shiftPCM, pcm[i], 0, pcm_current);
}
}
}
lW = W;
W = vb.W;
nW = -1;
glue_bits += vb.glue_bits;
time_bits += vb.time_bits;
floor_bits += vb.floor_bits;
res_bits += vb.res_bits;
if (sequence + 1 != vb.sequence)
granulepos = -1; // out of sequence; lose count
sequence = vb.sequence;
{
int sizeW = vi.blocksizes[W];
int _centerW = centerW + vi.blocksizes[lW] / 4 + sizeW / 4;
int beginW = _centerW - sizeW / 2;
int endW = beginW + sizeW;
int beginSl = 0;
int endSl = 0;
// Do we have enough PCM/mult storage for the block?
if (endW > pcm_storage) {
// expand the storage
pcm_storage = endW + vi.blocksizes[1];
for (int i = 0; i < vi.channels; i++) {
float[] foo = new float[pcm_storage];
System.arraycopy(pcm[i], 0, foo, 0, pcm[i].length);
pcm[i] = foo;
}
}
// overlap/add PCM
switch (W) {
case 0:
beginSl = 0;
endSl = vi.blocksizes[0] / 2;
break;
case 1:
beginSl = vi.blocksizes[1] / 4 - vi.blocksizes[lW] / 4;
endSl = beginSl + vi.blocksizes[lW] / 2;
break;
}
for (int j = 0; j < vi.channels; j++) {
int _pcm = beginW;
// the overlap/add section
int i = 0;
for (i = beginSl; i < endSl; i++) {
pcm[j][_pcm + i] += vb.pcm[j][i];
}
// the remaining section
for (; i < sizeW; i++) {
pcm[j][_pcm + i] = vb.pcm[j][i];
}
}
// track the frame number... This is for convenience, but also
// making sure our last packet doesn't end with added padding. If
// the last packet is partial, the number of samples we'll have to
// return will be past the vb->granulepos.
//
// This is not foolproof! It will be confused if we begin
// decoding at the last page after a seek or hole. In that case,
// we don't have a starting point to judge where the last frame
// is. For this reason, vorbisfile will always try to make sure
// it reads the last two marked pages in proper sequence
if (granulepos == -1) {
granulepos = vb.granulepos;
} else {
granulepos += (_centerW - centerW);
if (vb.granulepos != -1 && granulepos != vb.granulepos) {
if (granulepos > vb.granulepos && vb.eofflag != 0) {
// partial last frame. Strip the padding off
_centerW -= (granulepos - vb.granulepos);
} // else{ Shouldn't happen *unless* the bitstream is out of
// spec. Either way, believe the bitstream }
granulepos = vb.granulepos;
}
}
// Update, cleanup
centerW = _centerW;
pcm_current = endW;
if (vb.eofflag != 0)
eofflag = 1;
}
return (0);
}
// pcm==NULL indicates we just want the pending samples, no more
public int synthesis_pcmout(float[][][] _pcm, int[] index) {
if (pcm_returned < centerW) {
if (_pcm != null) {
for (int i = 0; i < vi.channels; i++) {
index[i] = pcm_returned;
}
_pcm[0] = pcm;
}
return (centerW - pcm_returned);
}
return (0);
}
public int synthesis_read(int bytes) {
if (bytes != 0 && pcm_returned + bytes > centerW)
return (-1);
pcm_returned += bytes;
return (0);
}
public void clear() {
}
}

@ -0,0 +1,332 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Floor0 extends FuncFloor {
void pack(Object i, Buffer opb) {
InfoFloor0 info = (InfoFloor0) i;
opb.write(info.order, 8);
opb.write(info.rate, 16);
opb.write(info.barkmap, 16);
opb.write(info.ampbits, 6);
opb.write(info.ampdB, 8);
opb.write(info.numbooks - 1, 4);
for (int j = 0; j < info.numbooks; j++)
opb.write(info.books[j], 8);
}
Object unpack(Info vi, Buffer opb) {
InfoFloor0 info = new InfoFloor0();
info.order = opb.read(8);
info.rate = opb.read(16);
info.barkmap = opb.read(16);
info.ampbits = opb.read(6);
info.ampdB = opb.read(8);
info.numbooks = opb.read(4) + 1;
if ((info.order < 1) || (info.rate < 1) || (info.barkmap < 1) || (info.numbooks < 1)) {
return (null);
}
for (int j = 0; j < info.numbooks; j++) {
info.books[j] = opb.read(8);
if (info.books[j] < 0 || info.books[j] >= vi.books) {
return (null);
}
}
return (info);
}
Object look(DspState vd, InfoMode mi, Object i) {
float scale;
Info vi = vd.vi;
InfoFloor0 info = (InfoFloor0) i;
LookFloor0 look = new LookFloor0();
look.m = info.order;
look.n = vi.blocksizes[mi.blockflag] / 2;
look.ln = info.barkmap;
look.vi = info;
look.lpclook.init(look.ln, look.m);
// we choose a scaling constant so that:
scale = look.ln / toBARK((float) (info.rate / 2.));
// the mapping from a linear scale to a smaller bark scale is
// straightforward. We do *not* make sure that the linear mapping
// does not skip bark-scale bins; the decoder simply skips them and
// the encoder may do what it wishes in filling them. They're
// necessary in some mapping combinations to keep the scale spacing
// accurate
look.linearmap = new int[look.n];
for (int j = 0; j < look.n; j++) {
int val = (int) Math.floor(toBARK((float) ((info.rate / 2.) / look.n * j)) * scale); // bark numbers
// represent band
// edges
if (val >= look.ln)
val = look.ln; // guard against the approximation
look.linearmap[j] = val;
}
return look;
}
static float toBARK(float f) {
return (float) (13.1 * Math.atan(.00074 * (f)) + 2.24 * Math.atan((f) * (f) * 1.85e-8) + 1e-4 * (f));
}
Object state(Object i) {
EchstateFloor0 state = new EchstateFloor0();
InfoFloor0 info = (InfoFloor0) i;
// a safe size if usually too big (dim==1)
state.codewords = new int[info.order];
state.curve = new float[info.barkmap];
state.frameno = -1;
return (state);
}
void free_info(Object i) {
}
void free_look(Object i) {
}
void free_state(Object vs) {
}
int forward(Block vb, Object i, float[] in, float[] out, Object vs) {
return 0;
}
float[] lsp = null;
int inverse(Block vb, Object i, float[] out) {
// System.err.println("Floor0.inverse "+i.getClass()+"]");
LookFloor0 look = (LookFloor0) i;
InfoFloor0 info = look.vi;
int ampraw = vb.opb.read(info.ampbits);
if (ampraw > 0) { // also handles the -1 out of data case
int maxval = (1 << info.ampbits) - 1;
float amp = (float) ampraw / maxval * info.ampdB;
int booknum = vb.opb.read(Util.ilog(info.numbooks));
if (booknum != -1 && booknum < info.numbooks) {
synchronized (this) {
if (lsp == null || lsp.length < look.m) {
lsp = new float[look.m];
} else {
for (int j = 0; j < look.m; j++)
lsp[j] = 0.f;
}
CodeBook b = vb.vd.fullbooks[info.books[booknum]];
float last = 0.f;
for (int j = 0; j < look.m; j++)
out[j] = 0.0f;
for (int j = 0; j < look.m; j += b.dim) {
if (b.decodevs(lsp, j, vb.opb, 1, -1) == -1) {
for (int k = 0; k < look.n; k++)
out[k] = 0.0f;
return (0);
}
}
for (int j = 0; j < look.m;) {
for (int k = 0; k < b.dim; k++, j++)
lsp[j] += last;
last = lsp[j - 1];
}
// take the coefficients back to a spectral envelope curve
Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp, info.ampdB);
return (1);
}
}
}
return (0);
}
Object inverse1(Block vb, Object i, Object memo) {
LookFloor0 look = (LookFloor0) i;
InfoFloor0 info = look.vi;
float[] lsp = null;
if (memo instanceof float[]) {
lsp = (float[]) memo;
}
int ampraw = vb.opb.read(info.ampbits);
if (ampraw > 0) { // also handles the -1 out of data case
int maxval = (1 << info.ampbits) - 1;
float amp = (float) ampraw / maxval * info.ampdB;
int booknum = vb.opb.read(Util.ilog(info.numbooks));
if (booknum != -1 && booknum < info.numbooks) {
CodeBook b = vb.vd.fullbooks[info.books[booknum]];
float last = 0.f;
if (lsp == null || lsp.length < look.m + 1) {
lsp = new float[look.m + 1];
} else {
for (int j = 0; j < lsp.length; j++)
lsp[j] = 0.f;
}
for (int j = 0; j < look.m; j += b.dim) {
if (b.decodev_set(lsp, j, vb.opb, b.dim) == -1) {
return (null);
}
}
for (int j = 0; j < look.m;) {
for (int k = 0; k < b.dim; k++, j++)
lsp[j] += last;
last = lsp[j - 1];
}
lsp[look.m] = amp;
return (lsp);
}
}
return (null);
}
int inverse2(Block vb, Object i, Object memo, float[] out) {
LookFloor0 look = (LookFloor0) i;
InfoFloor0 info = look.vi;
if (memo != null) {
float[] lsp = (float[]) memo;
float amp = lsp[look.m];
Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp, info.ampdB);
return (1);
}
for (int j = 0; j < look.n; j++) {
out[j] = 0.f;
}
return (0);
}
static float fromdB(float x) {
return (float) (Math.exp((x) * .11512925));
}
static void lsp_to_lpc(float[] lsp, float[] lpc, int m) {
int i, j, m2 = m / 2;
float[] O = new float[m2];
float[] E = new float[m2];
float A;
float[] Ae = new float[m2 + 1];
float[] Ao = new float[m2 + 1];
float B;
float[] Be = new float[m2];
float[] Bo = new float[m2];
float temp;
// even/odd roots setup
for (i = 0; i < m2; i++) {
O[i] = (float) (-2. * Math.cos(lsp[i * 2]));
E[i] = (float) (-2. * Math.cos(lsp[i * 2 + 1]));
}
// set up impulse response
for (j = 0; j < m2; j++) {
Ae[j] = 0.f;
Ao[j] = 1.f;
Be[j] = 0.f;
Bo[j] = 1.f;
}
Ao[j] = 1.f;
Ae[j] = 1.f;
// run impulse response
for (i = 1; i < m + 1; i++) {
A = B = 0.f;
for (j = 0; j < m2; j++) {
temp = O[j] * Ao[j] + Ae[j];
Ae[j] = Ao[j];
Ao[j] = A;
A += temp;
temp = E[j] * Bo[j] + Be[j];
Be[j] = Bo[j];
Bo[j] = B;
B += temp;
}
lpc[i - 1] = (A + Ao[j] + B - Ae[j]) / 2;
Ao[j] = A;
Ae[j] = B;
}
}
static void lpc_to_curve(float[] curve, float[] lpc, float amp, LookFloor0 l, String name, int frameno) {
// l->m+1 must be less than l->ln, but guard in case we get a bad stream
float[] lcurve = new float[Math.max(l.ln * 2, l.m * 2 + 2)];
if (amp == 0) {
for (int j = 0; j < l.n; j++)
curve[j] = 0.0f;
return;
}
l.lpclook.lpc_to_curve(lcurve, lpc, amp);
for (int i = 0; i < l.n; i++)
curve[i] = lcurve[l.linearmap[i]];
}
class InfoFloor0 {
int order;
int rate;
int barkmap;
int ampbits;
int ampdB;
int numbooks; // <= 16
int[] books = new int[16];
}
class LookFloor0 {
int n;
int ln;
int m;
int[] linearmap;
InfoFloor0 vi;
Lpc lpclook = new Lpc();
}
class EchstateFloor0 {
int[] codewords;
float[] curve;
long frameno;
long codes;
}
}

@ -0,0 +1,584 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Floor1 extends FuncFloor {
static final int floor1_rangedb = 140;
static final int VIF_POSIT = 63;
void pack(Object i, Buffer opb) {
InfoFloor1 info = (InfoFloor1) i;
int count = 0;
int rangebits;
int maxposit = info.postlist[1];
int maxclass = -1;
/* save out partitions */
opb.write(info.partitions, 5); /* only 0 to 31 legal */
for (int j = 0; j < info.partitions; j++) {
opb.write(info.partitionclass[j], 4); /* only 0 to 15 legal */
if (maxclass < info.partitionclass[j])
maxclass = info.partitionclass[j];
}
/* save out partition classes */
for (int j = 0; j < maxclass + 1; j++) {
opb.write(info.class_dim[j] - 1, 3); /* 1 to 8 */
opb.write(info.class_subs[j], 2); /* 0 to 3 */
if (info.class_subs[j] != 0) {
opb.write(info.class_book[j], 8);
}
for (int k = 0; k < (1 << info.class_subs[j]); k++) {
opb.write(info.class_subbook[j][k] + 1, 8);
}
}
/* save out the post list */
opb.write(info.mult - 1, 2); /* only 1,2,3,4 legal now */
opb.write(Util.ilog2(maxposit), 4);
rangebits = Util.ilog2(maxposit);
for (int j = 0, k = 0; j < info.partitions; j++) {
count += info.class_dim[info.partitionclass[j]];
for (; k < count; k++) {
opb.write(info.postlist[k + 2], rangebits);
}
}
}
Object unpack(Info vi, Buffer opb) {
int count = 0, maxclass = -1, rangebits;
InfoFloor1 info = new InfoFloor1();
/* read partitions */
info.partitions = opb.read(5); /* only 0 to 31 legal */
for (int j = 0; j < info.partitions; j++) {
info.partitionclass[j] = opb.read(4); /* only 0 to 15 legal */
if (maxclass < info.partitionclass[j])
maxclass = info.partitionclass[j];
}
/* read partition classes */
for (int j = 0; j < maxclass + 1; j++) {
info.class_dim[j] = opb.read(3) + 1; /* 1 to 8 */
info.class_subs[j] = opb.read(2); /* 0,1,2,3 bits */
if (info.class_subs[j] < 0) {
info.free();
return (null);
}
if (info.class_subs[j] != 0) {
info.class_book[j] = opb.read(8);
}
if (info.class_book[j] < 0 || info.class_book[j] >= vi.books) {
info.free();
return (null);
}
for (int k = 0; k < (1 << info.class_subs[j]); k++) {
info.class_subbook[j][k] = opb.read(8) - 1;
if (info.class_subbook[j][k] < -1 || info.class_subbook[j][k] >= vi.books) {
info.free();
return (null);
}
}
}
/* read the post list */
info.mult = opb.read(2) + 1; /* only 1,2,3,4 legal now */
rangebits = opb.read(4);
for (int j = 0, k = 0; j < info.partitions; j++) {
count += info.class_dim[info.partitionclass[j]];
for (; k < count; k++) {
int t = info.postlist[k + 2] = opb.read(rangebits);
if (t < 0 || t >= (1 << rangebits)) {
info.free();
return (null);
}
}
}
info.postlist[0] = 0;
info.postlist[1] = 1 << rangebits;
return (info);
}
Object look(DspState vd, InfoMode mi, Object i) {
int _n = 0;
int[] sortpointer = new int[VIF_POSIT + 2];
// Info vi=vd.vi;
InfoFloor1 info = (InfoFloor1) i;
LookFloor1 look = new LookFloor1();
look.vi = info;
look.n = info.postlist[1];
/*
* we drop each position value in-between already decoded values, and use linear
* interpolation to predict each new value past the edges. The positions are
* read in the order of the position list... we precompute the bounding
* positions in the lookup. Of course, the neighbors can change (if a position
* is declined), but this is an initial mapping
*/
for (int j = 0; j < info.partitions; j++) {
_n += info.class_dim[info.partitionclass[j]];
}
_n += 2;
look.posts = _n;
/* also store a sorted position index */
for (int j = 0; j < _n; j++) {
sortpointer[j] = j;
}
// qsort(sortpointer,n,sizeof(int),icomp); // !!
int foo;
for (int j = 0; j < _n - 1; j++) {
for (int k = j; k < _n; k++) {
if (info.postlist[sortpointer[j]] > info.postlist[sortpointer[k]]) {
foo = sortpointer[k];
sortpointer[k] = sortpointer[j];
sortpointer[j] = foo;
}
}
}
/* points from sort order back to range number */
for (int j = 0; j < _n; j++) {
look.forward_index[j] = sortpointer[j];
}
/* points from range order to sorted position */
for (int j = 0; j < _n; j++) {
look.reverse_index[look.forward_index[j]] = j;
}
/* we actually need the post values too */
for (int j = 0; j < _n; j++) {
look.sorted_index[j] = info.postlist[look.forward_index[j]];
}
/* quantize values to multiplier spec */
switch (info.mult) {
case 1: /* 1024 -> 256 */
look.quant_q = 256;
break;
case 2: /* 1024 -> 128 */
look.quant_q = 128;
break;
case 3: /* 1024 -> 86 */
look.quant_q = 86;
break;
case 4: /* 1024 -> 64 */
look.quant_q = 64;
break;
default:
look.quant_q = -1;
}
/*
* discover our neighbors for decode where we don't use fit flags (that would
* push the neighbors outward)
*/
for (int j = 0; j < _n - 2; j++) {
int lo = 0;
int hi = 1;
int lx = 0;
int hx = look.n;
int currentx = info.postlist[j + 2];
for (int k = 0; k < j + 2; k++) {
int x = info.postlist[k];
if (x > lx && x < currentx) {
lo = k;
lx = x;
}
if (x < hx && x > currentx) {
hi = k;
hx = x;
}
}
look.loneighbor[j] = lo;
look.hineighbor[j] = hi;
}
return look;
}
void free_info(Object i) {
}
void free_look(Object i) {
}
void free_state(Object vs) {
}
int forward(Block vb, Object i, float[] in, float[] out, Object vs) {
return 0;
}
Object inverse1(Block vb, Object ii, Object memo) {
LookFloor1 look = (LookFloor1) ii;
InfoFloor1 info = look.vi;
CodeBook[] books = vb.vd.fullbooks;
/* unpack wrapped/predicted values from stream */
if (vb.opb.read(1) == 1) {
int[] fit_value = null;
if (memo instanceof int[]) {
fit_value = (int[]) memo;
}
if (fit_value == null || fit_value.length < look.posts) {
fit_value = new int[look.posts];
} else {
for (int i = 0; i < fit_value.length; i++)
fit_value[i] = 0;
}
fit_value[0] = vb.opb.read(Util.ilog(look.quant_q - 1));
fit_value[1] = vb.opb.read(Util.ilog(look.quant_q - 1));
/* partition by partition */
for (int i = 0, j = 2; i < info.partitions; i++) {
int clss = info.partitionclass[i];
int cdim = info.class_dim[clss];
int csubbits = info.class_subs[clss];
int csub = 1 << csubbits;
int cval = 0;
/* decode the partition's first stage cascade value */
if (csubbits != 0) {
cval = books[info.class_book[clss]].decode(vb.opb);
if (cval == -1) {
return (null);
}
}
for (int k = 0; k < cdim; k++) {
int book = info.class_subbook[clss][cval & (csub - 1)];
cval >>>= csubbits;
if (book >= 0) {
if ((fit_value[j + k] = books[book].decode(vb.opb)) == -1) {
return (null);
}
} else {
fit_value[j + k] = 0;
}
}
j += cdim;
}
/* unwrap positive values and reconsitute via linear interpolation */
for (int i = 2; i < look.posts; i++) {
int predicted = render_point(info.postlist[look.loneighbor[i - 2]],
info.postlist[look.hineighbor[i - 2]], fit_value[look.loneighbor[i - 2]],
fit_value[look.hineighbor[i - 2]], info.postlist[i]);
int hiroom = look.quant_q - predicted;
int loroom = predicted;
int room = (hiroom < loroom ? hiroom : loroom) << 1;
int val = fit_value[i];
if (val != 0) {
if (val >= room) {
if (hiroom > loroom) {
val = val - loroom;
} else {
val = -1 - (val - hiroom);
}
} else {
if ((val & 1) != 0) {
val = -((val + 1) >>> 1);
} else {
val >>= 1;
}
}
fit_value[i] = val + predicted;
fit_value[look.loneighbor[i - 2]] &= 0x7fff;
fit_value[look.hineighbor[i - 2]] &= 0x7fff;
} else {
fit_value[i] = predicted | 0x8000;
}
}
return (fit_value);
}
return (null);
}
private static int render_point(int x0, int x1, int y0, int y1, int x) {
y0 &= 0x7fff; /* mask off flag */
y1 &= 0x7fff;
{
int dy = y1 - y0;
int adx = x1 - x0;
int ady = Math.abs(dy);
int err = ady * (x - x0);
int off = (int) (err / adx);
if (dy < 0)
return (y0 - off);
return (y0 + off);
}
}
int inverse2(Block vb, Object i, Object memo, float[] out) {
LookFloor1 look = (LookFloor1) i;
InfoFloor1 info = look.vi;
int n = vb.vd.vi.blocksizes[vb.mode] / 2;
if (memo != null) {
/* render the lines */
int[] fit_value = (int[]) memo;
int hx = 0;
int lx = 0;
int ly = fit_value[0] * info.mult;
for (int j = 1; j < look.posts; j++) {
int current = look.forward_index[j];
int hy = fit_value[current] & 0x7fff;
if (hy == fit_value[current]) {
hy *= info.mult;
hx = info.postlist[current];
render_line(lx, hx, ly, hy, out);
lx = hx;
ly = hy;
}
}
for (int j = hx; j < n; j++) {
out[j] *= out[j - 1]; /* be certain */
}
return (1);
}
for (int j = 0; j < n; j++) {
out[j] = 0.f;
}
return (0);
}
private static float[] FLOOR_fromdB_LOOKUP = { 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F,
1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, 1.7623575e-07F, 1.8768855e-07F,
1.9988561e-07F, 2.128753e-07F, 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F,
2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, 3.7516214e-07F, 3.9954229e-07F,
4.2550680e-07F, 4.5315863e-07F, 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F,
6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, 7.9862701e-07F, 8.5052630e-07F,
9.0579828e-07F, 9.6466216e-07F, 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F,
1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, 1.7000785e-06F, 1.8105592e-06F,
1.9282195e-06F, 2.0535261e-06F, 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F,
2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, 3.6190449e-06F, 3.8542308e-06F,
4.1047004e-06F, 4.3714470e-06F, 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F,
5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, 7.7040476e-06F, 8.2047000e-06F,
8.7378876e-06F, 9.3057248e-06F, 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F,
1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, 1.6400004e-05F, 1.7465768e-05F,
1.8600792e-05F, 1.9809576e-05F, 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F,
2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, 3.4911534e-05F, 3.7180282e-05F,
3.9596466e-05F, 4.2169667e-05F, 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F,
5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, 7.4317983e-05F, 7.9147585e-05F,
8.4291040e-05F, 8.9768747e-05F, 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F,
0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, 0.00015820453F, 0.00016848555F,
0.00017943469F, 0.00019109536F, 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F,
0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, 0.00033677814F, 0.00035866388F,
0.00038197188F, 0.00040679456F, 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F,
0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, 0.00071691700F, 0.00076350630F,
0.00081312324F, 0.00086596457F, 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, 0.0011863665F,
0.0012634633F, 0.0013455702F, 0.0014330129F, 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F,
0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, 0.0025254795F, 0.0026895994F, 0.0028643847F,
0.0030505286F, 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, 0.0041792066F, 0.0044507950F,
0.0047400328F, 0.0050480668F, 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, 0.0069158225F,
0.0073652516F, 0.0078438871F, 0.0083536271F, 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F,
0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, 0.014722068F, 0.015678791F, 0.016697687F,
0.017782797F, 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, 0.024362330F, 0.025945531F,
0.027631618F, 0.029427276F, 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, 0.040315199F,
0.042935108F, 0.045725273F, 0.048696758F, 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F,
0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, 0.085821044F, 0.091398179F, 0.097337747F,
0.10366330F, 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, 0.14201813F, 0.15124727F, 0.16107617F,
0.17154380F, 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, 0.23501402F, 0.25028656F, 0.26655159F,
0.28387361F, 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, 0.38890521F, 0.41417847F, 0.44109412F,
0.46975890F, 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, 0.64356699F, 0.68538959F, 0.72993007F,
0.77736504F, 0.82788260F, 0.88168307F, 0.9389798F, 1.F };
private static void render_line(int x0, int x1, int y0, int y1, float[] d) {
int dy = y1 - y0;
int adx = x1 - x0;
int ady = Math.abs(dy);
int base = dy / adx;
int sy = (dy < 0 ? base - 1 : base + 1);
int x = x0;
int y = y0;
int err = 0;
ady -= Math.abs(base * adx);
d[x] *= FLOOR_fromdB_LOOKUP[y];
while (++x < x1) {
err = err + ady;
if (err >= adx) {
err -= adx;
y += sy;
} else {
y += base;
}
d[x] *= FLOOR_fromdB_LOOKUP[y];
}
}
class InfoFloor1 {
static final int VIF_POSIT = 63;
static final int VIF_CLASS = 16;
static final int VIF_PARTS = 31;
int partitions; /* 0 to 31 */
int[] partitionclass = new int[VIF_PARTS]; /* 0 to 15 */
int[] class_dim = new int[VIF_CLASS]; /* 1 to 8 */
int[] class_subs = new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1<<n poss) */
int[] class_book = new int[VIF_CLASS]; /* subs ^ dim entries */
int[][] class_subbook = new int[VIF_CLASS][]; /* [VIF_CLASS][subs] */
int mult; /* 1 2 3 or 4 */
int[] postlist = new int[VIF_POSIT + 2]; /* first two implicit */
/* encode side analysis parameters */
float maxover;
float maxunder;
float maxerr;
int twofitminsize;
int twofitminused;
int twofitweight;
float twofitatten;
int unusedminsize;
int unusedmin_n;
int n;
InfoFloor1() {
for (int i = 0; i < class_subbook.length; i++) {
class_subbook[i] = new int[8];
}
}
void free() {
partitionclass = null;
class_dim = null;
class_subs = null;
class_book = null;
class_subbook = null;
postlist = null;
}
Object copy_info() {
InfoFloor1 info = this;
InfoFloor1 ret = new InfoFloor1();
ret.partitions = info.partitions;
System.arraycopy(info.partitionclass, 0, ret.partitionclass, 0, VIF_PARTS);
System.arraycopy(info.class_dim, 0, ret.class_dim, 0, VIF_CLASS);
System.arraycopy(info.class_subs, 0, ret.class_subs, 0, VIF_CLASS);
System.arraycopy(info.class_book, 0, ret.class_book, 0, VIF_CLASS);
for (int j = 0; j < VIF_CLASS; j++) {
System.arraycopy(info.class_subbook[j], 0, ret.class_subbook[j], 0, 8);
}
ret.mult = info.mult;
System.arraycopy(info.postlist, 0, ret.postlist, 0, VIF_POSIT + 2);
ret.maxover = info.maxover;
ret.maxunder = info.maxunder;
ret.maxerr = info.maxerr;
ret.twofitminsize = info.twofitminsize;
ret.twofitminused = info.twofitminused;
ret.twofitweight = info.twofitweight;
ret.twofitatten = info.twofitatten;
ret.unusedminsize = info.unusedminsize;
ret.unusedmin_n = info.unusedmin_n;
ret.n = info.n;
return (ret);
}
}
class LookFloor1 {
static final int VIF_POSIT = 63;
int[] sorted_index = new int[VIF_POSIT + 2];
int[] forward_index = new int[VIF_POSIT + 2];
int[] reverse_index = new int[VIF_POSIT + 2];
int[] hineighbor = new int[VIF_POSIT];
int[] loneighbor = new int[VIF_POSIT];
int posts;
int n;
int quant_q;
InfoFloor1 vi;
int phrasebits;
int postbits;
int frames;
void free() {
sorted_index = null;
forward_index = null;
reverse_index = null;
hineighbor = null;
loneighbor = null;
}
}
class Lsfit_acc {
long x0;
long x1;
long xa;
long ya;
long x2a;
long y2a;
long xya;
long n;
long an;
long un;
long edgey0;
long edgey1;
}
class EchstateFloor1 {
int[] codewords;
float[] curve;
long frameno;
long codes;
}
}

@ -0,0 +1,52 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncFloor {
public static FuncFloor[] floor_P = { new Floor0(), new Floor1() };
abstract void pack(Object i, Buffer opb);
abstract Object unpack(Info vi, Buffer opb);
abstract Object look(DspState vd, InfoMode mi, Object i);
abstract void free_info(Object i);
abstract void free_look(Object i);
abstract void free_state(Object vs);
abstract int forward(Block vb, Object i, float[] in, float[] out, Object vs);
abstract Object inverse1(Block vb, Object i, Object memo);
abstract int inverse2(Block vb, Object i, Object memo, float[] out);
}

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncMapping {
public static FuncMapping[] mapping_P = { new Mapping0() };
abstract void pack(Info info, Object imap, Buffer buffer);
abstract Object unpack(Info info, Buffer buffer);
abstract Object look(DspState vd, InfoMode vm, Object m);
abstract void free_info(Object imap);
abstract void free_look(Object imap);
abstract int inverse(Block vd, Object lm);
}

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncResidue {
public static FuncResidue[] residue_P = { new Residue0(), new Residue1(), new Residue2() };
abstract void pack(Object vr, Buffer opb);
abstract Object unpack(Info vi, Buffer opb);
abstract Object look(DspState vd, InfoMode vm, Object vr);
abstract void free_info(Object i);
abstract void free_look(Object i);
abstract int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch);
}

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncTime {
public static FuncTime[] time_P = { new Time0() };
abstract void pack(Object i, Buffer opb);
abstract Object unpack(Info vi, Buffer opb);
abstract Object look(DspState vd, InfoMode vm, Object i);
abstract void free_info(Object i);
abstract void free_look(Object i);
abstract int inverse(Block vb, Object i, float[] in, float[] out);
}

@ -0,0 +1,468 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
public class Info {
private static final int OV_EBADPACKET = -136;
private static final int OV_ENOTAUDIO = -135;
private static byte[] _vorbis = "vorbis".getBytes();
private static final int VI_TIMEB = 1;
// private static final int VI_FLOORB=1;
private static final int VI_FLOORB = 2;
// private static final int VI_RESB=1;
private static final int VI_RESB = 3;
private static final int VI_MAPB = 1;
private static final int VI_WINDOWB = 1;
public int version;
public int channels;
public int rate;
// The below bitrate declarations are *hints*.
// Combinations of the three values carry the following implications:
//
// all three set to the same value:
// implies a fixed rate bitstream
// only nominal set:
// implies a VBR stream that averages the nominal bitrate. No hard
// upper/lower limit
// upper and or lower set:
// implies a VBR bitstream that obeys the bitrate limits. nominal
// may also be set to give a nominal rate.
// none set:
// the coder does not care to speculate.
int bitrate_upper;
int bitrate_nominal;
int bitrate_lower;
// Vorbis supports only short and long blocks, but allows the
// encoder to choose the sizes
int[] blocksizes = new int[2];
// modes are the primary means of supporting on-the-fly different
// blocksizes, different channel mappings (LR or mid-side),
// different residue backends, etc. Each mode consists of a
// blocksize flag and a mapping (along with the mapping setup
int modes;
int maps;
int times;
int floors;
int residues;
int books;
int psys; // encode only
InfoMode[] mode_param = null;
int[] map_type = null;
Object[] map_param = null;
int[] time_type = null;
Object[] time_param = null;
int[] floor_type = null;
Object[] floor_param = null;
int[] residue_type = null;
Object[] residue_param = null;
StaticCodeBook[] book_param = null;
PsyInfo[] psy_param = new PsyInfo[64]; // encode only
// for block long/sort tuning; encode only
int envelopesa;
float preecho_thresh;
float preecho_clamp;
// used by synthesis, which has a full, alloced vi
public void init() {
rate = 0;
}
public void clear() {
for (int i = 0; i < modes; i++) {
mode_param[i] = null;
}
mode_param = null;
for (int i = 0; i < maps; i++) { // unpack does the range checking
FuncMapping.mapping_P[map_type[i]].free_info(map_param[i]);
}
map_param = null;
for (int i = 0; i < times; i++) { // unpack does the range checking
FuncTime.time_P[time_type[i]].free_info(time_param[i]);
}
time_param = null;
for (int i = 0; i < floors; i++) { // unpack does the range checking
FuncFloor.floor_P[floor_type[i]].free_info(floor_param[i]);
}
floor_param = null;
for (int i = 0; i < residues; i++) { // unpack does the range checking
FuncResidue.residue_P[residue_type[i]].free_info(residue_param[i]);
}
residue_param = null;
// the static codebooks *are* freed if you call info_clear, because
// decode side does alloc a 'static' codebook. Calling clear on the
// full codebook does not clear the static codebook (that's our
// responsibility)
for (int i = 0; i < books; i++) {
// just in case the decoder pre-cleared to save space
if (book_param[i] != null) {
book_param[i].clear();
book_param[i] = null;
}
}
// if(vi->book_param)free(vi->book_param);
book_param = null;
for (int i = 0; i < psys; i++) {
psy_param[i].free();
}
}
// Header packing/unpacking
int unpack_info(Buffer opb) {
version = opb.read(32);
if (version != 0)
return (-1);
channels = opb.read(8);
rate = opb.read(32);
bitrate_upper = opb.read(32);
bitrate_nominal = opb.read(32);
bitrate_lower = opb.read(32);
blocksizes[0] = 1 << opb.read(4);
blocksizes[1] = 1 << opb.read(4);
if ((rate < 1) || (channels < 1) || (blocksizes[0] < 8) || (blocksizes[1] < blocksizes[0])
|| (opb.read(1) != 1)) {
clear();
return (-1);
}
return (0);
}
// all of the real encoding details are here. The modes, books,
// everything
int unpack_books(Buffer opb) {
books = opb.read(8) + 1;
if (book_param == null || book_param.length != books)
book_param = new StaticCodeBook[books];
for (int i = 0; i < books; i++) {
book_param[i] = new StaticCodeBook();
if (book_param[i].unpack(opb) != 0) {
clear();
return (-1);
}
}
// time backend settings
times = opb.read(6) + 1;
if (time_type == null || time_type.length != times)
time_type = new int[times];
if (time_param == null || time_param.length != times)
time_param = new Object[times];
for (int i = 0; i < times; i++) {
time_type[i] = opb.read(16);
if (time_type[i] < 0 || time_type[i] >= VI_TIMEB) {
clear();
return (-1);
}
time_param[i] = FuncTime.time_P[time_type[i]].unpack(this, opb);
if (time_param[i] == null) {
clear();
return (-1);
}
}
// floor backend settings
floors = opb.read(6) + 1;
if (floor_type == null || floor_type.length != floors)
floor_type = new int[floors];
if (floor_param == null || floor_param.length != floors)
floor_param = new Object[floors];
for (int i = 0; i < floors; i++) {
floor_type[i] = opb.read(16);
if (floor_type[i] < 0 || floor_type[i] >= VI_FLOORB) {
clear();
return (-1);
}
floor_param[i] = FuncFloor.floor_P[floor_type[i]].unpack(this, opb);
if (floor_param[i] == null) {
clear();
return (-1);
}
}
// residue backend settings
residues = opb.read(6) + 1;
if (residue_type == null || residue_type.length != residues)
residue_type = new int[residues];
if (residue_param == null || residue_param.length != residues)
residue_param = new Object[residues];
for (int i = 0; i < residues; i++) {
residue_type[i] = opb.read(16);
if (residue_type[i] < 0 || residue_type[i] >= VI_RESB) {
clear();
return (-1);
}
residue_param[i] = FuncResidue.residue_P[residue_type[i]].unpack(this, opb);
if (residue_param[i] == null) {
clear();
return (-1);
}
}
// map backend settings
maps = opb.read(6) + 1;
if (map_type == null || map_type.length != maps)
map_type = new int[maps];
if (map_param == null || map_param.length != maps)
map_param = new Object[maps];
for (int i = 0; i < maps; i++) {
map_type[i] = opb.read(16);
if (map_type[i] < 0 || map_type[i] >= VI_MAPB) {
clear();
return (-1);
}
map_param[i] = FuncMapping.mapping_P[map_type[i]].unpack(this, opb);
if (map_param[i] == null) {
clear();
return (-1);
}
}
// mode settings
modes = opb.read(6) + 1;
if (mode_param == null || mode_param.length != modes)
mode_param = new InfoMode[modes];
for (int i = 0; i < modes; i++) {
mode_param[i] = new InfoMode();
mode_param[i].blockflag = opb.read(1);
mode_param[i].windowtype = opb.read(16);
mode_param[i].transformtype = opb.read(16);
mode_param[i].mapping = opb.read(8);
if ((mode_param[i].windowtype >= VI_WINDOWB) || (mode_param[i].transformtype >= VI_WINDOWB)
|| (mode_param[i].mapping >= maps)) {
clear();
return (-1);
}
}
if (opb.read(1) != 1) {
clear();
return (-1);
}
return (0);
}
// The Vorbis header is in three packets; the initial small packet in
// the first page that identifies basic parameters, a second packet
// with bitstream comments and a third packet that holds the
// codebook.
public int synthesis_headerin(Comment vc, Packet op) {
Buffer opb = new Buffer();
if (op != null) {
opb.readinit(op.packet_base, op.packet, op.bytes);
// Which of the three types of header is this?
// Also verify header-ness, vorbis
{
byte[] buffer = new byte[6];
int packtype = opb.read(8);
opb.read(buffer, 6);
if (buffer[0] != 'v' || buffer[1] != 'o' || buffer[2] != 'r' || buffer[3] != 'b' || buffer[4] != 'i'
|| buffer[5] != 's') {
// not a vorbis header
return (-1);
}
switch (packtype) {
case 0x01: // least significant *bit* is read first
if (op.b_o_s == 0) {
// Not the initial packet
return (-1);
}
if (rate != 0) {
// previously initialized info header
return (-1);
}
return (unpack_info(opb));
case 0x03: // least significant *bit* is read first
if (rate == 0) {
// um... we didn't get the initial header
return (-1);
}
return (vc.unpack(opb));
case 0x05: // least significant *bit* is read first
if (rate == 0 || vc.vendor == null) {
// um... we didn;t get the initial header or comments yet
return (-1);
}
return (unpack_books(opb));
default:
// Not a valid vorbis header type
// return(-1);
break;
}
}
}
return (-1);
}
// pack side
int pack_info(Buffer opb) {
// preamble
opb.write(0x01, 8);
opb.write(_vorbis);
// basic information about the stream
opb.write(0x00, 32);
opb.write(channels, 8);
opb.write(rate, 32);
opb.write(bitrate_upper, 32);
opb.write(bitrate_nominal, 32);
opb.write(bitrate_lower, 32);
opb.write(Util.ilog2(blocksizes[0]), 4);
opb.write(Util.ilog2(blocksizes[1]), 4);
opb.write(1, 1);
return (0);
}
int pack_books(Buffer opb) {
opb.write(0x05, 8);
opb.write(_vorbis);
// books
opb.write(books - 1, 8);
for (int i = 0; i < books; i++) {
if (book_param[i].pack(opb) != 0) {
// goto err_out;
return (-1);
}
}
// times
opb.write(times - 1, 6);
for (int i = 0; i < times; i++) {
opb.write(time_type[i], 16);
FuncTime.time_P[time_type[i]].pack(this.time_param[i], opb);
}
// floors
opb.write(floors - 1, 6);
for (int i = 0; i < floors; i++) {
opb.write(floor_type[i], 16);
FuncFloor.floor_P[floor_type[i]].pack(floor_param[i], opb);
}
// residues
opb.write(residues - 1, 6);
for (int i = 0; i < residues; i++) {
opb.write(residue_type[i], 16);
FuncResidue.residue_P[residue_type[i]].pack(residue_param[i], opb);
}
// maps
opb.write(maps - 1, 6);
for (int i = 0; i < maps; i++) {
opb.write(map_type[i], 16);
FuncMapping.mapping_P[map_type[i]].pack(this, map_param[i], opb);
}
// modes
opb.write(modes - 1, 6);
for (int i = 0; i < modes; i++) {
opb.write(mode_param[i].blockflag, 1);
opb.write(mode_param[i].windowtype, 16);
opb.write(mode_param[i].transformtype, 16);
opb.write(mode_param[i].mapping, 8);
}
opb.write(1, 1);
return (0);
}
public int blocksize(Packet op) {
// codec_setup_info
Buffer opb = new Buffer();
int mode;
opb.readinit(op.packet_base, op.packet, op.bytes);
/* Check the packet type */
if (opb.read(1) != 0) {
/* Oops. This is not an audio data packet */
return (OV_ENOTAUDIO);
}
{
int modebits = 0;
int v = modes;
while (v > 1) {
modebits++;
v >>>= 1;
}
/* read our mode and pre/post windowsize */
mode = opb.read(modebits);
}
if (mode == -1)
return (OV_EBADPACKET);
return (blocksizes[mode_param[mode].blockflag]);
}
public String toString() {
return "version:" + version + ", channels:" + channels + ", rate:" + rate
+ ", bitrate:" + bitrate_upper + "," + bitrate_nominal + ","
+ bitrate_lower;
}
}

@ -0,0 +1,34 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class InfoMode {
int blockflag;
int windowtype;
int transformtype;
int mapping;
}

@ -0,0 +1,40 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
public class JOrbisException extends Exception {
private static final long serialVersionUID = 1L;
public JOrbisException() {
super();
}
public JOrbisException(String s) {
super("JOrbis: " + s);
}
}

@ -0,0 +1,122 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Lookup {
static final int COS_LOOKUP_SZ = 128;
static final float[] COS_LOOKUP = { +1.0000000000000f, +0.9996988186962f, +0.9987954562052f, +0.9972904566787f,
+0.9951847266722f, +0.9924795345987f, +0.9891765099648f, +0.9852776423889f, +0.9807852804032f,
+0.9757021300385f, +0.9700312531945f, +0.9637760657954f, +0.9569403357322f, +0.9495281805930f,
+0.9415440651830f, +0.9329927988347f, +0.9238795325113f, +0.9142097557035f, +0.9039892931234f,
+0.8932243011955f, +0.8819212643484f, +0.8700869911087f, +0.8577286100003f, +0.8448535652497f,
+0.8314696123025f, +0.8175848131516f, +0.8032075314806f, +0.7883464276266f, +0.7730104533627f,
+0.7572088465065f, +0.7409511253550f, +0.7242470829515f, +0.7071067811865f, +0.6895405447371f,
+0.6715589548470f, +0.6531728429538f, +0.6343932841636f, +0.6152315905806f, +0.5956993044924f,
+0.5758081914178f, +0.5555702330196f, +0.5349976198871f, +0.5141027441932f, +0.4928981922298f,
+0.4713967368260f, +0.4496113296546f, +0.4275550934303f, +0.4052413140050f, +0.3826834323651f,
+0.3598950365350f, +0.3368898533922f, +0.3136817403989f, +0.2902846772545f, +0.2667127574749f,
+0.2429801799033f, +0.2191012401569f, +0.1950903220161f, +0.1709618887603f, +0.1467304744554f,
+0.1224106751992f, +0.0980171403296f, +0.0735645635997f, +0.0490676743274f, +0.0245412285229f,
+0.0000000000000f, -0.0245412285229f, -0.0490676743274f, -0.0735645635997f, -0.0980171403296f,
-0.1224106751992f, -0.1467304744554f, -0.1709618887603f, -0.1950903220161f, -0.2191012401569f,
-0.2429801799033f, -0.2667127574749f, -0.2902846772545f, -0.3136817403989f, -0.3368898533922f,
-0.3598950365350f, -0.3826834323651f, -0.4052413140050f, -0.4275550934303f, -0.4496113296546f,
-0.4713967368260f, -0.4928981922298f, -0.5141027441932f, -0.5349976198871f, -0.5555702330196f,
-0.5758081914178f, -0.5956993044924f, -0.6152315905806f, -0.6343932841636f, -0.6531728429538f,
-0.6715589548470f, -0.6895405447371f, -0.7071067811865f, -0.7242470829515f, -0.7409511253550f,
-0.7572088465065f, -0.7730104533627f, -0.7883464276266f, -0.8032075314806f, -0.8175848131516f,
-0.8314696123025f, -0.8448535652497f, -0.8577286100003f, -0.8700869911087f, -0.8819212643484f,
-0.8932243011955f, -0.9039892931234f, -0.9142097557035f, -0.9238795325113f, -0.9329927988347f,
-0.9415440651830f, -0.9495281805930f, -0.9569403357322f, -0.9637760657954f, -0.9700312531945f,
-0.9757021300385f, -0.9807852804032f, -0.9852776423889f, -0.9891765099648f, -0.9924795345987f,
-0.9951847266722f, -0.9972904566787f, -0.9987954562052f, -0.9996988186962f, -1.0000000000000f, };
/* interpolated lookup based cos function, domain 0 to PI only */
static float coslook(float a) {
double d = a * (.31830989 * (float) COS_LOOKUP_SZ);
int i = (int) d;
return COS_LOOKUP[i] + ((float) (d - i)) * (COS_LOOKUP[i + 1] - COS_LOOKUP[i]);
}
static final int INVSQ_LOOKUP_SZ = 32;
static final float[] INVSQ_LOOKUP = { 1.414213562373f, 1.392621247646f, 1.371988681140f, 1.352246807566f,
1.333333333333f, 1.315191898443f, 1.297771369046f, 1.281025230441f, 1.264911064067f, 1.249390095109f,
1.234426799697f, 1.219988562661f, 1.206045378311f, 1.192569588000f, 1.179535649239f, 1.166919931983f,
1.154700538379f, 1.142857142857f, 1.131370849898f, 1.120224067222f, 1.109400392450f, 1.098884511590f,
1.088662107904f, 1.078719779941f, 1.069044967650f, 1.059625885652f, 1.050451462878f, 1.041511287847f,
1.032795558989f, 1.024295039463f, 1.016001016002f, 1.007905261358f, 1.000000000000f, };
/* interpolated 1./sqrt(p) where .5 <= p < 1. */
static float invsqlook(float a) {
double d = a * (2.f * (float) INVSQ_LOOKUP_SZ) - (float) INVSQ_LOOKUP_SZ;
int i = (int) d;
return INVSQ_LOOKUP[i] + ((float) (d - i)) * (INVSQ_LOOKUP[i + 1] - INVSQ_LOOKUP[i]);
}
static final int INVSQ2EXP_LOOKUP_MIN = -32;
static final int INVSQ2EXP_LOOKUP_MAX = 32;
static final float[] INVSQ2EXP_LOOKUP = { 65536.f, 46340.95001f, 32768.f, 23170.47501f, 16384.f, 11585.2375f,
8192.f, 5792.618751f, 4096.f, 2896.309376f, 2048.f, 1448.154688f, 1024.f, 724.0773439f, 512.f, 362.038672f,
256.f, 181.019336f, 128.f, 90.50966799f, 64.f, 45.254834f, 32.f, 22.627417f, 16.f, 11.3137085f, 8.f,
5.656854249f, 4.f, 2.828427125f, 2.f, 1.414213562f, 1.f, 0.7071067812f, 0.5f, 0.3535533906f, 0.25f,
0.1767766953f, 0.125f, 0.08838834765f, 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, 0.015625f,
0.01104854346f, 0.0078125f, 0.005524271728f, 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f,
0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, 0.000244140625f, 0.0001726334915f,
0.0001220703125f, 8.631674575e-05f, 6.103515625e-05f, 4.315837288e-05f, 3.051757812e-05f, 2.157918644e-05f,
1.525878906e-05f, };
/* interpolated 1./sqrt(p) where .5 <= p < 1. */
static float invsq2explook(int a) {
return INVSQ2EXP_LOOKUP[a - INVSQ2EXP_LOOKUP_MIN];
}
static final int FROMdB_LOOKUP_SZ = 35;
static final int FROMdB2_LOOKUP_SZ = 32;
static final int FROMdB_SHIFT = 5;
static final int FROMdB2_SHIFT = 3;
static final int FROMdB2_MASK = 31;
static final float[] FROMdB_LOOKUP = { 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, 0.1584893192f, 0.1f,
0.06309573445f, 0.03981071706f, 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, 0.003981071706f,
0.002511886432f, 0.001584893192f, 0.001f, 0.0006309573445f, 0.0003981071706f, 0.0002511886432f,
0.0001584893192f, 0.0001f, 6.309573445e-05f, 3.981071706e-05f, 2.511886432e-05f, 1.584893192e-05f, 1e-05f,
6.309573445e-06f, 3.981071706e-06f, 2.511886432e-06f, 1.584893192e-06f, 1e-06f, 6.309573445e-07f,
3.981071706e-07f, 2.511886432e-07f, 1.584893192e-07f, };
static final float[] FROMdB2_LOOKUP = { 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, 0.9372921937f,
0.92390007f, 0.9106992942f, 0.8976871324f, 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f,
0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, 0.7886330981f, 0.7773650302f, 0.7662579617f,
0.755309592f, 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, 0.7028699885f, 0.6928273125f,
0.6829281272f, 0.6731703824f, 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, };
/* interpolated lookup based fromdB function, domain -140dB to 0dB only */
static float fromdBlook(float a) {
int i = (int) (a * ((float) (-(1 << FROMdB2_SHIFT))));
return (i < 0) ? 1.f
: ((i >= (FROMdB_LOOKUP_SZ << FROMdB_SHIFT)) ? 0.f
: FROMdB_LOOKUP[i >>> FROMdB_SHIFT] * FROMdB2_LOOKUP[i & FROMdB2_MASK]);
}
}

@ -0,0 +1,185 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Lpc {
// en/decode lookups
Drft fft = new Drft();;
int ln;
int m;
// Autocorrelation LPC coeff generation algorithm invented by
// N. Levinson in 1947, modified by J. Durbin in 1959.
// Input : n elements of time doamin data
// Output: m lpc coefficients, excitation energy
static float lpc_from_data(float[] data, float[] lpc, int n, int m) {
float[] aut = new float[m + 1];
float error;
int i, j;
// autocorrelation, p+1 lag coefficients
j = m + 1;
while (j-- != 0) {
float d = 0;
for (i = j; i < n; i++)
d += data[i] * data[i - j];
aut[j] = d;
}
// Generate lpc coefficients from autocorr values
error = aut[0];
/*
* if(error==0){ for(int k=0; k<m; k++) lpc[k]=0.0f; return 0; }
*/
for (i = 0; i < m; i++) {
float r = -aut[i + 1];
if (error == 0) {
for (int k = 0; k < m; k++)
lpc[k] = 0.0f;
return 0;
}
// Sum up this iteration's reflection coefficient; note that in
// Vorbis we don't save it. If anyone wants to recycle this code
// and needs reflection coefficients, save the results of 'r' from
// each iteration.
for (j = 0; j < i; j++)
r -= lpc[j] * aut[i - j];
r /= error;
// Update LPC coefficients and total error
lpc[i] = r;
for (j = 0; j < i / 2; j++) {
float tmp = lpc[j];
lpc[j] += r * lpc[i - 1 - j];
lpc[i - 1 - j] += r * tmp;
}
if (i % 2 != 0)
lpc[j] += lpc[j] * r;
error *= 1.0 - r * r;
}
// we need the error value to know how big an impulse to hit the
// filter with later
return error;
}
// Input : n element envelope spectral curve
// Output: m lpc coefficients, excitation energy
float lpc_from_curve(float[] curve, float[] lpc) {
int n = ln;
float[] work = new float[n + n];
float fscale = (float) (.5 / n);
int i, j;
// input is a real curve. make it complex-real
// This mixes phase, but the LPC generation doesn't care.
for (i = 0; i < n; i++) {
work[i * 2] = curve[i] * fscale;
work[i * 2 + 1] = 0;
}
work[n * 2 - 1] = curve[n - 1] * fscale;
n *= 2;
fft.backward(work);
// The autocorrelation will not be circular. Shift, else we lose
// most of the power in the edges.
for (i = 0, j = n / 2; i < n / 2;) {
float temp = work[i];
work[i++] = work[j];
work[j++] = temp;
}
return (lpc_from_data(work, lpc, n, m));
}
void init(int mapped, int m) {
ln = mapped;
this.m = m;
// we cheat decoding the LPC spectrum via FFTs
fft.init(mapped * 2);
}
void clear() {
fft.clear();
}
static float FAST_HYPOT(float a, float b) {
return (float) Math.sqrt((a) * (a) + (b) * (b));
}
// One can do this the long way by generating the transfer function in
// the time domain and taking the forward FFT of the result. The
// results from direct calculation are cleaner and faster.
//
// This version does a linear curve generation and then later
// interpolates the log curve from the linear curve.
void lpc_to_curve(float[] curve, float[] lpc, float amp) {
for (int i = 0; i < ln * 2; i++)
curve[i] = 0.0f;
if (amp == 0)
return;
for (int i = 0; i < m; i++) {
curve[i * 2 + 1] = lpc[i] / (4 * amp);
curve[i * 2 + 2] = -lpc[i] / (4 * amp);
}
fft.backward(curve);
{
int l2 = ln * 2;
float unit = (float) (1. / amp);
curve[0] = (float) (1. / (curve[0] * 2 + unit));
for (int i = 1; i < ln; i++) {
float real = (curve[i] + curve[l2 - i]);
float imag = (curve[i] - curve[l2 - i]);
float a = real + unit;
curve[i] = (float) (1.0 / FAST_HYPOT(a, imag));
}
}
}
}

@ -0,0 +1,102 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
/*
function: LSP (also called LSF) conversion routines
The LSP generation code is taken (with minimal modification) from
"On the Computation of the LSP Frequencies" by Joseph Rothweiler
<rothwlr@altavista.net>, available at:
http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html
********************************************************************/
class Lsp {
static final float M_PI = (float) (3.1415926539);
static void lsp_to_curve(float[] curve, int[] map, int n, int ln, float[] lsp, int m, float amp, float ampoffset) {
int i;
float wdel = M_PI / ln;
for (i = 0; i < m; i++)
lsp[i] = Lookup.coslook(lsp[i]);
int m2 = (m / 2) * 2;
i = 0;
while (i < n) {
int k = map[i];
float p = .7071067812f;
float q = .7071067812f;
float w = Lookup.coslook(wdel * k);
for (int j = 0; j < m2; j += 2) {
q *= lsp[j] - w;
p *= lsp[j + 1] - w;
}
if ((m & 1) != 0) {
/* odd order filter; slightly assymetric */
/* the last coefficient */
q *= lsp[m - 1] - w;
q *= q;
p *= p * (1.f - w * w);
} else {
/* even order filter; still symmetric */
q *= q * (1.f + w);
p *= p * (1.f - w);
}
// q=frexp(p+q,&qexp);
q = p + q;
int hx = Float.floatToIntBits(q);
int ix = 0x7fffffff & hx;
int qexp = 0;
if (ix >= 0x7f800000 || (ix == 0)) {
// 0,inf,nan
} else {
if (ix < 0x00800000) { // subnormal
q *= 3.3554432000e+07; // 0x4c000000
hx = Float.floatToIntBits(q);
ix = 0x7fffffff & hx;
qexp = -25;
}
qexp += ((ix >>> 23) - 126);
hx = (hx & 0x807fffff) | 0x3f000000;
q = Float.intBitsToFloat(hx);
}
q = Lookup.fromdBlook(amp * Lookup.invsqlook(q) * Lookup.invsq2explook(qexp + m) - ampoffset);
do {
curve[i++] *= q;
} while (i < n && map[i] == k);
}
}
}

@ -0,0 +1,361 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Mapping0 extends FuncMapping {
static int seq = 0;
void free_info(Object imap) {
};
void free_look(Object imap) {
}
Object look(DspState vd, InfoMode vm, Object m) {
// System.err.println("Mapping0.look");
Info vi = vd.vi;
LookMapping0 look = new LookMapping0();
InfoMapping0 info = look.map = (InfoMapping0) m;
look.mode = vm;
look.time_look = new Object[info.submaps];
look.floor_look = new Object[info.submaps];
look.residue_look = new Object[info.submaps];
look.time_func = new FuncTime[info.submaps];
look.floor_func = new FuncFloor[info.submaps];
look.residue_func = new FuncResidue[info.submaps];
for (int i = 0; i < info.submaps; i++) {
int timenum = info.timesubmap[i];
int floornum = info.floorsubmap[i];
int resnum = info.residuesubmap[i];
look.time_func[i] = FuncTime.time_P[vi.time_type[timenum]];
look.time_look[i] = look.time_func[i].look(vd, vm, vi.time_param[timenum]);
look.floor_func[i] = FuncFloor.floor_P[vi.floor_type[floornum]];
look.floor_look[i] = look.floor_func[i].look(vd, vm, vi.floor_param[floornum]);
look.residue_func[i] = FuncResidue.residue_P[vi.residue_type[resnum]];
look.residue_look[i] = look.residue_func[i].look(vd, vm, vi.residue_param[resnum]);
}
if (vi.psys != 0 && vd.analysisp != 0) {
// ??
}
look.ch = vi.channels;
return (look);
}
void pack(Info vi, Object imap, Buffer opb) {
InfoMapping0 info = (InfoMapping0) imap;
/*
* another 'we meant to do it this way' hack... up to beta 4, we packed 4 binary
* zeros here to signify one submapping in use. We now redefine that to mean
* four bitflags that indicate use of deeper features; bit0:submappings,
* bit1:coupling, bit2,3:reserved. This is backward compatable with all actual
* uses of the beta code.
*/
if (info.submaps > 1) {
opb.write(1, 1);
opb.write(info.submaps - 1, 4);
} else {
opb.write(0, 1);
}
if (info.coupling_steps > 0) {
opb.write(1, 1);
opb.write(info.coupling_steps - 1, 8);
for (int i = 0; i < info.coupling_steps; i++) {
opb.write(info.coupling_mag[i], Util.ilog2(vi.channels));
opb.write(info.coupling_ang[i], Util.ilog2(vi.channels));
}
} else {
opb.write(0, 1);
}
opb.write(0, 2); /* 2,3:reserved */
/* we don't write the channel submappings if we only have one... */
if (info.submaps > 1) {
for (int i = 0; i < vi.channels; i++)
opb.write(info.chmuxlist[i], 4);
}
for (int i = 0; i < info.submaps; i++) {
opb.write(info.timesubmap[i], 8);
opb.write(info.floorsubmap[i], 8);
opb.write(info.residuesubmap[i], 8);
}
}
// also responsible for range checking
Object unpack(Info vi, Buffer opb) {
InfoMapping0 info = new InfoMapping0();
if (opb.read(1) != 0) {
info.submaps = opb.read(4) + 1;
} else {
info.submaps = 1;
}
if (opb.read(1) != 0) {
info.coupling_steps = opb.read(8) + 1;
for (int i = 0; i < info.coupling_steps; i++) {
int testM = info.coupling_mag[i] = opb.read(Util.ilog2(vi.channels));
int testA = info.coupling_ang[i] = opb.read(Util.ilog2(vi.channels));
if (testM < 0 || testA < 0 || testM == testA || testM >= vi.channels || testA >= vi.channels) {
// goto err_out;
info.free();
return (null);
}
}
}
if (opb.read(2) > 0) { /* 2,3:reserved */
info.free();
return (null);
}
if (info.submaps > 1) {
for (int i = 0; i < vi.channels; i++) {
info.chmuxlist[i] = opb.read(4);
if (info.chmuxlist[i] >= info.submaps) {
info.free();
return (null);
}
}
}
for (int i = 0; i < info.submaps; i++) {
info.timesubmap[i] = opb.read(8);
if (info.timesubmap[i] >= vi.times) {
info.free();
return (null);
}
info.floorsubmap[i] = opb.read(8);
if (info.floorsubmap[i] >= vi.floors) {
info.free();
return (null);
}
info.residuesubmap[i] = opb.read(8);
if (info.residuesubmap[i] >= vi.residues) {
info.free();
return (null);
}
}
return info;
}
float[][] pcmbundle = null;
int[] zerobundle = null;
int[] nonzero = null;
Object[] floormemo = null;
synchronized int inverse(Block vb, Object l) {
DspState vd = vb.vd;
Info vi = vd.vi;
LookMapping0 look = (LookMapping0) l;
InfoMapping0 info = look.map;
InfoMode mode = look.mode;
int n = vb.pcmend = vi.blocksizes[vb.W];
float[] window = vd.window[vb.W][vb.lW][vb.nW][mode.windowtype];
if (pcmbundle == null || pcmbundle.length < vi.channels) {
pcmbundle = new float[vi.channels][];
nonzero = new int[vi.channels];
zerobundle = new int[vi.channels];
floormemo = new Object[vi.channels];
}
// time domain information decode (note that applying the
// information would have to happen later; we'll probably add a
// function entry to the harness for that later
// NOT IMPLEMENTED
// recover the spectral envelope; store it in the PCM vector for now
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
int submap = info.chmuxlist[i];
floormemo[i] = look.floor_func[submap].inverse1(vb, look.floor_look[submap], floormemo[i]);
if (floormemo[i] != null) {
nonzero[i] = 1;
} else {
nonzero[i] = 0;
}
for (int j = 0; j < n / 2; j++) {
pcm[j] = 0;
}
}
for (int i = 0; i < info.coupling_steps; i++) {
if (nonzero[info.coupling_mag[i]] != 0 || nonzero[info.coupling_ang[i]] != 0) {
nonzero[info.coupling_mag[i]] = 1;
nonzero[info.coupling_ang[i]] = 1;
}
}
// recover the residue, apply directly to the spectral envelope
for (int i = 0; i < info.submaps; i++) {
int ch_in_bundle = 0;
for (int j = 0; j < vi.channels; j++) {
if (info.chmuxlist[j] == i) {
if (nonzero[j] != 0) {
zerobundle[ch_in_bundle] = 1;
} else {
zerobundle[ch_in_bundle] = 0;
}
pcmbundle[ch_in_bundle++] = vb.pcm[j];
}
}
look.residue_func[i].inverse(vb, look.residue_look[i], pcmbundle, zerobundle, ch_in_bundle);
}
for (int i = info.coupling_steps - 1; i >= 0; i--) {
float[] pcmM = vb.pcm[info.coupling_mag[i]];
float[] pcmA = vb.pcm[info.coupling_ang[i]];
for (int j = 0; j < n / 2; j++) {
float mag = pcmM[j];
float ang = pcmA[j];
if (mag > 0) {
if (ang > 0) {
pcmM[j] = mag;
pcmA[j] = mag - ang;
} else {
pcmA[j] = mag;
pcmM[j] = mag + ang;
}
} else {
if (ang > 0) {
pcmM[j] = mag;
pcmA[j] = mag + ang;
} else {
pcmA[j] = mag;
pcmM[j] = mag - ang;
}
}
}
}
// /* compute and apply spectral envelope */
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
int submap = info.chmuxlist[i];
look.floor_func[submap].inverse2(vb, look.floor_look[submap], floormemo[i], pcm);
}
// transform the PCM data; takes PCM vector, vb; modifies PCM vector
// only MDCT right now....
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
// _analysis_output("out",seq+i,pcm,n/2,0,0);
((Mdct) vd.transform[vb.W][0]).backward(pcm, pcm);
}
// now apply the decoded pre-window time information
// NOT IMPLEMENTED
// window the data
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
if (nonzero[i] != 0) {
for (int j = 0; j < n; j++) {
pcm[j] *= window[j];
}
} else {
for (int j = 0; j < n; j++) {
pcm[j] = 0.f;
}
}
}
// now apply the decoded post-window time information
// NOT IMPLEMENTED
// all done!
return (0);
}
class InfoMapping0 {
int submaps; // <= 16
int[] chmuxlist = new int[256]; // up to 256 channels in a Vorbis stream
int[] timesubmap = new int[16]; // [mux]
int[] floorsubmap = new int[16]; // [mux] submap to floors
int[] residuesubmap = new int[16];// [mux] submap to residue
int[] psysubmap = new int[16]; // [mux]; encode only
int coupling_steps;
int[] coupling_mag = new int[256];
int[] coupling_ang = new int[256];
void free() {
chmuxlist = null;
timesubmap = null;
floorsubmap = null;
residuesubmap = null;
psysubmap = null;
coupling_mag = null;
coupling_ang = null;
}
}
class LookMapping0 {
InfoMode mode;
InfoMapping0 map;
Object[] time_look;
Object[] floor_look;
Object[] floor_state;
Object[] residue_look;
PsyLook[] psy_look;
FuncTime[] time_func;
FuncFloor[] floor_func;
FuncResidue[] residue_func;
int ch;
float[][] decay;
int lastframe; // if a different mode is called, we need to
// invalidate decay and floor state
}
}

@ -0,0 +1,249 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Mdct {
int n;
int log2n;
float[] trig;
int[] bitrev;
float scale;
void init(int n) {
bitrev = new int[n / 4];
trig = new float[n + n / 4];
log2n = (int) Math.rint(Math.log(n) / Math.log(2));
this.n = n;
int AE = 0;
int AO = 1;
int BE = AE + n / 2;
int BO = BE + 1;
int CE = BE + n / 2;
int CO = CE + 1;
// trig lookups...
for (int i = 0; i < n / 4; i++) {
trig[AE + i * 2] = (float) Math.cos((Math.PI / n) * (4 * i));
trig[AO + i * 2] = (float) -Math.sin((Math.PI / n) * (4 * i));
trig[BE + i * 2] = (float) Math.cos((Math.PI / (2 * n)) * (2 * i + 1));
trig[BO + i * 2] = (float) Math.sin((Math.PI / (2 * n)) * (2 * i + 1));
}
for (int i = 0; i < n / 8; i++) {
trig[CE + i * 2] = (float) Math.cos((Math.PI / n) * (4 * i + 2));
trig[CO + i * 2] = (float) -Math.sin((Math.PI / n) * (4 * i + 2));
}
{
int mask = (1 << (log2n - 1)) - 1;
int msb = 1 << (log2n - 2);
for (int i = 0; i < n / 8; i++) {
int acc = 0;
for (int j = 0; msb >>> j != 0; j++)
if (((msb >>> j) & i) != 0)
acc |= 1 << j;
bitrev[i * 2] = ((~acc) & mask);
// bitrev[i*2]=((~acc)&mask)-1;
bitrev[i * 2 + 1] = acc;
}
}
scale = 4.f / n;
}
void clear() {
}
void forward(float[] in, float[] out) {
}
float[] _x = new float[1024];
float[] _w = new float[1024];
synchronized void backward(float[] in, float[] out) {
if (_x.length < n / 2) {
_x = new float[n / 2];
}
if (_w.length < n / 2) {
_w = new float[n / 2];
}
float[] x = _x;
float[] w = _w;
int n2 = n >>> 1;
int n4 = n >>> 2;
int n8 = n >>> 3;
// rotate + step 1
{
int inO = 1;
int xO = 0;
int A = n2;
int i;
for (i = 0; i < n8; i++) {
A -= 2;
x[xO++] = -in[inO + 2] * trig[A + 1] - in[inO] * trig[A];
x[xO++] = in[inO] * trig[A + 1] - in[inO + 2] * trig[A];
inO += 4;
}
inO = n2 - 4;
for (i = 0; i < n8; i++) {
A -= 2;
x[xO++] = in[inO] * trig[A + 1] + in[inO + 2] * trig[A];
x[xO++] = in[inO] * trig[A] - in[inO + 2] * trig[A + 1];
inO -= 4;
}
}
float[] xxx = mdct_kernel(x, w, n, n2, n4, n8);
int xx = 0;
// step 8
{
int B = n2;
int o1 = n4, o2 = o1 - 1;
int o3 = n4 + n2, o4 = o3 - 1;
for (int i = 0; i < n4; i++) {
float temp1 = (xxx[xx] * trig[B + 1] - xxx[xx + 1] * trig[B]);
float temp2 = -(xxx[xx] * trig[B] + xxx[xx + 1] * trig[B + 1]);
out[o1] = -temp1;
out[o2] = temp1;
out[o3] = temp2;
out[o4] = temp2;
o1++;
o2--;
o3++;
o4--;
xx += 2;
B += 2;
}
}
}
private float[] mdct_kernel(float[] x, float[] w, int n, int n2, int n4, int n8) {
// step 2
int xA = n4;
int xB = 0;
int w2 = n4;
int A = n2;
for (int i = 0; i < n4;) {
float x0 = x[xA] - x[xB];
float x1;
w[w2 + i] = x[xA++] + x[xB++];
x1 = x[xA] - x[xB];
A -= 4;
w[i++] = x0 * trig[A] + x1 * trig[A + 1];
w[i] = x1 * trig[A] - x0 * trig[A + 1];
w[w2 + i] = x[xA++] + x[xB++];
i++;
}
// step 3
{
for (int i = 0; i < log2n - 3; i++) {
int k0 = n >>> (i + 2);
int k1 = 1 << (i + 3);
int wbase = n2 - 2;
A = 0;
float[] temp;
for (int r = 0; r < (k0 >>> 2); r++) {
int w1 = wbase;
w2 = w1 - (k0 >> 1);
float AEv = trig[A], wA;
float AOv = trig[A + 1], wB;
wbase -= 2;
k0++;
for (int s = 0; s < (2 << i); s++) {
wB = w[w1] - w[w2];
x[w1] = w[w1] + w[w2];
wA = w[++w1] - w[++w2];
x[w1] = w[w1] + w[w2];
x[w2] = wA * AEv - wB * AOv;
x[w2 - 1] = wB * AEv + wA * AOv;
w1 -= k0;
w2 -= k0;
}
k0--;
A += k1;
}
temp = w;
w = x;
x = temp;
}
}
// step 4, 5, 6, 7
{
int C = n;
int bit = 0;
int x1 = 0;
int x2 = n2 - 1;
for (int i = 0; i < n8; i++) {
int t1 = bitrev[bit++];
int t2 = bitrev[bit++];
float wA = w[t1] - w[t2 + 1];
float wB = w[t1 - 1] + w[t2];
float wC = w[t1] + w[t2 + 1];
float wD = w[t1 - 1] - w[t2];
float wACE = wA * trig[C];
float wBCE = wB * trig[C++];
float wACO = wA * trig[C];
float wBCO = wB * trig[C++];
x[x1++] = (wC + wACO + wBCE) * .5f;
x[x2--] = (-wD + wBCO - wACE) * .5f;
x[x1++] = (wD + wBCO - wACE) * .5f;
x[x2--] = (wC - wACO - wBCE) * .5f;
}
}
return (x);
}
}

@ -0,0 +1,74 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
// psychoacoustic setup
class PsyInfo {
int athp;
int decayp;
int smoothp;
int noisefitp;
int noisefit_subblock;
float noisefit_threshdB;
float ath_att;
int tonemaskp;
float[] toneatt_125Hz = new float[5];
float[] toneatt_250Hz = new float[5];
float[] toneatt_500Hz = new float[5];
float[] toneatt_1000Hz = new float[5];
float[] toneatt_2000Hz = new float[5];
float[] toneatt_4000Hz = new float[5];
float[] toneatt_8000Hz = new float[5];
int peakattp;
float[] peakatt_125Hz = new float[5];
float[] peakatt_250Hz = new float[5];
float[] peakatt_500Hz = new float[5];
float[] peakatt_1000Hz = new float[5];
float[] peakatt_2000Hz = new float[5];
float[] peakatt_4000Hz = new float[5];
float[] peakatt_8000Hz = new float[5];
int noisemaskp;
float[] noiseatt_125Hz = new float[5];
float[] noiseatt_250Hz = new float[5];
float[] noiseatt_500Hz = new float[5];
float[] noiseatt_1000Hz = new float[5];
float[] noiseatt_2000Hz = new float[5];
float[] noiseatt_4000Hz = new float[5];
float[] noiseatt_8000Hz = new float[5];
float max_curve_dB;
float attack_coeff;
float decay_coeff;
void free() {
}
}

@ -0,0 +1,42 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class PsyLook {
int n;
PsyInfo vi;
float[][][] tonecurves;
float[][] peakatt;
float[][][] noisecurves;
float[] ath;
int[] octave;
void init(PsyInfo vi, int n, int rate) {
}
}

@ -0,0 +1,326 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Residue0 extends FuncResidue {
void pack(Object vr, Buffer opb) {
InfoResidue0 info = (InfoResidue0) vr;
int acc = 0;
opb.write(info.begin, 24);
opb.write(info.end, 24);
opb.write(info.grouping - 1, 24); /*
* residue vectors to group and code with a partitioned book
*/
opb.write(info.partitions - 1, 6); /* possible partition choices */
opb.write(info.groupbook, 8); /* group huffman book */
/*
* secondstages is a bitmask; as encoding progresses pass by pass, a bitmask of
* one indicates this partition class has bits to write this pass
*/
for (int j = 0; j < info.partitions; j++) {
int i = info.secondstages[j];
if (Util.ilog(i) > 3) {
/* yes, this is a minor hack due to not thinking ahead */
opb.write(i, 3);
opb.write(1, 1);
opb.write(i >>> 3, 5);
} else {
opb.write(i, 4); /* trailing zero */
}
acc += Util.icount(i);
}
for (int j = 0; j < acc; j++) {
opb.write(info.booklist[j], 8);
}
}
Object unpack(Info vi, Buffer opb) {
int acc = 0;
InfoResidue0 info = new InfoResidue0();
info.begin = opb.read(24);
info.end = opb.read(24);
info.grouping = opb.read(24) + 1;
info.partitions = opb.read(6) + 1;
info.groupbook = opb.read(8);
for (int j = 0; j < info.partitions; j++) {
int cascade = opb.read(3);
if (opb.read(1) != 0) {
cascade |= (opb.read(5) << 3);
}
info.secondstages[j] = cascade;
acc += Util.icount(cascade);
}
for (int j = 0; j < acc; j++) {
info.booklist[j] = opb.read(8);
}
if (info.groupbook >= vi.books) {
free_info(info);
return (null);
}
for (int j = 0; j < acc; j++) {
if (info.booklist[j] >= vi.books) {
free_info(info);
return (null);
}
}
return (info);
}
Object look(DspState vd, InfoMode vm, Object vr) {
InfoResidue0 info = (InfoResidue0) vr;
LookResidue0 look = new LookResidue0();
int acc = 0;
int dim;
int maxstage = 0;
look.info = info;
look.map = vm.mapping;
look.parts = info.partitions;
look.fullbooks = vd.fullbooks;
look.phrasebook = vd.fullbooks[info.groupbook];
dim = look.phrasebook.dim;
look.partbooks = new int[look.parts][];
for (int j = 0; j < look.parts; j++) {
int i = info.secondstages[j];
int stages = Util.ilog(i);
if (stages != 0) {
if (stages > maxstage)
maxstage = stages;
look.partbooks[j] = new int[stages];
for (int k = 0; k < stages; k++) {
if ((i & (1 << k)) != 0) {
look.partbooks[j][k] = info.booklist[acc++];
}
}
}
}
look.partvals = (int) Math.rint(Math.pow(look.parts, dim));
look.stages = maxstage;
look.decodemap = new int[look.partvals][];
for (int j = 0; j < look.partvals; j++) {
int val = j;
int mult = look.partvals / look.parts;
look.decodemap[j] = new int[dim];
for (int k = 0; k < dim; k++) {
int deco = val / mult;
val -= deco * mult;
mult /= look.parts;
look.decodemap[j][k] = deco;
}
}
return (look);
}
void free_info(Object i) {
}
void free_look(Object i) {
}
private static int[][][] _01inverse_partword = new int[2][][]; // _01inverse is synchronized for
// re-using partword
synchronized static int _01inverse(Block vb, Object vl, float[][] in, int ch, int decodepart) {
int i, j, k, l, s;
LookResidue0 look = (LookResidue0) vl;
InfoResidue0 info = look.info;
// move all this setup out later
int samples_per_partition = info.grouping;
int partitions_per_word = look.phrasebook.dim;
int n = info.end - info.begin;
int partvals = n / samples_per_partition;
int partwords = (partvals + partitions_per_word - 1) / partitions_per_word;
if (_01inverse_partword.length < ch) {
_01inverse_partword = new int[ch][][];
}
for (j = 0; j < ch; j++) {
if (_01inverse_partword[j] == null || _01inverse_partword[j].length < partwords) {
_01inverse_partword[j] = new int[partwords][];
}
}
for (s = 0; s < look.stages; s++) {
// each loop decodes on partition codeword containing
// partitions_pre_word partitions
for (i = 0, l = 0; i < partvals; l++) {
if (s == 0) {
// fetch the partition word for each channel
for (j = 0; j < ch; j++) {
int temp = look.phrasebook.decode(vb.opb);
if (temp == -1) {
return (0);
}
_01inverse_partword[j][l] = look.decodemap[temp];
if (_01inverse_partword[j][l] == null) {
return (0);
}
}
}
// now we decode residual values for the partitions
for (k = 0; k < partitions_per_word && i < partvals; k++, i++)
for (j = 0; j < ch; j++) {
int offset = info.begin + i * samples_per_partition;
int index = _01inverse_partword[j][l][k];
if ((info.secondstages[index] & (1 << s)) != 0) {
CodeBook stagebook = look.fullbooks[look.partbooks[index][s]];
if (stagebook != null) {
if (decodepart == 0) {
if (stagebook.decodevs_add(in[j], offset, vb.opb, samples_per_partition) == -1) {
return (0);
}
} else if (decodepart == 1) {
if (stagebook.decodev_add(in[j], offset, vb.opb, samples_per_partition) == -1) {
return (0);
}
}
}
}
}
}
}
return (0);
}
static int[][] _2inverse_partword = null;
synchronized static int _2inverse(Block vb, Object vl, float[][] in, int ch) {
int i, k, l, s;
LookResidue0 look = (LookResidue0) vl;
InfoResidue0 info = look.info;
// move all this setup out later
int samples_per_partition = info.grouping;
int partitions_per_word = look.phrasebook.dim;
int n = info.end - info.begin;
int partvals = n / samples_per_partition;
int partwords = (partvals + partitions_per_word - 1) / partitions_per_word;
if (_2inverse_partword == null || _2inverse_partword.length < partwords) {
_2inverse_partword = new int[partwords][];
}
for (s = 0; s < look.stages; s++) {
for (i = 0, l = 0; i < partvals; l++) {
if (s == 0) {
// fetch the partition word for each channel
int temp = look.phrasebook.decode(vb.opb);
if (temp == -1) {
return (0);
}
_2inverse_partword[l] = look.decodemap[temp];
if (_2inverse_partword[l] == null) {
return (0);
}
}
// now we decode residual values for the partitions
for (k = 0; k < partitions_per_word && i < partvals; k++, i++) {
int offset = info.begin + i * samples_per_partition;
int index = _2inverse_partword[l][k];
if ((info.secondstages[index] & (1 << s)) != 0) {
CodeBook stagebook = look.fullbooks[look.partbooks[index][s]];
if (stagebook != null) {
if (stagebook.decodevv_add(in, offset, ch, vb.opb, samples_per_partition) == -1) {
return (0);
}
}
}
}
}
}
return (0);
}
int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) {
int used = 0;
for (int i = 0; i < ch; i++) {
if (nonzero[i] != 0) {
in[used++] = in[i];
}
}
if (used != 0)
return (_01inverse(vb, vl, in, used, 0));
else
return (0);
}
class LookResidue0 {
InfoResidue0 info;
int map;
int parts;
int stages;
CodeBook[] fullbooks;
CodeBook phrasebook;
int[][] partbooks;
int partvals;
int[][] decodemap;
int postbits;
int phrasebits;
int frames;
}
class InfoResidue0 {
// block-partitioned VQ coded straight residue
int begin;
int end;
// first stage (lossless partitioning)
int grouping; // group n vectors per partition
int partitions; // possible codebooks for a partition
int groupbook; // huffbook for partitioning
int[] secondstages = new int[64]; // expanded out to pointers in lookup
int[] booklist = new int[256]; // list of second stage books
// encode-only heuristic settings
float[] entmax = new float[64]; // book entropy threshholds
float[] ampmax = new float[64]; // book amp threshholds
int[] subgrp = new int[64]; // book heuristic subgroup size
int[] blimit = new int[64]; // subgroup position limits
}
}

@ -0,0 +1,44 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Residue1 extends Residue0 {
int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) {
int used = 0;
for (int i = 0; i < ch; i++) {
if (nonzero[i] != 0) {
in[used++] = in[i];
}
}
if (used != 0) {
return (_01inverse(vb, vl, in, used, 1));
} else {
return 0;
}
}
}

@ -0,0 +1,41 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Residue2 extends Residue0 {
int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) {
int i = 0;
for (i = 0; i < ch; i++)
if (nonzero[i] != 0)
break;
if (i == ch)
return (0); /* no nonzero vectors */
return (_2inverse(vb, vl, in, ch));
}
}

@ -0,0 +1,436 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class StaticCodeBook {
int dim; // codebook dimensions (elements per vector)
int entries; // codebook entries
int[] lengthlist; // codeword lengths in bits
// mapping
int maptype; // 0=none
// 1=implicitly populated values from map column
// 2=listed arbitrary values
// The below does a linear, single monotonic sequence mapping.
int q_min; // packed 32 bit float; quant value 0 maps to minval
int q_delta; // packed 32 bit float; val 1 - val 0 == delta
int q_quant; // bits: 0 < quant <= 16
int q_sequencep; // bitflag
// additional information for log (dB) mapping; the linear mapping
// is assumed to actually be values in dB. encodebias is used to
// assign an error weight to 0 dB. We have two additional flags:
// zeroflag indicates if entry zero is to represent -Inf dB; negflag
// indicates if we're to represent negative linear values in a
// mirror of the positive mapping.
int[] quantlist; // map == 1: (int)(entries/dim) element column map
// map == 2: list of dim*entries quantized entry vals
StaticCodeBook() {
}
int pack(Buffer opb) {
int i;
boolean ordered = false;
opb.write(0x564342, 24);
opb.write(dim, 16);
opb.write(entries, 24);
// pack the codewords. There are two packings; length ordered and
// length random. Decide between the two now.
for (i = 1; i < entries; i++) {
if (lengthlist[i] < lengthlist[i - 1])
break;
}
if (i == entries)
ordered = true;
if (ordered) {
// length ordered. We only need to say how many codewords of
// each length. The actual codewords are generated
// deterministically
int count = 0;
opb.write(1, 1); // ordered
opb.write(lengthlist[0] - 1, 5); // 1 to 32
for (i = 1; i < entries; i++) {
int _this = lengthlist[i];
int _last = lengthlist[i - 1];
if (_this > _last) {
for (int j = _last; j < _this; j++) {
opb.write(i - count, Util.ilog(entries - count));
count = i;
}
}
}
opb.write(i - count, Util.ilog(entries - count));
} else {
// length random. Again, we don't code the codeword itself, just
// the length. This time, though, we have to encode each length
opb.write(0, 1); // unordered
// algortihmic mapping has use for 'unused entries', which we tag
// here. The algorithmic mapping happens as usual, but the unused
// entry has no codeword.
for (i = 0; i < entries; i++) {
if (lengthlist[i] == 0)
break;
}
if (i == entries) {
opb.write(0, 1); // no unused entries
for (i = 0; i < entries; i++) {
opb.write(lengthlist[i] - 1, 5);
}
} else {
opb.write(1, 1); // we have unused entries; thus we tag
for (i = 0; i < entries; i++) {
if (lengthlist[i] == 0) {
opb.write(0, 1);
} else {
opb.write(1, 1);
opb.write(lengthlist[i] - 1, 5);
}
}
}
}
// is the entry number the desired return value, or do we have a
// mapping? If we have a mapping, what type?
opb.write(maptype, 4);
switch (maptype) {
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
if (quantlist == null) {
// no quantlist? error
return (-1);
}
// values that define the dequantization
opb.write(q_min, 32);
opb.write(q_delta, 32);
opb.write(q_quant - 1, 4);
opb.write(q_sequencep, 1);
{
int quantvals = 0;
switch (maptype) {
case 1:
// a single column of (c->entries/c->dim) quantized values for
// building a full value list algorithmically (square lattice)
quantvals = maptype1_quantvals();
break;
case 2:
// every value (c->entries*c->dim total) specified explicitly
quantvals = entries * dim;
break;
}
// quantized values
for (i = 0; i < quantvals; i++) {
opb.write(Math.abs(quantlist[i]), q_quant);
}
}
break;
default:
// error case; we don't have any other map types now
return (-1);
}
return (0);
}
// unpacks a codebook from the packet buffer into the codebook struct,
// readies the codebook auxiliary structures for decode
int unpack(Buffer opb) {
int i;
// memset(s,0,sizeof(static_codebook));
// make sure alignment is correct
if (opb.read(24) != 0x564342) {
// goto _eofout;
clear();
return (-1);
}
// first the basic parameters
dim = opb.read(16);
entries = opb.read(24);
if (entries == -1) {
// goto _eofout;
clear();
return (-1);
}
// codeword ordering.... length ordered or unordered?
switch (opb.read(1)) {
case 0:
// unordered
lengthlist = new int[entries];
// allocated but unused entries?
if (opb.read(1) != 0) {
// yes, unused entries
for (i = 0; i < entries; i++) {
if (opb.read(1) != 0) {
int num = opb.read(5);
if (num == -1) {
// goto _eofout;
clear();
return (-1);
}
lengthlist[i] = num + 1;
} else {
lengthlist[i] = 0;
}
}
} else {
// all entries used; no tagging
for (i = 0; i < entries; i++) {
int num = opb.read(5);
if (num == -1) {
// goto _eofout;
clear();
return (-1);
}
lengthlist[i] = num + 1;
}
}
break;
case 1:
// ordered
{
int length = opb.read(5) + 1;
lengthlist = new int[entries];
for (i = 0; i < entries;) {
int num = opb.read(Util.ilog(entries - i));
if (num == -1) {
// goto _eofout;
clear();
return (-1);
}
for (int j = 0; j < num; j++, i++) {
lengthlist[i] = length;
}
length++;
}
}
break;
default:
// EOF
return (-1);
}
// Do we have a mapping to unpack?
switch ((maptype = opb.read(4))) {
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
q_min = opb.read(32);
q_delta = opb.read(32);
q_quant = opb.read(4) + 1;
q_sequencep = opb.read(1);
{
int quantvals = 0;
switch (maptype) {
case 1:
quantvals = maptype1_quantvals();
break;
case 2:
quantvals = entries * dim;
break;
}
// quantized values
quantlist = new int[quantvals];
for (i = 0; i < quantvals; i++) {
quantlist[i] = opb.read(q_quant);
}
if (quantlist[quantvals - 1] == -1) {
// goto _eofout;
clear();
return (-1);
}
}
break;
default:
// goto _eofout;
clear();
return (-1);
}
// all set
return (0);
// _errout:
// _eofout:
// vorbis_staticbook_clear(s);
// return(-1);
}
// there might be a straightforward one-line way to do the below
// that's portable and totally safe against roundoff, but I haven't
// thought of it. Therefore, we opt on the side of caution
private int maptype1_quantvals() {
int vals = (int) (Math.floor(Math.pow(entries, 1. / dim)));
// the above *should* be reliable, but we'll not assume that FP is
// ever reliable when bitstream sync is at stake; verify via integer
// means that vals really is the greatest value of dim for which
// vals^b->bim <= b->entries
// treat the above as an initial guess
while (true) {
int acc = 1;
int acc1 = 1;
for (int i = 0; i < dim; i++) {
acc *= vals;
acc1 *= vals + 1;
}
if (acc <= entries && acc1 > entries) {
return (vals);
} else {
if (acc > entries) {
vals--;
} else {
vals++;
}
}
}
}
void clear() {
}
// unpack the quantized list of values for encode/decode
// we need to deal with two map types: in map type 1, the values are
// generated algorithmically (each column of the vector counts through
// the values in the quant vector). in map type 2, all the values came
// in in an explicit list. Both value lists must be unpacked
float[] unquantize() {
if (maptype == 1 || maptype == 2) {
int quantvals;
float mindel = float32_unpack(q_min);
float delta = float32_unpack(q_delta);
float[] r = new float[entries * dim];
// maptype 1 and 2 both use a quantized value vector, but
// different sizes
switch (maptype) {
case 1:
// most of the time, entries%dimensions == 0, but we need to be
// well defined. We define that the possible vales at each
// scalar is values == entries/dim. If entries%dim != 0, we'll
// have 'too few' values (values*dim<entries), which means that
// we'll have 'left over' entries; left over entries use zeroed
// values (and are wasted). So don't generate codebooks like that
quantvals = maptype1_quantvals();
for (int j = 0; j < entries; j++) {
float last = 0.f;
int indexdiv = 1;
for (int k = 0; k < dim; k++) {
int index = (j / indexdiv) % quantvals;
float val = quantlist[index];
val = Math.abs(val) * delta + mindel + last;
if (q_sequencep != 0)
last = val;
r[j * dim + k] = val;
indexdiv *= quantvals;
}
}
break;
case 2:
for (int j = 0; j < entries; j++) {
float last = 0.f;
for (int k = 0; k < dim; k++) {
float val = quantlist[j * dim + k];
// if((j*dim+k)==0){System.err.println(" | 0 -> "+val+" | ");}
val = Math.abs(val) * delta + mindel + last;
if (q_sequencep != 0)
last = val;
r[j * dim + k] = val;
// if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");}
}
}
// System.err.println("\nr[0]="+r[0]);
}
return (r);
}
return (null);
}
// 32 bit float (not IEEE; nonnormalized mantissa +
// biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm
// Why not IEEE? It's just not that important here.
static final int VQ_FEXP = 10;
static final int VQ_FMAN = 21;
static final int VQ_FEXP_BIAS = 768; // bias toward values smaller than 1.
// doesn't currently guard under/overflow
static long float32_pack(float val) {
int sign = 0;
int exp;
int mant;
if (val < 0) {
sign = 0x80000000;
val = -val;
}
exp = (int) Math.floor(Math.log(val) / Math.log(2));
mant = (int) Math.rint(Math.pow(val, (VQ_FMAN - 1) - exp));
exp = (exp + VQ_FEXP_BIAS) << VQ_FMAN;
return (sign | exp | mant);
}
static float float32_unpack(int val) {
float mant = val & 0x1fffff;
float exp = (val & 0x7fe00000) >>> VQ_FMAN;
if ((val & 0x80000000) != 0)
mant = -mant;
return (ldexp(mant, ((int) exp) - (VQ_FMAN - 1) - VQ_FEXP_BIAS));
}
static float ldexp(float foo, int e) {
return (float) (foo * Math.pow(2, e));
}
}

@ -0,0 +1,52 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Time0 extends FuncTime {
void pack(Object i, Buffer opb) {
}
Object unpack(Info vi, Buffer opb) {
return "";
}
Object look(DspState vd, InfoMode mi, Object i) {
return "";
}
void free_info(Object i) {
}
void free_look(Object i) {
}
int inverse(Block vb, Object i, float[] in, float[] out) {
return 0;
}
}

Some files were not shown because too many files have changed in this diff Show More