Compare commits

..
1 Commits
Author SHA1 Message Date
kichikuou 64edcfda29 Support loading system3.5 v1 save files 2023-03-18 09:51:58 +09:00
32 changed files with 747 additions and 319 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ name: Emscripten Build
on: [push, pull_request]
env:
EM_VERSION: 3.1.34
EM_VERSION: 3.1.27
EM_CACHE_FOLDER: 'emsdk-cache'
jobs:
+5 -1
View File
@@ -123,10 +123,14 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
list(APPEND SRC_CDROM cdrom.emscripten.c)
list(APPEND SUMMARY_CDROM "Emscripten")
set(ENABLE_CDROM_EMSCRIPTEN 1)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Android")
list(APPEND SRC_CDROM cdrom.android.c)
list(APPEND SUMMARY_CDROM "Android")
set(ENABLE_CDROM_ANDROID 1)
else()
list(APPEND SRC_CDROM cdrom.empty.c)
endif()
if (SDL2MIXER_FOUND)
if (SDL2MIXER_FOUND AND NOT ANDROID)
list(APPEND SRC_CDROM cdrom.mp3.c)
list(APPEND SUMMARY_CDROM "SDL_mixer (wav|mp3|ogg...)")
set(ENABLE_CDROM_MP3 1)
+2 -3
View File
@@ -16,8 +16,8 @@ android {
}
minSdkVersion 19
targetSdkVersion 31
versionCode 14
versionName "2.9.0" + gitRevision()
versionCode 13
versionName "2.8.0" + gitRevision()
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static"
@@ -82,7 +82,6 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
}
static String gitRevision() {
@@ -37,20 +37,24 @@ class GameActivity : SDLActivity() {
}
private lateinit var gameRoot: File
private lateinit var cdda: CddaPlayer
private val midi = MidiPlayer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gameRoot = File(intent.getStringExtra(EXTRA_GAME_ROOT)!!)
cdda = CddaPlayer(File(gameRoot, Launcher.PLAYLIST_FILE))
}
override fun onStop() {
super.onStop()
cdda.onActivityStop()
midi.onActivityStop()
}
override fun onResume() {
super.onResume()
cdda.onActivityResume()
midi.onActivityResume()
}
@@ -59,9 +63,7 @@ class GameActivity : SDLActivity() {
}
override fun getArguments(): Array<String> {
return arrayOf(
"-gamedir", intent.getStringExtra(EXTRA_GAME_ROOT)!!,
"-devcd", Launcher.PLAYLIST_FILE)
return arrayOf("-gamedir", intent.getStringExtra(EXTRA_GAME_ROOT)!!)
}
override fun setTitle(title: CharSequence?) {
@@ -118,6 +120,9 @@ class GameActivity : SDLActivity() {
}
// The functions below are called in the SDL thread by JNI.
@Suppress("unused") fun cddaStart(track: Int, loop: Boolean) = cdda.start(track, loop)
@Suppress("unused") fun cddaStop() = cdda.stop()
@Suppress("unused") fun cddaCurrentPosition() = cdda.currentPosition()
@Suppress("unused") fun midiStart(path: String, loop: Boolean) = midi.start(path, loop)
@Suppress("unused") fun midiStop() = midi.stop()
@Suppress("unused") fun midiCurrentPosition() = midi.currentPosition()
@@ -151,6 +156,69 @@ class GameActivity : SDLActivity() {
}
}
private class CddaPlayer(private val playlistPath: File) {
private val playlist =
try {
playlistPath.readLines()
} catch (e: IOException) {
Log.e("loadPlaylist", "Cannot load $playlistPath", e)
emptyList()
}
private var currentTrack = 0
private val player = MediaPlayer()
private var playerPaused = false
fun start(track: Int, loop: Boolean) {
val f = playlist.elementAtOrNull(track - 1)
if (f.isNullOrEmpty()) {
Log.w("cddaStart", "No playlist entry for track $track")
return
}
Log.v("cddaStart", "$f Loop:$loop")
try {
player.apply {
reset()
setDataSource(File(playlistPath.parent, f).path)
isLooping = loop
prepare()
start()
}
currentTrack = track
} catch (e: IOException) {
Log.e("cddaStart", "Cannot play $f", e)
player.reset()
}
}
fun stop() {
if (currentTrack > 0 && player.isPlaying) {
player.stop()
currentTrack = 0
}
}
fun currentPosition(): Int {
if (currentTrack == 0)
return 0
val frames = player.currentPosition * 75 / 1000
return currentTrack or (frames shl 8)
}
fun onActivityStop() {
if (currentTrack > 0 && player.isPlaying) {
player.pause()
playerPaused = true
}
}
fun onActivityResume() {
if (playerPaused) {
player.start()
playerPaused = false
}
}
}
private class MidiPlayer {
private val player = MediaPlayer()
private var playing = false
@@ -17,13 +17,11 @@
*/
package io.github.kichikuou.xsystem35
import android.annotation.SuppressLint
import android.os.Build
import android.os.Handler
import android.os.Message
import android.util.Log
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.*
import java.lang.StringBuilder
import java.nio.charset.Charset
@@ -42,6 +40,9 @@ interface LauncherObserver {
}
private const val SAVE_DIR = "save"
private const val PROGRESS = 0
private const val SUCCESS = 1
private const val FAILURE = 2
class Launcher private constructor(private val rootDir: File) {
companion object {
@@ -73,28 +74,31 @@ class Launcher private constructor(private val rootDir: File) {
updateGameList()
}
@OptIn(DelicateCoroutinesApi::class)
fun install(input: InputStream, archiveName: String?) {
val dir = createDirForGame()
isInstalling = true
GlobalScope.launch(Dispatchers.Main) {
try {
withContext(Dispatchers.IO) {
extractFiles(input, dir) { msg ->
GlobalScope.launch(Dispatchers.Main) {
observer?.onInstallProgress(msg)
}
@SuppressLint("HandlerLeak")
val handler = object : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
PROGRESS -> {
observer?.onInstallProgress(msg.obj as String)
}
SUCCESS -> {
isInstalling = false
observer?.onInstallSuccess(msg.obj as File, archiveName)
}
FAILURE -> {
isInstalling = false
observer?.onInstallFailure(msg.obj as Int)
}
}
observer?.onInstallSuccess(dir, archiveName)
} catch (e: InstallFailureException) {
observer?.onInstallFailure(e.msgId)
} catch (e: Exception) {
Log.e("launcher", "Failed to extract ZIP", e)
observer?.onInstallFailure(R.string.zip_extraction_error)
}
isInstalling = false
}
val t = Thread {
extractFiles(input, dir, handler)
}
t.start()
isInstalling = true
}
fun uninstall(id: Int) {
@@ -179,26 +183,32 @@ class Launcher private constructor(private val rootDir: File) {
}
}
private fun extractFiles(input: InputStream, outDir: File, progressCallback: (String) -> Unit) {
val configWriter = GameConfigWriter()
val hadDecodeError = forEachZipEntry(input) { zipEntry, zip ->
Log.i("extractFiles", zipEntry.name)
val path = File(outDir, zipEntry.name)
if (zipEntry.isDirectory)
return@forEachZipEntry
path.parentFile?.mkdirs()
progressCallback(zipEntry.name)
FileOutputStream(path).buffered().use {
zip.copyTo(it)
private fun extractFiles(input: InputStream, outDir: File, handler: Handler) {
try {
val configWriter = GameConfigWriter()
val hadDecodeError = forEachZipEntry(input) { zipEntry, zip ->
Log.i("extractFiles", zipEntry.name)
val path = File(outDir, zipEntry.name)
if (zipEntry.isDirectory)
return@forEachZipEntry
path.parentFile?.mkdirs()
handler.sendMessage(handler.obtainMessage(PROGRESS, zipEntry.name))
FileOutputStream(path).buffered().use {
zip.copyTo(it)
}
configWriter.maybeAdd(zipEntry.name)
}
configWriter.maybeAdd(zipEntry.name)
if (!configWriter.readyToWrite()) {
val msgId = if (hadDecodeError) R.string.unsupported_zip else R.string.cannot_find_ald
handler.sendMessage(handler.obtainMessage(FAILURE, msgId))
return
}
configWriter.write(outDir)
handler.sendMessage(handler.obtainMessage(SUCCESS, outDir))
} catch (e: IOException) {
Log.e("launcher", "Failed to extract ZIP", e)
handler.sendMessage(handler.obtainMessage(FAILURE, R.string.zip_extraction_error))
}
if (!configWriter.readyToWrite()) {
if (hadDecodeError)
throw InstallFailureException(R.string.unsupported_zip)
throw InstallFailureException(R.string.cannot_find_ald)
}
configWriter.write(outDir)
}
// Xsystem35-sdl2 <=2.2.0 had a bug where playlist had an extra empty line at
@@ -209,14 +219,12 @@ class Launcher private constructor(private val rootDir: File) {
if (!oldPlaylist.exists())
return
var tracks = oldPlaylist.readLines()
if (tracks.isNotEmpty())
if (!tracks.isEmpty())
tracks = tracks.subList(1, tracks.size)
File(dir, PLAYLIST_FILE).writeText(tracks.joinToString("\n"))
oldPlaylist.delete()
}
class InstallFailureException(val msgId: Int) : Exception()
// A helper class which generates xsystem35.gr and playlist.txt in the game root directory.
private class GameConfigWriter {
private val grb = StringBuilder()
@@ -27,7 +27,6 @@ import android.view.Menu
import android.view.MenuItem
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.TextView
import android.widget.Toast
import java.io.*
@@ -35,11 +34,10 @@ private const val CONTENT_TYPE_ZIP = "application/zip"
private const val INSTALL_REQUEST = 1
private const val SAVEDATA_EXPORT_REQUEST = 2
private const val SAVEDATA_IMPORT_REQUEST = 3
private const val STATE_PROGRESS_TEXT = "progressText"
class LauncherActivity : Activity(), LauncherObserver {
private lateinit var launcher: Launcher
private var progressDialog: Dialog? = null
private var progressDialog: ProgressDialogFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -48,7 +46,7 @@ class LauncherActivity : Activity(), LauncherObserver {
launcher = Launcher.getInstance(filesDir)
launcher.observer = this
if (launcher.isInstalling) {
showProgressDialog(savedInstanceState)
showProgressDialog()
}
onGameListChange()
@@ -63,17 +61,9 @@ class LauncherActivity : Activity(), LauncherObserver {
override fun onDestroy() {
launcher.observer = null
dismissProgressDialog()
super.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) {
progressDialog?.let {
outState.putCharSequence(STATE_PROGRESS_TEXT, it.findViewById<TextView>(R.id.text).text)
}
super.onSaveInstanceState(outState)
}
private fun onListItemClick(position: Int) {
if (position < launcher.games.size) {
startGame(launcher.games[position].path, null)
@@ -163,7 +153,7 @@ class LauncherActivity : Activity(), LauncherObserver {
}
override fun onInstallProgress(path: String) {
progressDialog?.findViewById<TextView>(R.id.text)?.text = getString(R.string.install_progress, path)
progressDialog?.setProgress(getString(R.string.install_progress, path))
}
override fun onInstallSuccess(path: File, archiveName: String?) {
@@ -188,17 +178,9 @@ class LauncherActivity : Activity(), LauncherObserver {
launcher.uninstall(id)
}
private fun showProgressDialog(savedInstanceState: Bundle? = null) {
progressDialog = Dialog(this)
progressDialog!!.apply {
setTitle(R.string.install_dialog_title)
setCancelable(false)
setContentView(R.layout.progress_dialog)
savedInstanceState?.let {
findViewById<TextView>(R.id.text)?.text = it.getCharSequence(STATE_PROGRESS_TEXT)
}
show()
}
private fun showProgressDialog() {
progressDialog = ProgressDialogFragment()
progressDialog!!.show(fragmentManager, "progress_dialog")
}
private fun dismissProgressDialog() {
@@ -227,3 +209,18 @@ class LauncherActivity : Activity(), LauncherObserver {
return null
}
}
@Suppress("DEPRECATION") // for ProgressDialog
class ProgressDialogFragment : DialogFragment() {
private lateinit var dialog: ProgressDialog
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
dialog = ProgressDialog(activity)
return dialog.apply {
setTitle(R.string.install_dialog_title)
setCancelable(true)
}
}
fun setProgress(msg: String) {
dialog.setMessage(msg)
}
}
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:padding="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ProgressBar
android:layout_marginEnd="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="16sp"/>
</LinearLayout>
+1 -1
View File
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.7.21'
ext.kotlin_version = '1.6.20'
repositories {
mavenCentral()
google()
+1 -1
View File
@@ -1,5 +1,5 @@
#cmakedefine PACKAGE "@PACKAGE@"
#define VERSION "2.9.0"
#define VERSION "2.8.0"
#cmakedefine CMAKE_SYSTEM_NAME "@CMAKE_SYSTEM_NAME@"
+5 -5
View File
@@ -33,7 +33,7 @@ static void cb_waitkey_simple(agsevent_t *e) {
switch (e->type) {
case AGSEVENT_BUTTON_RELEASE:
case AGSEVENT_KEY_RELEASE:
night.waitkey = e->code;
night.waitkey = e->d3;
break;
}
}
@@ -59,15 +59,15 @@ void ntev_callback(agsevent_t *e) {
return;
}
if (e->type == AGSEVENT_KEY_PRESS && e->code == KEY_CTRL) {
if (e->type == AGSEVENT_KEY_PRESS && e->d3 == KEY_CTRL) {
night.waitskiplv = 2;
night.waitkey = e->code;
night.waitkey = e->d3;
return;
}
if (e->type == AGSEVENT_KEY_RELEASE && e->code == KEY_CTRL) {
if (e->type == AGSEVENT_KEY_RELEASE && e->d3 == KEY_CTRL) {
night.waitskiplv = 0;
night.waitkey = e->code;
night.waitkey = e->d3;
return;
}
+9 -3
View File
@@ -392,11 +392,13 @@ static void is_in_icon() {
static void cb_keyrelease(agsevent_t *e) {
switch (e->code) {
int x = e->d1, y = e->d2;
switch (e->d3) {
case AGSEVENT_BUTTON_LEFT:
#if 0
if (is_in_icon()) {
do_icon(e->mousex, e->mousey);
do_icon(x, y);
break;
}
#endif
@@ -406,15 +408,19 @@ static void cb_keyrelease(agsevent_t *e) {
// unhide();
break;
}
night.waitkey = e->code;
night.waitkey = e->d3;
break;
}
}
static void cb_mousemove(agsevent_t *e) {
int x = e->d1, y = e->d2;
// 音声mute/メッセージスキップ/メッセージ枠消去の領域に
// マウスが移動したら、その部分のアイコンを変化させる
}
// メッセージ表示時に、キー入力を促すアニメーションの設定
+12 -12
View File
@@ -213,7 +213,7 @@ static void cb_waitkey_simple(agsevent_t *e) {
switch (e->type) {
case AGSEVENT_KEY_PRESS:
if (e->code == KEY_Z) {
if (e->d3 == KEY_Z) {
cur = sdl_getTicks();
if (!sact.zhiding) {
slist_foreach(sact.sp_zhide, cb_defocused_zkey, &update);
@@ -242,7 +242,7 @@ static void cb_waitkey_simple(agsevent_t *e) {
// fall through
case AGSEVENT_KEY_RELEASE:
switch(e->code) {
switch(e->d3) {
case KEY_Z:
cur = sdl_getTicks();
if (500 < (cur - sact.zofftime) || !sact.zdooff) {
@@ -258,7 +258,7 @@ static void cb_waitkey_simple(agsevent_t *e) {
sact.waittype = KEYWAIT_BACKLOG;
break;
default:
sact.waitkey = e->code;
sact.waitkey = e->d3;
break;
}
}
@@ -292,7 +292,7 @@ void cb_waitkey_sprite(agsevent_t *e) {
// 右クリックキャンセル
// drag中でない時のみ、キャンセルを受け付ける
if (e->type == AGSEVENT_BUTTON_RELEASE &&
e->code == AGSEVENT_BUTTON_RIGHT) {
e->d3 == AGSEVENT_BUTTON_RIGHT) {
sact.waitkey = 0;
return;
}
@@ -311,7 +311,7 @@ void cb_waitkey_sprite(agsevent_t *e) {
// dragg中の sprite は無視する
if (sp == sact.draggedsp) continue;
if (focused_sp == NULL && sp_is_insprite(sp, e->mousex, e->mousey)) {
if (focused_sp == NULL && sp_is_insprite(sp, e->d1, e->d2)) {
/*
focusを得ている sprite
*/
@@ -388,7 +388,7 @@ static void cb_waitkey_selection(agsevent_t *e) {
static void cb_waitkey_backlog(agsevent_t *e) {
switch (e->type) {
case AGSEVENT_KEY_RELEASE:
switch (e->code) {
switch (e->d3) {
case KEY_ESC:
sblog_end();
sact.waittype = KEYWAIT_MESSAGE;
@@ -409,14 +409,14 @@ static void cb_waitkey_backlog(agsevent_t *e) {
break;
case AGSEVENT_BUTTON_RELEASE:
if (e->code == AGSEVENT_BUTTON_RIGHT) {
if (e->d3 == AGSEVENT_BUTTON_RIGHT) {
sblog_end();
sact.waittype = KEYWAIT_MESSAGE;
}
break;
case AGSEVENT_MOUSE_WHEEL:
if (e->code > 0)
if (e->d3 > 0)
sblog_pagenext();
else
sblog_pagepre();
@@ -434,15 +434,15 @@ void spev_callback(agsevent_t *e) {
}
if (sact.waittype != KEYWAIT_BACKLOG) {
if (e->type == AGSEVENT_KEY_PRESS && e->code == KEY_CTRL) {
if (e->type == AGSEVENT_KEY_PRESS && e->d3 == KEY_CTRL) {
sact.waitskiplv = 2;
sact.waitkey = e->code;
sact.waitkey = e->d3;
return;
}
if (e->type == AGSEVENT_KEY_RELEASE && e->code == KEY_CTRL) {
if (e->type == AGSEVENT_KEY_RELEASE && e->d3 == KEY_CTRL) {
sact.waitskiplv = 0;
sact.waitkey = e->code;
sact.waitkey = e->d3;
return;
}
}
+5 -5
View File
@@ -58,12 +58,12 @@ static int eventCB_GET(sprite_t *sp, agsevent_t *e) {
switch(e->type) {
case AGSEVENT_BUTTON_PRESS:
if (e->code != AGSEVENT_BUTTON_LEFT) break;
if (e->d3 != AGSEVENT_BUTTON_LEFT) break;
// drag開始時のマウスの位置記録
sp->u.get.dragging = TRUE;
sp->u.get.dragstart.x = e->mousex;
sp->u.get.dragstart.y = e->mousey;
sp->u.get.dragstart.x = e->d1;
sp->u.get.dragstart.y = e->d2;
if (sp->cg3) {
sp->curcg = sp->cg3;
@@ -104,8 +104,8 @@ static int eventCB_GET(sprite_t *sp, agsevent_t *e) {
// if (!sp->u.get.dragging) break;
// マウスの現在位置により新しい場所を計算
newx = sp->loc.x + (e->mousex - sp->u.get.dragstart.x);
newy = sp->loc.y + (e->mousey - sp->u.get.dragstart.y);
newx = sp->loc.x + (e->d1 - sp->u.get.dragstart.x);
newy = sp->loc.y + (e->d2 - sp->u.get.dragstart.y);
if (newx != sp->cur.x || newy != sp->cur.y) {
sp_updateme(sp);
sp->cur.x = newx;
+5 -5
View File
@@ -135,13 +135,13 @@ int sp_keywait(int *vOK, int *vRND, int *vD01, int *vD02, int *vD03, int timeout
{
// とりあえず、現在のマウス位置を送って、switch sprite の
// 状態を更新しておく
agsevent_t agse;
MyPoint p;
sys_getMouseInfo(&p, FALSE);
agsevent_t agse = {
.type = AGSEVENT_MOUSE_MOTION,
.mousex = p.x,
.mousey = p.y
};
agse.type = AGSEVENT_MOUSE_MOTION;
agse.d1 = p.x;
agse.d2 = p.y;
agse.d3 = 0;
spev_callback(&agse);
}
+1 -1
View File
@@ -40,7 +40,7 @@ static int eventCB_PUT(sprite_t *sp, agsevent_t *e) {
switch(e->type) {
case AGSEVENT_BUTTON_PRESS:
if (e->code != AGSEVENT_BUTTON_LEFT) return 0;
if (e->d3 != AGSEVENT_BUTTON_LEFT) return 0;
// ボタン押下時のスプライトがあれば、それを表示
if (sp->cg3) {
+3 -3
View File
@@ -77,7 +77,7 @@ static boolean sp_is_insprite2(sprite_t *sp, int x, int y, int margin) {
// マウスが移動したときの callback
static void cb_select_move(agsevent_t *e) {
int x = e->mousex, y = e->mousey;
int x = e->d1, y = e->d2;
sprite_t *sp = sact.sp[sact.sel.spno];
boolean newstate;
int newindex;
@@ -115,12 +115,12 @@ static void cb_select_move(agsevent_t *e) {
// ボタンがリリースされたときの callback
static void cb_select_release(agsevent_t *e) {
int x = e->mousex, y = e->mousey;
int x = e->d1, y = e->d2;
sprite_t *sp = sact.sp[sact.sel.spno];
boolean st;
int iy;
switch (e->code) {
switch (e->d3) {
case AGSEVENT_BUTTON_LEFT:
st = sp_is_insprite2(sp, x, y, sact.sel.frame_dot);
iy = (y - (sp->cur.y + sact.sel.frame_dot)) / (sact.sel.font_size + sact.sel.linespace);
+2 -2
View File
@@ -41,7 +41,7 @@ static int eventCB_switch(sprite_t *sp, agsevent_t *e) {
switch(e->type) {
case AGSEVENT_BUTTON_PRESS:
if (e->code != AGSEVENT_BUTTON_LEFT) return 0;
if (e->d3 != AGSEVENT_BUTTON_LEFT) return 0;
// ボタン押下時のスプライトがあれば、それを表示
if (sp->cg3) {
@@ -53,7 +53,7 @@ static int eventCB_switch(sprite_t *sp, agsevent_t *e) {
break;
case AGSEVENT_BUTTON_RELEASE:
if (e->code != AGSEVENT_BUTTON_LEFT) return 0;
if (e->d3 != AGSEVENT_BUTTON_LEFT) return 0;
// ここにくるときは forcusが当たっているときしかこないので、
// curcg は cg2 に戻せばよい
-2
View File
@@ -84,7 +84,6 @@ if (EMSCRIPTEN)
"SHELL:-s USE_ZLIB=1"
"SHELL:-s USE_SDL=2"
"SHELL:-s USE_SDL_TTF=2")
target_compile_options(src_lib PRIVATE ${LIBS})
target_compile_options(xsystem35 PRIVATE ${LIBS})
target_link_options(xsystem35 PRIVATE ${LIBS})
target_link_libraries(xsystem35 PRIVATE idbfs.js)
@@ -100,7 +99,6 @@ if (EMSCRIPTEN)
"SHELL:-s ASYNCIFY_ADD=commands2F60,nact_main,send_agsevent,cb_waitkey_sprite"
"SHELL:-s ALLOW_MEMORY_GROWTH=1"
"SHELL:-s NO_EXIT_RUNTIME=1"
"SHELL:-s EXPORTED_FUNCTIONS=_main,_malloc"
"SHELL:-s EXPORTED_RUNTIME_METHODS=getValue,addRunDependency,removeRunDependency")
elseif (ANDROID)
+15 -18
View File
@@ -78,27 +78,24 @@ typedef struct agsurface agsurface_t;
// for SDL_surface
#define PIXEL_AT(suf, x, y) ((suf)->pixels + (y) * (suf)->pitch + (x) * (suf)->format->BytesPerPixel)
typedef struct {
struct _agsevent {
int type;
int code;
int mousex, mousey;
} agsevent_t;
enum agsevent_type {
AGSEVENT_MOUSE_MOTION,
AGSEVENT_BUTTON_PRESS,
AGSEVENT_BUTTON_RELEASE,
AGSEVENT_KEY_PRESS,
AGSEVENT_KEY_RELEASE,
AGSEVENT_TIMER,
AGSEVENT_MOUSE_WHEEL,
int d1, d2, d3;
};
typedef struct _agsevent agsevent_t;
enum agsevent_button {
AGSEVENT_BUTTON_LEFT,
AGSEVENT_BUTTON_MID,
AGSEVENT_BUTTON_RIGHT,
};
#define AGSEVENT_MOUSE_MOTION 1
#define AGSEVENT_BUTTON_PRESS 2
#define AGSEVENT_BUTTON_RELEASE 3
#define AGSEVENT_KEY_PRESS 4
#define AGSEVENT_KEY_RELEASE 5
#define AGSEVENT_TIMER 6
#define AGSEVENT_MOUSE_WHEEL 7
#define AGSEVENT_BUTTON_LEFT 1
#define AGSEVENT_BUTTON_MID 2
#define AGSEVENT_BUTTON_RIGHT 3
enum mouse_warp_mode {
MOUSE_WARP_DISABLED,
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2020 <KichikuouChrome@gmail.com>
*
* This program 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <SDL.h>
#include <jni.h>
#include "system.h"
#include "portab.h"
#include "cdrom.h"
static int cdrom_init(char *);
static int cdrom_exit(void);
static int cdrom_reset(void);
static int cdrom_start(int, int);
static int cdrom_stop();
static int cdrom_getPlayingInfo(cd_time *);
#define cdrom cdrom_android
cdromdevice_t cdrom = {
cdrom_init,
cdrom_exit,
cdrom_reset,
cdrom_start,
cdrom_stop,
cdrom_getPlayingInfo,
NULL,
NULL
};
int cdrom_init(char *name) {
return OK;
}
int cdrom_reset(void) {
cdrom_stop();
return OK;
}
int cdrom_exit(void) {
cdrom_stop();
return OK;
}
int cdrom_start(int trk, int loop) {
JNIEnv *env = SDL_AndroidGetJNIEnv();
if ((*env)->PushLocalFrame(env, 16) < 0) {
WARNING("Failed to allocate JVM local references");
return NG;
}
jobject context = SDL_AndroidGetActivity();
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
"cddaStart", "(IZ)V");
(*env)->CallVoidMethod(env, context, mid, trk, loop == 0);
(*env)->PopLocalFrame(env, NULL);
return OK;
}
int cdrom_stop() {
JNIEnv *env = SDL_AndroidGetJNIEnv();
if ((*env)->PushLocalFrame(env, 16) < 0) {
WARNING("Failed to allocate JVM local references");
return NG;
}
jobject context = SDL_AndroidGetActivity();
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
"cddaStop", "()V");
(*env)->CallVoidMethod(env, context, mid);
(*env)->PopLocalFrame(env, NULL);
return OK;
}
int cdrom_getPlayingInfo (cd_time *info) {
JNIEnv *env = SDL_AndroidGetJNIEnv();
if ((*env)->PushLocalFrame(env, 16) < 0) {
WARNING("Failed to allocate JVM local references");
return NG;
}
jobject context = SDL_AndroidGetActivity();
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
"cddaCurrentPosition", "()I");
int t = (*env)->CallIntMethod(env, context, mid);
(*env)->PopLocalFrame(env, NULL);
info->t = t & 0xff;
FRAMES_TO_MSF(t >> 8, &info->m, &info->s, &info->f);
return OK;
}
+6 -2
View File
@@ -43,6 +43,10 @@ extern cdromdevice_t cdrom_bsd;
extern cdromdevice_t cdrom_emscripten;
#define NATIVE_CD_DEVICE &cdrom_emscripten
#elif defined(ENABLE_CDROM_ANDROID)
extern cdromdevice_t cdrom_android;
#define NATIVE_CD_DEVICE &cdrom_android
#else
extern cdromdevice_t cdrom_empty;
@@ -54,7 +58,7 @@ extern cdromdevice_t cdrom_mp3;
#endif
cdromdevice_t *cd_init(const char *dev) {
#if defined(ENABLE_CDROM_EMSCRIPTEN)
#if defined(ENABLE_CDROM_EMSCRIPTEN) || defined(ENABLE_CDROM_ANDROID)
return NATIVE_CD_DEVICE;
#else
struct stat st;
@@ -67,5 +71,5 @@ cdromdevice_t *cd_init(const char *dev) {
WARNING("no cdrom device available");
return NULL;
#endif
#endif // ENABLE_CDROM_EMSCRIPTEN
#endif // ENABLE_CDROM_EMSCRIPTEN || ENABLE_CDROM_ANDROID
}
+29 -21
View File
@@ -27,10 +27,39 @@
static int frame_of_getpos = -1;
static int cdrom_init(char *);
static int cdrom_exit(void);
static int cdrom_reset(void);
extern int cdrom_start(int, int);
extern int cdrom_stop();
static int cdrom_getPlayingInfo(cd_time *);
#define cdrom cdrom_emscripten
cdromdevice_t cdrom = {
cdrom_init,
cdrom_exit,
cdrom_reset,
cdrom_start,
cdrom_stop,
cdrom_getPlayingInfo,
NULL,
NULL
};
int cdrom_init(char *name) {
return OK;
}
int cdrom_exit(void) {
cdrom_stop();
return OK;
}
int cdrom_reset(void) {
cdrom_stop();
return OK;
}
EM_JS(int, cdrom_start, (int trk, int loop), {
xsystem35.cdPlayer.play(trk, loop == 0 ? 1 : 0);
return xsystem35.Status.OK;
@@ -41,16 +70,6 @@ EM_JS(int, cdrom_stop, (), {
return xsystem35.Status.OK;
});
int cdrom_exit(void) {
cdrom_stop();
return OK;
}
int cdrom_reset(void) {
cdrom_stop();
return OK;
}
int cdrom_getPlayingInfo(cd_time *info) {
if (nact->frame_count == frame_of_getpos)
nact->wait_vsync = TRUE;
@@ -63,14 +82,3 @@ int cdrom_getPlayingInfo(cd_time *info) {
FRAMES_TO_MSF(t >> 8, &info->m, &info->s, &info->f);
return OK;
}
cdromdevice_t cdrom_emscripten = {
cdrom_init,
cdrom_exit,
cdrom_reset,
cdrom_start,
cdrom_stop,
cdrom_getPlayingInfo,
NULL,
NULL
};
+4 -2
View File
@@ -345,8 +345,10 @@ void commandZZ0() {
sys_exit(sysVar[0]);
#endif
} else if (sw == 1) {
while (!nact->is_quit)
sys_keywait(1000, 0);
while (TRUE) {
usleep(1000*1000);
sys_getInputInfo();
}
}
}
+3 -3
View File
@@ -72,7 +72,7 @@ static void send_json(cJSON *json) {
printf("Content-Length: %zu\r\n\r\n%s", strlen(str), str);
fflush(stdout);
free(str);
cJSON_Delete(json);
cJSON_free(json);
}
static void emit_initialized_event(void) {
@@ -494,7 +494,7 @@ static boolean handle_request(cJSON *request) {
if (!cJSON_IsString(command)) {
fprintf(stderr, "protocol error: command is not a string\n");
// FIXME: return an error response
cJSON_Delete(resp);
cJSON_free(resp);
return continue_repl;
}
@@ -549,7 +549,7 @@ static boolean handle_message(char *msg) {
boolean continue_repl = true;
if (cJSON_IsString(type) && !strcmp(type->valuestring, "request"))
continue_repl = handle_request(json);
cJSON_Delete(json);
cJSON_free(json);
free(msg);
return continue_repl;
}
+38 -22
View File
@@ -24,15 +24,43 @@
#include "portab.h"
#include "midi.h"
static int midi_initilize(char *pname, int subdev);
static int midi_exit(void);
static int midi_reset(void);
static int midi_start(int no, int loop, char *data, int datalen);
static int midi_stop();
extern int midi_pause(void);
extern int midi_unpause(void);
static int midi_get_playing_info(midiplaystate *st);
static int midi_getflag(int mode, int index);
static int midi_setflag(int mode, int index, int val);
extern int midi_setvol(int vol);
extern int midi_getvol();
extern int midi_fadestart(int time, int volume, int stop);
extern boolean midi_fading();
#define midi midi_emscripten
mididevice_t midi = {
midi_initilize,
midi_exit,
midi_reset,
midi_start,
midi_stop,
midi_pause,
midi_unpause,
midi_get_playing_info,
midi_getflag,
midi_setflag,
midi_setvol,
midi_getvol,
midi_fadestart,
midi_fading
};
static int midi_initilize(char *pname, int subdev) {
return OK;
}
EM_JS(int, midi_stop, (void), {
xsystem35.midiPlayer.stop();
return xsystem35.Status.OK;
});
static int midi_exit(void) {
midi_stop();
return OK;
@@ -48,6 +76,11 @@ static int midi_start(int no, int loop, char *data, int datalen) {
return OK;
}
static int midi_stop() {
EM_ASM( xsystem35.midiPlayer.stop(); );
return OK;
}
EM_JS(int, midi_pause, (void), {
xsystem35.midiPlayer.pause();
return xsystem35.Status.OK;
@@ -94,20 +127,3 @@ EM_JS(int, midi_fadestart, (int time, int volume, int stop), {
EM_JS(boolean, midi_fading, (), {
return xsystem35.midiPlayer.isFading();
});
mididevice_t midi_emscripten = {
midi_initilize,
midi_exit,
midi_reset,
midi_start,
midi_stop,
midi_pause,
midi_unpause,
midi_get_playing_info,
midi_getflag,
midi_setflag,
midi_setvol,
midi_getvol,
midi_fadestart,
midi_fading
};
+212 -18
View File
@@ -23,6 +23,7 @@
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -44,6 +45,7 @@ typedef int emscripten_align1_int;
#include "message.h"
const char *save_signature[] = {
[SAVEFMT_SYS35] = "This is save data for System3.5 Win95 For NACT/ADV system (C) 1996 ALICE-SOFT",
[SAVEFMT_XSYS35] = "System3.5 SavaData(c)ALICE-SOFT",
[SAVEFMT_SYS36] = "System3.5 SaveData(c)ALICE-SOFT",
[SAVEFMT_SYS38] = "System3.8 SaveData(c)ALICE-SOFT",
@@ -113,11 +115,7 @@ typedef struct {
emscripten_align1_int rsv2;
} asd_varPageHdr;
#ifdef __EMSCRIPTEN__
static enum save_format save_format = SAVEFMT_XSYS35;
#else
static enum save_format save_format = SAVEFMT_SYS38;
#endif
#ifdef __EMSCRIPTEN__
EM_JS(void, scheduleSync, (), {
@@ -477,19 +475,83 @@ int save_copyAll(int dstno, int srcno) {
return status;
}
static int loadpartial_sys35(char *saveTop, int filesize, struct VarRef *vref, int cnt) {
if (filesize < 0xb0)
return SAVE_LOADERR;
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
if (vref->page == 0) {
// SysVar section
int section_offset = LittleEndian_getDW(saveTop, 0x90);
int section_length = LittleEndian_getDW(saveTop, 0x94);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int sysvar_count = LittleEndian_getDW(saveTop, section_offset);
if (sysvar_count > varPage[0].size)
return SAVE_LOADERR; // resizing system page is not supported in sys3.5
int offset = section_offset + 16 + vref->index * 2;
for (int i = 0; i < cnt; i++) {
var[i] = LittleEndian_getW(saveTop, offset);
offset += 2;
}
return SAVE_LOADOK;
}
// Arrays section
int section_offset = LittleEndian_getDW(saveTop, 0xa0);
int section_length = LittleEndian_getDW(saveTop, 0xa4);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int page_count = LittleEndian_getDW(saveTop, section_offset + 4);
int offset = section_offset + 16;
// TODO: section size check
for (int i = 0; i < page_count; i++) {
int page = LittleEndian_getDW(saveTop, offset); offset += 4;
int count = LittleEndian_getDW(saveTop, offset); offset += 4;
int content_offset = LittleEndian_getDW(saveTop, offset); offset += 4;
offset += 4;
if (page != vref->page)
continue;
// TODO page resize
// TODO: section size check
content_offset += vref->index * 2;
for (int j = 0; j < cnt; j++) {
var[j] = LittleEndian_getW(saveTop, content_offset + j * 2);
}
return SAVE_LOADOK;
}
return SAVE_LOADERR;
}
/* データの一部ロード */
int save_loadPartial(int no, struct VarRef *vref, int cnt) {
if (no >= SAVE_MAXNUMBER)
return SAVE_SAVEERR;
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
int status, filesize;
char *saveTop = loadGameData(no, &status, &filesize);
if (!saveTop)
return status;
if (filesize < 0x60)
goto errexit;
enum save_format format = (enum save_format)-1;
for (int i = 0; i < sizeof(save_signature) / sizeof(save_signature[0]); i++) {
if (!strcmp(save_signature[i], saveTop)) {
format = i;
break;
}
}
if (format < 0) {
WARNING("unrecognized save format");
goto errexit;
}
if (format == SAVEFMT_SYS35) {
int result = loadpartial_sys35(saveTop, filesize, vref, cnt);
free(saveTop);
return result;
}
if (filesize <= sizeof(asd_baseHdr))
goto errexit;
@@ -499,6 +561,8 @@ int save_loadPartial(int no, struct VarRef *vref, int cnt) {
goto errexit;
}
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
if (save_base->varSys[vref->page] == 0)
goto errexit;
@@ -519,9 +583,11 @@ int save_loadPartial(int no, struct VarRef *vref, int cnt) {
/* データの一部セーブ */
int save_savePartial(int no, struct VarRef *vref, int cnt) {
if (no >= SAVE_MAXNUMBER || !varPage[vref->page].saveflag)
if (no >= SAVE_MAXNUMBER) {
return SAVE_SAVEERR;
}
if (!varPage[vref->page].saveflag)
goto errexit;
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
@@ -553,6 +619,125 @@ int save_savePartial(int no, struct VarRef *vref, int cnt) {
return SAVE_SAVEERR;
}
static int loadall_sys35(char *saveTop, int filesize) {
if (filesize < 0xb0)
return SAVE_LOADERR;
int page = LittleEndian_getDW(saveTop, 0x80);
int addr = LittleEndian_getDW(saveTop, 0x84);
sl_jmpFar2(page - 1, addr);
// SysVar section
int section_offset = LittleEndian_getDW(saveTop, 0x90);
int section_length = LittleEndian_getDW(saveTop, 0x94);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int sysvar_count = LittleEndian_getDW(saveTop, section_offset);
if (sysvar_count > varPage[0].size)
return SAVE_LOADERR; // resizing system page is not supported in sys3.5
int offset = section_offset + 16;
for (int i = 0; i < sysvar_count; i++) {
varPage[0].value[i] = LittleEndian_getW(saveTop, offset);
offset += 2;
}
nact->sel.MsgFontSize = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.MsgFontSize = LittleEndian_getW(saveTop, offset); offset += 2;
cg_vspPB = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.MsgFontColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.MsgFontColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.WinFrameColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.WinBackgroundColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.WinFrameColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.WinBackgroundColor = LittleEndian_getW(saveTop, offset); offset += 2;
int left = section_offset + 16 + section_length - offset;
WARNING("0x%x bytes for window info", left);
if (left < 0)
return SAVE_LOADERR;
int win_info_count = left / 32;
if (win_info_count >= SELWINMAX)
return SAVE_LOADERR;
for (int i = 1; i <= win_info_count; i++) {
offset += 2;
nact->sel.wininfo[i].x = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.wininfo[i].y = LittleEndian_getW(saveTop, offset); offset += 2;
int ex = LittleEndian_getW(saveTop, offset); offset += 2;
int ey = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.wininfo[i].width = ex + 1 - nact->sel.wininfo[i].x;
nact->sel.wininfo[i].height = ey + 1 - nact->sel.wininfo[i].y;
offset += 6;
}
for (int i = 1; i <= win_info_count; i++) {
offset += 2;
nact->msg.wininfo[i].x = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.wininfo[i].y = LittleEndian_getW(saveTop, offset); offset += 2;
int ex = LittleEndian_getW(saveTop, offset); offset += 2;
int ey = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.wininfo[i].width = ex + 1 - nact->msg.wininfo[i].x;
nact->msg.wininfo[i].height = ey + 1 - nact->msg.wininfo[i].y;
offset += 6;
}
//assert(offset == section_offset + 16 + section_length);
// Strings section
section_offset = LittleEndian_getDW(saveTop, 0x98);
section_length = LittleEndian_getDW(saveTop, 0x9c);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int strvar_count = LittleEndian_getDW(saveTop, section_offset);
int strvar_length = LittleEndian_getDW(saveTop, section_offset + 4);
offset = section_offset + 16;
if (section_length != strvar_count * strvar_length)
return SAVE_LOADERR;
if (strvar_count > svar_maxindex())
strvar_count = svar_maxindex();
char *buf = malloc(strvar_length + 1);
buf[strvar_length] = '\0';
for (int i = 1; i <= strvar_count; i++) {
memcpy(buf, saveTop + offset, strvar_length);
svar_set(i, buf);
offset += strvar_length;
}
// Arrays section
section_offset = LittleEndian_getDW(saveTop, 0xa0);
section_length = LittleEndian_getDW(saveTop, 0xa4);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int array_total_size = LittleEndian_getDW(saveTop, section_offset);
int page_count = LittleEndian_getDW(saveTop, section_offset + 4);
offset = section_offset + 16;
// TODO: section size check
int total = 0;
for (int i = 0; i < page_count; i++) {
int page = LittleEndian_getDW(saveTop, offset); offset += 4;
int count = LittleEndian_getDW(saveTop, offset); offset += 4;
int content_offset = LittleEndian_getDW(saveTop, offset); offset += 4;
offset += 4;
total += count;
// TODO page resize
// TODO: section size check
for (int j = 0; j < count; j++) {
varPage[page].value[j] = LittleEndian_getW(saveTop, content_offset + j * 2);
}
}
if (total != array_total_size) {
WARNING("wrong array total size");
} else {
WARNING("correct array total size");
}
// Stack section
section_offset = LittleEndian_getDW(saveTop, 0xa8);
section_length = LittleEndian_getDW(saveTop, 0xac);
if (section_offset + section_length > filesize)
return SAVE_LOADERR;
sl_loadStack(SAVEFMT_SYS35, saveTop + section_offset, section_length);
return SAVE_LOADOK;
}
/* データのロード */
int save_loadAll(int no) {
@@ -563,17 +748,11 @@ int save_loadAll(int no) {
char *saveTop = loadGameData(no, &status, &filesize);
if (!saveTop)
return status;
if (filesize <= sizeof(asd_baseHdr))
if (filesize < 0x60)
goto errexit;
/* 各種データの反映 */
asd_baseHdr *save_base = (asd_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
WARNING("endian mismatch");
goto errexit;
}
enum save_format format = (enum save_format)-1;
for (int i = 0; i < sizeof(save_signature) / sizeof(save_signature[0]); i++) {
if (!strcmp(save_signature[i], save_base->ID)) {
if (!strcmp(save_signature[i], saveTop)) {
format = i;
break;
}
@@ -583,6 +762,21 @@ int save_loadAll(int no) {
goto errexit;
}
if (format == SAVEFMT_SYS35) {
int result = loadall_sys35(saveTop, filesize);
free(saveTop);
return result;
}
if (filesize <= sizeof(asd_baseHdr))
goto errexit;
/* 各種データの反映 */
asd_baseHdr *save_base = (asd_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
WARNING("endian mismatch");
goto errexit;
}
nact->sel.MsgFontSize = save_base->selMsgSize;
nact->sel.MsgFontColor = save_base->selMsgColor;
nact->sel.WinBackgroundColor = save_base->selBackColor;
+1
View File
@@ -38,6 +38,7 @@
enum save_format {
SAVEFMT_XSYS35,
SAVEFMT_SYS35, // System3.5 v1.x
SAVEFMT_SYS36, // System3.5 v2.x - System3.6
SAVEFMT_SYS38, // System3.8 - System3.9
};
+121 -36
View File
@@ -354,7 +354,7 @@ void sl_popVar(struct VarRef *vref, int cnt) {
}
}
enum save_stack_frame_type {
enum xsys35_stack_frame_type {
SAVE_NEARJMP = 1,
SAVE_FARJMP = 2,
SAVE_VARIABLE = 3,
@@ -367,15 +367,15 @@ enum txx_type {
TxxTEXTLOC = 3
};
struct save_stack_frame {
struct save_stack_frame *next;
struct xsys35_stack_frame {
struct xsys35_stack_frame *next;
int len;
int buf[];
};
struct save_stack_frame *push_save_stack_frame(int size, struct save_stack_frame *next) {
struct save_stack_frame *f =
malloc(sizeof(struct save_stack_frame) + sizeof(int) * size);
struct xsys35_stack_frame *push_xsys35_stack_frame(int size, struct xsys35_stack_frame *next) {
struct xsys35_stack_frame *f =
malloc(sizeof(struct xsys35_stack_frame) + sizeof(int) * size);
f->next = next;
f->len = size;
return f;
@@ -391,13 +391,13 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
}
// Serialize to the stack format used in old versions of xsystem35.
struct save_stack_frame *frame = NULL;
struct xsys35_stack_frame *frame = NULL;
uint8_t *sp = stack_top;
while (sp > stack_buf) {
switch (sp[-1]) {
case STACK_FARCALL:
sp -= 7;
frame = push_save_stack_frame(4, frame);
frame = push_xsys35_stack_frame(4, frame);
frame->buf[3] = SAVE_FARJMP;
frame->buf[2] = 2;
frame->buf[1] = LittleEndian_getW(sp, 0) - 1; // page
@@ -405,7 +405,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_NEARCALL:
sp -= 5;
frame = push_save_stack_frame(3, frame);
frame = push_xsys35_stack_frame(3, frame);
frame->buf[2] = SAVE_NEARJMP;
frame->buf[1] = 1;
frame->buf[0] = LittleEndian_getDW(sp, 0); // addr
@@ -422,7 +422,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
int base = frame->buf[frame->len - 4];
// Merge into existing SAVE_VARIABLE frame.
if (page == fpage && index == base - 1) {
frame = realloc(frame, sizeof(struct save_stack_frame) + sizeof(int) * (frame->len + 1));
frame = realloc(frame, sizeof(struct xsys35_stack_frame) + sizeof(int) * (frame->len + 1));
frame->len++;
frame->buf[frame->len - 1] = SAVE_VARIABLE;
frame->buf[frame->len - 2] = count + 1;
@@ -432,7 +432,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
}
}
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_VARIABLE;
frame->buf[3] = 3;
frame->buf[2] = page;
@@ -442,7 +442,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_TEXTCOLOR:
sp -= 3;
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTCOLOR;
@@ -451,7 +451,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_TEXTSIZE:
sp -= 6;
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTSIZE;
@@ -460,7 +460,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_TEXTLOC:
sp -= 9;
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTLOC;
@@ -472,15 +472,15 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
}
}
int total_len = 0;
for (struct save_stack_frame *f = frame; f; f = f->next)
for (struct xsys35_stack_frame *f = frame; f; f = f->next)
total_len += f->len;
int *buf = malloc(total_len * sizeof(int));
int *p = buf;
for (struct save_stack_frame *f = frame; f;) {
for (struct xsys35_stack_frame *f = frame; f;) {
memcpy(p, f->buf, f->len * sizeof(int));
p += f->len;
struct save_stack_frame *next = f->next;
struct xsys35_stack_frame *next = f->next;
free(f);
f = next;
}
@@ -510,36 +510,22 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
return (uint8_t *)buf;
}
void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
if (format != SAVEFMT_XSYS35) {
if (size > stack_size) {
while (size > stack_size)
stack_size *= 2;
free(stack_buf);
stack_buf = malloc(stack_size);
if (!stack_buf)
NOMEMERR();
}
memcpy(stack_buf, unaligned_data, size);
stack_top = stack_buf + size;
return;
}
static void load_stack_xsys35(uint8_t *unaligned_data, int size) {
// Deserialize from the stack format used in old versions of xsystem35.
int *data = malloc(size);
memcpy(data, unaligned_data, size);
struct save_stack_frame *frame = NULL;
struct xsys35_stack_frame *frame = NULL;
for (int *p = data + size / sizeof(int); p > data;) {
int len = p[-2] + 2;
p -= len;
frame = push_save_stack_frame(len, frame);
frame = push_xsys35_stack_frame(len, frame);
memcpy(frame->buf, p, len * sizeof(int));
}
free(data);
stack_top = stack_buf;
for (struct save_stack_frame *f = frame; f;) {
for (struct xsys35_stack_frame *f = frame; f;) {
switch (f->buf[f->len - 1]) {
case SAVE_NEARJMP:
if (f->len != 3)
@@ -601,12 +587,111 @@ void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
default:
SYSERROR("broken stack data");
}
struct save_stack_frame *next = f->next;
struct xsys35_stack_frame *next = f->next;
free(f);
f = next;
}
}
struct asdv1_stack_frame {
struct asdv1_stack_frame *next;
int len;
uint8_t buf[];
};
struct asdv1_stack_frame *push_asdv1_stack_frame(int size, struct asdv1_stack_frame *next) {
struct asdv1_stack_frame *f =
malloc(sizeof(struct asdv1_stack_frame) + size);
f->next = next;
f->len = size;
return f;
}
static void load_stack_sys35(uint8_t *data, int size) {
struct asdv1_stack_frame *frame = NULL;
for (uint8_t *p = data + size; p > data;) {
int frame_size = 0;
switch (p[-1]) {
case STACK_FARCALL:
frame_size = 13;
break;
case 0xee:
frame_size = 5;
break;
case 0xdd:
frame_size = LittleEndian_getW(p, -3) * 2 + 5;
break;
}
if (!frame_size)
break; // System3.5 save files has some junk data at the stack bottom.
p -= frame_size;
frame = push_asdv1_stack_frame(frame_size, frame);
memcpy(frame->buf, p, frame_size);
}
stack_top = stack_buf;
for (struct asdv1_stack_frame *f = frame; f;) {
switch (f->buf[f->len - 1]) {
case STACK_FARCALL:
stack_reserve(2 + 4 + 1);
if (LittleEndian_getDW(f->buf, 0) != 0)
WARNING("unexpected stack structure");
stack_push_word(LittleEndian_getDW(f->buf, 4));
stack_push_dword(LittleEndian_getDW(f->buf, 8));
stack_push_byte(STACK_FARCALL);
break;
case STACK_NEARCALL:
stack_reserve(4 + 1);
stack_push_dword(LittleEndian_getDW(f->buf, 0));
stack_push_byte(STACK_NEARCALL);
break;
case STACK_VARIABLE:
{
int count = LittleEndian_getW(f->buf, f->len - 3);
int base = LittleEndian_getW(f->buf, f->len - 5);
stack_reserve((2 + 2 + 2 + 1) * count);
for (int i = 0; i < count; i++) {
stack_push_word(base + 1);
stack_push_word(0); // page
stack_push_word(LittleEndian_getW(f->buf, i * 2));
stack_push_byte(STACK_VARIABLE);
}
}
break;
}
struct asdv1_stack_frame *next = f->next;
free(f);
f = next;
}
}
static void load_stack_sys39(uint8_t *data, int size) {
if (size > stack_size) {
while (size > stack_size)
stack_size *= 2;
free(stack_buf);
stack_buf = malloc(stack_size);
if (!stack_buf)
NOMEMERR();
}
memcpy(stack_buf, data, size);
stack_top = stack_buf + size;
}
void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
switch (format) {
case SAVEFMT_XSYS35:
load_stack_xsys35(unaligned_data, size);
break;
case SAVEFMT_SYS35:
load_stack_sys35(unaligned_data, size);
break;
default:
load_stack_sys39(unaligned_data, size);
break;
}
}
void sl_getStackInfo(struct stack_info *info) {
memset(info, 0, sizeof(struct stack_info));
+20 -61
View File
@@ -45,12 +45,6 @@
static void sdl_getEvent(void);
static void keyEventProsess(SDL_KeyboardEvent *e, boolean pressed);
static uint32_t custom_event_type = (uint32_t)-1;
enum CustomEventCode {
SIMULATE_RIGHT_BUTTON,
};
/* pointer の状態 */
static int mousex, mousey, mouseb;
static int mouse_wheel_up, mouse_wheel_down;
@@ -59,11 +53,6 @@ boolean RawKeyInfo[256];
/* SDL Joystick */
static int joyinfo=0;
void sdl_event_init(void) {
if (custom_event_type == (uint32_t)-1)
custom_event_type = SDL_RegisterEvents(1);
}
static int mouse_to_rawkey(int button) {
switch(button) {
case SDL_BUTTON_LEFT:
@@ -89,16 +78,20 @@ static int mouse_to_agsevent(int button) {
}
EMSCRIPTEN_KEEPALIVE
void send_agsevent(enum agsevent_type type, int code) {
void send_agsevent(int type, int code) {
if (!nact->ags.eventcb)
return;
agsevent_t agse = {
.type = type,
.code = code,
.mousex = mousex,
.mousey = mousey
};
agsevent_t agse;
agse.type = type;
agse.d1 = mousex;
agse.d2 = mousey;
agse.d3 = code;
nact->ags.eventcb(&agse); // Async in emscripten
#ifdef __EMSCRIPTEN__
// HACK: this ensures that callers of this function are instrumented.
emscripten_sleep(0);
#endif
}
// Improves map navigation of Rance4 v2. See also the function comment of
@@ -198,41 +191,34 @@ static void sdl_getEvent(void) {
mouseb |= 1 << SDL_BUTTON_RIGHT;
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = FALSE;
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = TRUE;
send_agsevent(AGSEVENT_BUTTON_PRESS, AGSEVENT_BUTTON_RIGHT);
} else {
// SDL_RendererEventWatch clamps touch locations outside of the
// viewport to 0.0-1.0. Treat such events as right-clicks.
int button;
if (e.tfinger.x == 0.0f || e.tfinger.x == 1.0f || e.tfinger.y == 0.0f || e.tfinger.y == 1.0f) {
button = SDL_BUTTON_RIGHT;
if (e.tfinger.x == 0.0f || e.tfinger.x == 1.0f || e.tfinger.y == 0.0f || e.tfinger.y == 1.0f) {
mouseb |= 1 << SDL_BUTTON_RIGHT;
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = TRUE;
} else {
button = SDL_BUTTON_LEFT;
mousex = e.tfinger.x * view_w;
mousey = e.tfinger.y * view_h;
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
mouseb |= 1 << SDL_BUTTON_LEFT;
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = TRUE;
}
mouseb |= 1 << button;
RawKeyInfo[mouse_to_rawkey(button)] = TRUE;
send_agsevent(AGSEVENT_BUTTON_PRESS, mouse_to_agsevent(button));
mousex = e.tfinger.x * view_w;
mousey = e.tfinger.y * view_h;
}
break;
case SDL_FINGERUP:
if (SDL_GetNumTouchFingers(e.tfinger.touchId) == 0) {
int ags_button = (mouseb & 1 << SDL_BUTTON_LEFT) ? AGSEVENT_BUTTON_LEFT : AGSEVENT_BUTTON_RIGHT;
mouseb &= ~(1 << SDL_BUTTON_LEFT | 1 << SDL_BUTTON_RIGHT);
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_LEFT)] = FALSE;
RawKeyInfo[mouse_to_rawkey(SDL_BUTTON_RIGHT)] = FALSE;
mousex = e.tfinger.x * view_w;
mousey = e.tfinger.y * view_h;
send_agsevent(AGSEVENT_BUTTON_RELEASE, ags_button);
}
break;
case SDL_FINGERMOTION:
mousex = e.tfinger.x * view_w;
mousey = e.tfinger.y * view_h;
send_agsevent(AGSEVENT_MOUSE_MOTION, 0);
break;
case SDL_JOYDEVICEADDED:
@@ -279,22 +265,9 @@ static void sdl_getEvent(void) {
}
}
break;
default:
if (e.type == custom_event_type) {
switch (e.user.code) {
case SIMULATE_RIGHT_BUTTON:
if ((intptr_t)e.user.data1) {
mouseb |= 1 << SDL_BUTTON_RIGHT;
RawKeyInfo[KEY_MOUSE_RIGHT] = TRUE;
send_agsevent(AGSEVENT_BUTTON_PRESS, AGSEVENT_BUTTON_RIGHT);
} else {
mouseb &= ~(1 << SDL_BUTTON_RIGHT);
RawKeyInfo[KEY_MOUSE_RIGHT] = FALSE;
send_agsevent(AGSEVENT_BUTTON_RELEASE, AGSEVENT_BUTTON_RIGHT);
}
break;
}
}
NOTICE("ev %x", e.type);
break;
}
}
@@ -367,17 +340,3 @@ int sdl_getJoyInfo(void) {
sdl_getEvent();
return joyinfo;
}
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_KEEPALIVE
void simulate_right_button(int pressed) {
SDL_Event event = {
.user = {
.type = custom_event_type,
.code = SIMULATE_RIGHT_BUTTON,
.data1 = (void*)pressed
}
};
SDL_PushEvent(&event);
}
#endif
-1
View File
@@ -52,7 +52,6 @@ struct sdl_private_data {
boolean (*custom_event_handler)(const SDL_Event *);
};
void sdl_event_init(void);
void sdl_cursor_init(void);
void sdl_shadow_init(void);
int sdl_nearest_color(int r, int g, int b);
+1 -1
View File
@@ -81,7 +81,7 @@ int sdl_Initialize(const char *render_driver) {
/* offscreen Pixmap */
makeDIB(SYS35_DEFAULT_WIDTH, SYS35_DEFAULT_HEIGHT, SYS35_DEFAULT_DEPTH);
sdl_event_init();
/* init cursor */
sdl_cursor_init();
sdl_setWindowSize(SYS35_DEFAULT_WIDTH, SYS35_DEFAULT_HEIGHT);
+1 -1
View File
@@ -111,7 +111,7 @@ static void sys35_usage(boolean verbose) {
puts("OPTIONS");
puts(" -gamefile file : set game resource file to 'file'");
puts(" -game game : enable game-specific hacks");
puts(" -saveformat fmt : save file format. 'xsystem35', 'system36' or 'system39' (default)");
puts(" -saveformat fmt : save file format. 'xsystem35' (default), 'system36' or 'system39'");
puts(" -renderer name : set rendering driver name to 'name'");
puts(" -devcd device : set cdrom device name to 'device'");
puts(" -devmidi device : set midi device name to 'device'");