Working beeper (it can send morse)

Former-commit-id: 02553a52405ef4bba0d3f8f781f710b6c5b3ae16
Former-commit-id: 6cee7d2b44563a664756651d73a4b1ddfa70cf2f
This commit is contained in:
Song Minjae
2016-09-26 23:28:15 +09:00
parent 49fc723e66
commit bd4c95b321
12 changed files with 242 additions and 101 deletions

View File

@@ -20,13 +20,15 @@ import org.newdawn.slick.state.StateBasedGame
class StateVTTest : BasicGameState() { class StateVTTest : BasicGameState() {
// HiRes: 100x64, LoRes: 80x25 // HiRes: 100x64, LoRes: 80x25
val vt = SimpleTextTerminal(SimpleTextTerminal.GREEN, 80, 25, colour = false, hires = false) val computerInside = BaseTerrarumComputer()
val computerInside = BaseTerrarumComputer(vt) val vt = SimpleTextTerminal(SimpleTextTerminal.GREEN, 80, 25, computerInside, colour = false, hires = false)
val vtUI = Image(vt.displayW, vt.displayH) val vtUI = Image(vt.displayW, vt.displayH)
init { init {
computerInside.attachTerminal(vt)
} }
override fun init(container: GameContainer, game: StateBasedGame) { override fun init(container: GameContainer, game: StateBasedGame) {

View File

@@ -100,8 +100,8 @@ constructor(gamename: String) : StateBasedGame(gamename) {
gc.graphics.clear() // clean up any 'dust' in the buffer gc.graphics.clear() // clean up any 'dust' in the buffer
//addState(StateVTTest()) addState(StateVTTest())
addState(StateTestingSandbox()) //addState(StateTestingSandbox())
//addState(StateSplash()) //addState(StateSplash())
//addState(StateMonitorCheck()) //addState(StateMonitorCheck())
//addState(StateFontTester()) //addState(StateFontTester())

View File

@@ -950,7 +950,7 @@ sandbox._G = sandbox
package.path = "/net/torvald/terrarum/virtualcomputer/assets/lua/?.lua;" .. package.path package.path = "/net/torvald/terrarum/virtualcomputer/assets/lua/?.lua;" .. package.path
-- global variables -- global variables
_G._VERSION = "Luaj-jse 5.2.3" _G._VERSION = "Luaj-jse 3.0.1 (Lua 5.2.3)"
_G.EMDASH = string.char(0xC4) _G.EMDASH = string.char(0xC4)
_G.UNCHECKED = string.char(0x9C) -- box unchecked _G.UNCHECKED = string.char(0x9C) -- box unchecked
_G.CHECKED = string.char(0x9D) -- box checked _G.CHECKED = string.char(0x9D) -- box checked
@@ -962,6 +962,8 @@ _G.DC3 = string.char(19) -- dim grey
_G.DC4 = string.char(20) -- light grey _G.DC4 = string.char(20) -- light grey
_G.DLE = string.char(16) -- default error colour _G.DLE = string.char(16) -- default error colour
_G.runscript = function(s, src, ...) _G.runscript = function(s, src, ...)
if s:byte(1) == 27 then error("Bytecode execution is prohibited.") end
local code, reason = load(s, src) local code, reason = load(s, src)
if code then if code then
@@ -971,6 +973,8 @@ _G.runscript = function(s, src, ...)
end end
end end
_G.__scanMode__ = "UNINIT" -- part of inputstream implementation _G.__scanMode__ = "UNINIT" -- part of inputstream implementation
_G.bell = function(patn) term.bell(patn or ".") end
_G.beep = _G.bell
local screenbufferdim = term.width() * term.height() local screenbufferdim = term.width() * term.height()
local screencolours = 4 local screencolours = 4
@@ -995,6 +999,9 @@ computer.freeMemory = function() return totalMemory() - getMemory() end
-- load libraries that coded in Lua -- load libraries that coded in Lua
require("ROMLIB") require("ROMLIB")
-- POST passed, initialise beeper
beep "."
-- load bios, if any -- load bios, if any
if fs.exists(computer.bootloader) then shell.run(computer.bootloader) end if fs.exists(computer.bootloader) then shell.run(computer.bootloader) end
-- halt/run luaprompt upon the termination of bios. -- halt/run luaprompt upon the termination of bios.

View File

@@ -1,9 +1,11 @@
package net.torvald.terrarum.virtualcomputer.computer package net.torvald.terrarum.virtualcomputer.computer
import com.jme3.math.FastMath
import li.cil.repack.org.luaj.vm2.Globals import li.cil.repack.org.luaj.vm2.Globals
import li.cil.repack.org.luaj.vm2.LuaError import li.cil.repack.org.luaj.vm2.LuaError
import li.cil.repack.org.luaj.vm2.LuaTable import li.cil.repack.org.luaj.vm2.LuaTable
import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.LuaValue
import li.cil.repack.org.luaj.vm2.lib.TwoArgFunction
import li.cil.repack.org.luaj.vm2.lib.ZeroArgFunction import li.cil.repack.org.luaj.vm2.lib.ZeroArgFunction
import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform import li.cil.repack.org.luaj.vm2.lib.jse.JsePlatform
import net.torvald.terrarum.KVHashMap import net.torvald.terrarum.KVHashMap
@@ -12,9 +14,13 @@ import net.torvald.terrarum.virtualcomputer.luaapi.*
import net.torvald.terrarum.virtualcomputer.terminal.* import net.torvald.terrarum.virtualcomputer.terminal.*
import net.torvald.terrarum.virtualcomputer.worldobject.ComputerPartsCodex import net.torvald.terrarum.virtualcomputer.worldobject.ComputerPartsCodex
import net.torvald.terrarum.virtualcomputer.worldobject.FixtureComputerBase import net.torvald.terrarum.virtualcomputer.worldobject.FixtureComputerBase
import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL
import org.lwjgl.openal.AL10
import org.newdawn.slick.GameContainer import org.newdawn.slick.GameContainer
import org.newdawn.slick.Input import org.newdawn.slick.Input
import java.io.* import java.io.*
import java.nio.ByteBuffer
/** /**
* A part that makes "computer fixture" actually work * A part that makes "computer fixture" actually work
@@ -24,11 +30,12 @@ import java.io.*
* @param term : terminal that is connected to the computer fixtures, null if not connected any. * @param term : terminal that is connected to the computer fixtures, null if not connected any.
* Created by minjaesong on 16-09-10. * Created by minjaesong on 16-09-10.
*/ */
class BaseTerrarumComputer(val term: Teletype? = null) { class BaseTerrarumComputer() {
val DEBUG_UNLIMITED_MEM = false val DEBUG_UNLIMITED_MEM = false
val luaJ_globals: Globals = JsePlatform.debugGlobals() lateinit var luaJ_globals: Globals
private set
var termOut: PrintStream? = null var termOut: PrintStream? = null
private set private set
@@ -59,6 +66,9 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
lateinit var input: Input lateinit var input: Input
private set private set
lateinit var term: Teletype
private set
init { init {
computerValue["memslot0"] = 4864 // -1 indicates mem slot is empty computerValue["memslot0"] = 4864 // -1 indicates mem slot is empty
computerValue["memslot1"] = -1 // put index of item here computerValue["memslot1"] = -1 // put index of item here
@@ -83,11 +93,16 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
// boot device // boot device
computerValue["boot"] = computerValue.getAsString("hda")!! computerValue["boot"] = computerValue.getAsString("hda")!!
}
if (term != null) initSandbox(term) fun attachTerminal(term: Teletype) {
this.term = term
initSandbox(term)
} }
fun initSandbox(term: Teletype) { fun initSandbox(term: Teletype) {
luaJ_globals = JsePlatform.debugGlobals()
termOut = TerminalPrintStream(term) termOut = TerminalPrintStream(term)
termErr = TerminalPrintStream(term) termErr = TerminalPrintStream(term)
termIn = TerminalInputStream(term) termIn = TerminalInputStream(term)
@@ -200,6 +215,7 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
else else
throw IllegalArgumentException("Unsupported mode: $mode") throw IllegalArgumentException("Unsupported mode: $mode")
chunk.call() chunk.call()
} }
catch (e: LuaError) { catch (e: LuaError) {
@@ -216,4 +232,119 @@ class BaseTerrarumComputer(val term: Teletype? = null) {
return LuaValue.valueOf(computer.memSize) return LuaValue.valueOf(computer.memSize)
} }
} }
class EmitTone(val computer: BaseTerrarumComputer) : TwoArgFunction() {
override fun call(millisec: LuaValue, freq: LuaValue): LuaValue {
computer.playTone(millisec.toint(), freq.tofloat())
return LuaValue.NONE
}
}
///////////////////
// BEEPER DRIVER //
///////////////////
private val sampleRate = 22050
private var beepSource: Int? = null
private var beepBuffer: Int? = null
var audioData: ByteBuffer? = null
/**
* @param duration : milliseconds
*/
private fun makeAudioData(duration: Int, freq: Float): ByteBuffer {
val audioData = BufferUtils.createByteBuffer(duration.times(sampleRate).div(1000))
val realDuration = duration * sampleRate / 1000
val chopSize = freq * 2f / sampleRate
val amp = Math.max(4600f / freq, 1f)
val nHarmonics = 2
val transitionThre = 2300f
if (freq == 0f) {
for (x in 0..realDuration - 1) {
audioData.put(0x00.toByte())
}
}
else if (freq < transitionThre) { // chopper generator (for low freq)
for (x in 0..realDuration - 1) {
var sine: Float = amp * FastMath.cos(FastMath.PI * x * chopSize)
if (sine > 1f) sine = 1f
else if (sine < -1f) sine = -1f
audioData.put(
(0.5f + 0.5f * sine).times(0xFF).toByte()
)
}
}
else { // harmonics generator (for high freq)
for (x in 0..realDuration - 1) {
var sine: Float = 0f
for (k in 0..nHarmonics) { // mix only odd harmonics to make squarewave
sine += (1f / (2*k + 1)) *
FastMath.sin((2 * k + 1) * FastMath.PI * x * chopSize)
}
audioData.put(
(0.5f + 0.5f * sine).times(0xFF).toByte()
)
}
}
audioData.rewind()
return audioData
}
internal fun playTone(leninmilli: Int, freq: Float) {
audioData = makeAudioData(leninmilli, freq)
if (!AL.isCreated()) AL.create()
// Clear error stack.
AL10.alGetError()
beepBuffer = AL10.alGenBuffers()
checkALError()
try {
AL10.alBufferData(beepBuffer!!, AL10.AL_FORMAT_MONO8, audioData, sampleRate)
checkALError()
beepSource = AL10.alGenSources()
checkALError()
try {
AL10.alSourceQueueBuffers(beepSource!!, beepBuffer!!)
checkALError()
AL10.alSource3f(beepSource!!, AL10.AL_POSITION, 0f, 0f, 1f)
AL10.alSourcef(beepSource!!, AL10.AL_REFERENCE_DISTANCE, 1f)
AL10.alSourcef(beepSource!!, AL10.AL_MAX_DISTANCE, 1f)
AL10.alSourcef(beepSource!!, AL10.AL_GAIN, 0.3f)
checkALError()
AL10.alSourcePlay(beepSource!!)
checkALError()
}
catch (e: ALException) {
AL10.alDeleteSources(beepSource!!)
}
}
catch (e: ALException) {
if (beepSource != null) AL10.alDeleteSources(beepSource!!)
}
}
// Custom implementation of Util.checkALError() that uses our custom exception.
private fun checkALError() {
val errorCode = AL10.alGetError()
if (errorCode != AL10.AL_NO_ERROR) {
throw ALException(errorCode)
}
}
} }

View File

@@ -22,11 +22,11 @@ internal class HostAccessProvider(globals: Globals, computer: BaseTerrarumComput
globals["native"]["println"] = PrintLn() globals["native"]["println"] = PrintLn()
globals["native"]["isHalted"] = IsHalted(computer) globals["native"]["isHalted"] = IsHalted(computer)
globals["native"]["closeInputString"] = NativeCloseInputString(computer.term!!) globals["native"]["closeInputString"] = NativeCloseInputString(computer.term)
globals["native"]["closeInputKey"] = NativeCloseInputKey(computer.term!!) globals["native"]["closeInputKey"] = NativeCloseInputKey(computer.term)
globals["native"]["openInput"] = NativeOpenInput(computer.term!!) globals["native"]["openInput"] = NativeOpenInput(computer.term)
globals["native"]["getLastStreamInput"] = NativeGetLastStreamInput(computer.term!!) globals["native"]["getLastStreamInput"] = NativeGetLastStreamInput(computer.term)
globals["native"]["getLastKeyPress"] = NativeGetLastKeyPress(computer.term!!) globals["native"]["getLastKeyPress"] = NativeGetLastKeyPress(computer.term)
// while lua's dofile/require is fixiated to fs, this command allows // while lua's dofile/require is fixiated to fs, this command allows
// libraries in JAR to be loaded. // libraries in JAR to be loaded.

View File

@@ -24,6 +24,7 @@ internal class Term(globals: Globals, term: Teletype) {
globals["term"]["width"] = Term.GetWidth(term) globals["term"]["width"] = Term.GetWidth(term)
globals["term"]["scroll"] = Term.Scroll(term) globals["term"]["scroll"] = Term.Scroll(term)
globals["term"]["isTeletype"] = Term.IsTeletype(term) globals["term"]["isTeletype"] = Term.IsTeletype(term)
globals["term"]["bell"] = Term.Bell(term)
if (term is Terminal) { if (term is Terminal) {
globals["term"]["emitRaw"] = Term.EmitRaw(term) globals["term"]["emitRaw"] = Term.EmitRaw(term)
@@ -60,6 +61,13 @@ internal class Term(globals: Globals, term: Teletype) {
} }
} }
class Bell(val tty: Teletype) : OneArgFunction() {
override fun call(pattern: LuaValue): LuaValue {
tty.bell(pattern.checkjstring())
return LuaValue.NONE
}
}
class WriteString(val tty: Teletype) : LuaFunction() { class WriteString(val tty: Teletype) : LuaFunction() {
override fun call(p0: LuaValue): LuaValue { override fun call(p0: LuaValue): LuaValue {
if (tty is Terminal) if (tty is Terminal)

View File

@@ -5,12 +5,14 @@ import net.torvald.aa.ColouredFastFont
import net.torvald.terrarum.blendNormal import net.torvald.terrarum.blendNormal
import net.torvald.terrarum.blendMul import net.torvald.terrarum.blendMul
import net.torvald.terrarum.blendScreen import net.torvald.terrarum.blendScreen
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
import org.lwjgl.BufferUtils import org.lwjgl.BufferUtils
import org.lwjgl.openal.AL import org.lwjgl.openal.AL
import org.lwjgl.openal.AL10 import org.lwjgl.openal.AL10
import org.lwjgl.openal.AL11 import org.lwjgl.openal.AL11
import org.newdawn.slick.* import org.newdawn.slick.*
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.*
/** /**
* Default text terminal, four text colours (black, grey, lgrey, white). * Default text terminal, four text colours (black, grey, lgrey, white).
@@ -18,7 +20,7 @@ import java.nio.ByteBuffer
* Created by minjaesong on 16-09-07. * Created by minjaesong on 16-09-07.
*/ */
open class SimpleTextTerminal( open class SimpleTextTerminal(
phosphorColour: Color, override val width: Int, override val height: Int, phosphorColour: Color, override val width: Int, override val height: Int, private val host: BaseTerrarumComputer,
colour: Boolean = false, hires: Boolean = false colour: Boolean = false, hires: Boolean = false
) : Terminal { ) : Terminal {
/** /**
@@ -107,13 +109,31 @@ open class SimpleTextTerminal(
wrap() wrap()
// beep AL-related // start beep queue
if (beepSource != null && AL10.alGetSourcei(beepSource!!, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING) { if (beepQueue.size > 0 && beepCursor == -1) {
AL10.alDeleteSources(beepSource!!) beepCursor = 0
AL10.alDeleteBuffers(beepBuffer!!)
beepSource = null
beepBuffer == null
} }
// continue beep queue
if (beepCursor >= 0 && beepQueueLineExecTimer >= beepQueueGetLenOfPtn(beepCursor)) {
beepQueueLineExecTimer -= beepQueueGetLenOfPtn(beepCursor)
beepCursor += 1
beepQueueFired = false
}
// complete beep queue
if (beepCursor >= beepQueue.size) {
clearBeepQueue()
println("!! Beep queue clear")
}
// actually play queue
if (beepCursor >= 0 && beepQueue.size > 0 && !beepQueueFired) {
beep(beepQueue[beepCursor].first, beepQueue[beepCursor].second)
beepQueueFired = true
}
if (beepQueueFired) beepQueueLineExecTimer += delta
} }
private fun wrap() { private fun wrap() {
@@ -312,85 +332,48 @@ open class SimpleTextTerminal(
foreColour = foreDefault foreColour = foreDefault
} }
private val sampleRate = 22050
private val maxDuration = 10000 private val maxDuration = 10000
private val beepSamples = maxDuration.div(1000).times(sampleRate)
private var beepSource: Int? = null
private var beepBuffer: Int? = null
override fun beep(freq: Float, duration: Int) { // let's regard it as a tracker...
throw NotImplementedError("errenous OpenAL behaviour; *grunts*") private val beepQueue = ArrayList<Pair<Int, Float>>()
private var beepCursor = -1
private var beepQueueLineExecTimer = 0 // millisec
private var beepQueueFired = false
val audioData = BufferUtils.createByteBuffer(duration.times(sampleRate).div(1000)) /**
* @param duration: milliseconds
var chop = false * @param freg: Frequency (float)
val realDuration = Math.min(maxDuration, duration) */
override fun beep(duration: Int, freq: Float) {
for (i in 0..realDuration - 1) { println("!! Beep playing row $beepCursor, d ${Math.min(duration, maxDuration)} f $freq")
if (i.mod(freq) < 1.0) chop = !chop host.playTone(Math.min(duration, maxDuration), freq)
audioData.put(if (chop) 0xFF.toByte() else 0x00.toByte())
}
audioData.rewind()
// Clear error stack.
AL10.alGetError()
beepBuffer = AL10.alGenBuffers()
checkALError()
try {
AL10.alBufferData(beepBuffer!!, AL10.AL_FORMAT_MONO8, audioData, sampleRate)
checkALError()
beepSource = AL10.alGenSources()
checkALError()
try {
AL10.alSourceQueueBuffers(beepSource!!, beepBuffer!!)
checkALError()
AL10.alSource3f(beepSource!!, AL10.AL_POSITION, 0f, 0f, 1f)
AL10.alSourcef(beepSource!!, AL10.AL_REFERENCE_DISTANCE, 1f)
AL10.alSourcef(beepSource!!, AL10.AL_MAX_DISTANCE, 1f)
AL10.alSourcef(beepSource!!, AL10.AL_GAIN, 0.3f)
checkALError()
AL10.alSourcePlay(beepSource!!)
checkALError()
}
catch (e: ALException) {
AL10.alDeleteSources(beepSource!!)
}
}
catch (e: ALException) {
if (beepSource != null) AL10.alDeleteSources(beepSource!!)
}
/*def checkFinished = AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING && {
AL10.alDeleteSources(source)
AL10.alDeleteBuffers(buffer)
true
}*/
}
// Custom implementation of Util.checkALError() that uses our custom exception.
private fun checkALError() {
val errorCode = AL10.alGetError()
if (errorCode != AL10.AL_NO_ERROR) {
throw ALException(errorCode)
}
} }
/** for "beep code" on modern BIOS. Pattern: - . */ /** for "beep code" on modern BIOS. Pattern: - . */
override fun bell(pattern: String) { override fun bell(pattern: String) {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. clearBeepQueue()
for (c in pattern) {
when (c) {
'.' -> { enqueueBeep(80, 1000f); enqueueBeep(80, 0f) }
'-' -> { enqueueBeep(250, 1000f); enqueueBeep(80, 0f) }
' ' -> { enqueueBeep(250, 0f) }
else -> throw IllegalArgumentException("Unacceptable pattern: $c (from '$pattern')")
}
}
} }
fun clearBeepQueue() {
beepQueue.clear()
beepCursor = -1
beepQueueLineExecTimer = 0
}
fun enqueueBeep(duration: Int, freq: Float) {
beepQueue.add(Pair(Math.min(duration, maxDuration), freq))
}
fun beepQueueGetLenOfPtn(ptnIndex: Int) = beepQueue[ptnIndex].first
override var lastInputByte: Int = -1 override var lastInputByte: Int = -1
var sb: StringBuilder = StringBuilder() var sb: StringBuilder = StringBuilder()
private var inputOpen = false private var inputOpen = false

View File

@@ -58,11 +58,16 @@ interface Terminal : Teletype {
fun setColour(back: Int, fore: Int) fun setColour(back: Int, fore: Int)
fun resetColour() fun resetColour()
/** /**
* @param freg: Frequency (float)
* @param duration: milliseconds * @param duration: milliseconds
* @param freg: Frequency (float)
*/
fun beep(duration: Int = 80, freq: Float = 1000f)
/**
* Pattern: - .
* . 80 ms
* - 200 ms
* ( ) 80 ms
*/ */
fun beep(freq: Float = 1000f, duration: Int = 200)
/** for "beep code" on modern BIOS. Pattern: - . */
override fun bell(pattern: String) override fun bell(pattern: String)
/** Requires keyPressed() event to be processed. /** Requires keyPressed() event to be processed.
* *

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.virtualcomputer.worldobject
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameactors.FixtureBase import net.torvald.terrarum.gameactors.FixtureBase
import net.torvald.terrarum.virtualcomputer.computer.BaseTerrarumComputer
import net.torvald.terrarum.virtualcomputer.terminal.SimpleTextTerminal import net.torvald.terrarum.virtualcomputer.terminal.SimpleTextTerminal
import net.torvald.terrarum.virtualcomputer.terminal.Terminal import net.torvald.terrarum.virtualcomputer.terminal.Terminal
import net.torvald.terrarum.virtualcomputer.worldobject.ui.UITextTerminal import net.torvald.terrarum.virtualcomputer.worldobject.ui.UITextTerminal
@@ -13,10 +14,13 @@ import java.util.*
*/ */
class FixtureBasicTerminal(phosphor: Color) : FixtureBase() { class FixtureBasicTerminal(phosphor: Color) : FixtureBase() {
val vt: Terminal = SimpleTextTerminal(phosphor, 80, 25) val computer = BaseTerrarumComputer()
val vt: Terminal = SimpleTextTerminal(phosphor, 80, 25, computer)
val ui = UITextTerminal(vt) val ui = UITextTerminal(vt)
init { init {
computer.attachTerminal(vt)
collisionFlag = COLLISION_PLATFORM collisionFlag = COLLISION_PLATFORM
actorValue[AVKey.UUID] = UUID.randomUUID().toString() actorValue[AVKey.UUID] = UUID.randomUUID().toString()

View File

@@ -33,7 +33,8 @@ open class FixtureComputerBase() : FixtureBase() {
fun attachTerminal(uuid: String) { fun attachTerminal(uuid: String) {
val fetchedTerminal = getTerminalByUUID(uuid) val fetchedTerminal = getTerminalByUUID(uuid)
computerInside = BaseTerrarumComputer(fetchedTerminal) computerInside = BaseTerrarumComputer()
computerInside!!.attachTerminal(fetchedTerminal!!)
actorValue["computerid"] = computerInside!!.UUID actorValue["computerid"] = computerInside!!.UUID
} }

View File

@@ -61,9 +61,9 @@
°3C°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±Ý ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ Þ±±±±±± °3C°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±Ý ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ Þ±±±±±±
RecPlay °3D°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±ÝZÞÝXÞÝCÞÝVÞÝBÞÝNÞÝMÞÝ,ÞÝ.ÞÝ/Þ±Oct±± RecPlay °3D°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±ÝZÞÝXÞÝCÞÝVÞÝBÞÝNÞÝMÞÝ,ÞÝ.ÞÝ/Þ±Oct±±
Trcks 4 °3E°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±Ý ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ Þ± -1±± Trcks 4 °3E°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±Ý ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ ÞÝ Þ± -1±±
Tick 08 °3F°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±± Tick 00 °3F°úúú úúú°úúú úúú°úúú úúú°úúú úúú° ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
RecPlay RecPlay RecPlay RecPlay
Trcks 4 Trcks 4  Editable Trcks 4 Trcks 4  Editable
Tick 08 Tick 08  Fxx eff Tick 00 Tick 00  Current play tick
SAUCE00 20160924~dEIBM VGA50 SAUCE00 20160924u~dEIBM VGA50

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB