/* * Copyright (C) 2014 James Lawrence. * * This file is part of LibLab. * * LibLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.sqrt.liblab.codec; import com.sqrt.liblab.entry.lua.LocalVarDefinition; import com.sqrt.liblab.entry.lua.LuaChunk; import com.sqrt.liblab.entry.lua.LuaConstant; import com.sqrt.liblab.entry.lua.LuaFunction; import com.sqrt.liblab.io.DataSource; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Created by James on 29/09/2014. */ public class LuaChunkCodec extends EntryCodec<LuaChunk> { private String readTString(DataSource source) throws IOException { int size = source.getUShort(); if(size == 0) return null; byte[] b = new byte[size]; source.get(b, 0, size); for(int i = 0; i < b.length; i++) b[i] ^= 0xff; String s = new String(b); if(s.endsWith("\0")) s = s.substring(0, s.length() - 1); return s; } private ArrayList<LuaConstant> readConstants(DataSource source) throws IOException { int count = source.getUShort(); ArrayList<LuaConstant> constants = new ArrayList<>(count); for(int i = 0; i < count; i++) { int type = source.getUByte(); switch(type) { case 'N': // number, float - 4 bytes constants.add(new LuaConstant<>(source.getFloatLE())); break; case 'S': // string constants.add(new LuaConstant<>(readTString(source))); break; case 'F': // constant function, for some reason it uses null here :S (https://github.com/residualvm/residualvm/blob/master/engines/grim/lua/lundump.cpp#L112) constants.add(new LuaConstant<LuaFunction>(null)); break; default: System.err.println("Unknown LUA constant type: " + ((char) type)); break; } } return constants; } private ArrayList<LocalVarDefinition> readLocals(DataSource source) throws IOException { int count = source.getUShort(); ArrayList<LocalVarDefinition> lvars = new ArrayList<>(); for(int i = 0; i < count; i++) lvars.add(new LocalVarDefinition(source.getUShort(), readTString(source))); lvars.add(new LocalVarDefinition(-1, null)); return lvars; } private void readFunctions(List<LuaConstant> constants, DataSource source) throws IOException { while(source.getUByte() == '#') { int idx = source.getUShort(); LuaFunction func = readFunction(source); constants.set(idx, new LuaConstant<>(func)); } } private LuaFunction readFunction(DataSource source) throws IOException { LuaFunction lf = new LuaFunction(); lf.lineNo = source.getUShort(); lf.source = readTString(source); int codeSize = source.getInt(); lf.code = new byte[codeSize]; source.get(lf.code); lf.constants = readConstants(source); lf.localVars = readLocals(source); readFunctions(lf.constants, source); return lf; } @Override protected LuaChunk _read(DataSource source) throws IOException { int magic = source.getInt(); if(magic != 0x1B4C7561) throw new IOException("Invalid LUA file"); int version = source.getUByte(); if(version != 0x31) throw new IOException("Invalid LUA file version: " + Integer.toHexString(version)); if(source.getUByte() != 4) throw new IOException("Invalid LUA file, float length isn't 4..."); source.skip(4); // Todo: wtf are these? LuaFunction lf = readFunction(source); // Todo: decompile? haha, if it'll be anything like the java decompiler I wrote then fuck that return new LuaChunk(source.container, source.getName(), lf); } @Override public DataSource write(LuaChunk source) throws IOException { return null; } @Override public String[] getFileExtensions() { return new String[]{"lua"}; } @Override public Class<LuaChunk> getEntryClass() { return LuaChunk.class; } }