Skip to content

Commit bcab7cb

Browse files
committed
Automatic updates, other work-in-progress
1 parent 543c570 commit bcab7cb

File tree

102 files changed

+4347
-417
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+4347
-417
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fs-uae-launcher_*
2525
/Launcher-*
2626
/Launcher_*
2727
Lib
28+
/*.log
2829
*.mo
2930
/.mypy_cache/
3031
*.pot

Pipfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pywin32-ctypes = "*"
2323
pyinstaller = "*"
2424
pefile = "*"
2525
macholib = "*"
26+
rx = "*"
27+
autologging = "*"
2628

2729
[requires]
2830
python_version = "3.9"

Pipfile.lock

Lines changed: 143 additions & 128 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/whdload-support.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,23 +128,28 @@ specific game requires, so it installs all of them. The following kickstart
128128
ROM images must be present in your file database:
129129

130130
SHA-1: `11f9e62cf299f72184835b7b2a70a16333fc0d88`, known as:
131-
- Kickstart v1.2 r33.180 (1986-10)(Commodore)(A500-A1000-A2000)[!].rom
132-
- kick33180.A500
131+
- `Kickstart v1.2 r33.180 (1986-10)(Commodore)(A500-A1000-A2000)[!].rom`
132+
- `kick33180.A500`
133+
- `amiga-os-120.rom` (Amiga Forever)
133134

134135
SHA-1: `891e9a547772fe0c6c19b610baf8bc4ea7fcb785`, known as:
135-
- Kickstart v1.3 r34.005 (1987-12)(Commodore)(A500-A1000-A2000-CDTV)[!].rom
136-
- kick34005.A500
136+
- `Kickstart v1.3 r34.005 (1987-12)(Commodore)(A500-A1000-A2000-CDTV)[!].rom`
137+
- `kick34005.A500`
138+
- `amiga-os-130.rom` (Amiga Forever)
137139

138140
SHA-1: `e21545723fe8374e91342617604f1b3d703094f1`, known as:
139-
- Kickstart v3.1 r40.068 (1993-12)(Commodore)(A1200)[!].rom
140-
- kick40068.A1200
141+
- `Kickstart v3.1 r40.068 (1993-12)(Commodore)(A1200)[!].rom`
142+
- `kick40068.A1200`
143+
- `amiga-os-310-a1200.rom` (Amiga Forever)
141144

142145
SHA-1: `5fe04842d04a489720f0f4bb0e46948199406f49`, known as:
143-
- Kickstart v3.1 r40.068 (1993-12)(Commodore)(A4000)[!].rom
144-
- kick40068.A4000
146+
- `Kickstart v3.1 r40.068 (1993-12)(Commodore)(A4000)[!].rom`
147+
- `kick40068.A4000`
148+
- `amiga-os-310-a4000.rom` (Amiga Forever)
145149

146150
If you do not have these kickstart ROM files, then you get these as part of
147-
Amiga Forever (at least the Plus edition).
151+
Amiga Forever (at least the Plus edition). See
152+
https://www.amigaforever.com/kb/15-120 for more information about relevant kickstart files included in the Amiga Forever package.
148153

149154
## Saving games
150155

fsbc/util.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,23 @@ def split_version(version_string: str) -> List[str]:
3737
pattern = re.compile(
3838
"^([0-9]{1,4})(?:\.([0-9]{1,4}))?"
3939
"(?:\.([0-9]{1,4}))?(?:\.([0-9]{1,4}))?"
40-
"(~?[a-z0-9\.]*)?(?:[-_]([0-9]+))?$"
40+
"([-~]?[a-z0-9\.]*)?(?:[-_]([0-9]+))?$"
4141
)
4242
m = pattern.match(version_string)
4343
if m is None:
4444
raise ValueError(version_string + " is not a valid version number")
4545
return list(m.groups())
4646

4747

48-
class Version(object):
48+
class Version:
49+
"""
50+
>>> Version("1.26.1.16-fs") == Version("1.26.1.16-fs")
51+
True
52+
>>> Version("1.26.1.16-fs") < Version("1.26.1.16-fs")
53+
False
54+
>>> Version("1.26.1.16-fs") > Version("1.26.1.16-fs")
55+
False
56+
"""
4957
def __init__(self, version_string: str) -> None:
5058
self.string = version_string
5159
v = split_version(version_string)
@@ -77,12 +85,21 @@ def cmp_value(self):
7785
mod_cmp = "~~" + mod_cmp
7886
return self.val, mod_cmp, self.release
7987

88+
def __eq__(self, other) -> bool:
89+
return self.cmp_value() == other.cmp_value()
90+
8091
def __lt__(self, other: "Version") -> bool:
8192
return self.cmp_value() < other.cmp_value()
8293

94+
def __le__(self, other: "Version") -> bool:
95+
return self.cmp_value() <= other.cmp_value()
96+
8397
def __gt__(self, other: "Version") -> bool:
8498
return self.cmp_value() > other.cmp_value()
8599

100+
def __ge__(self, other: "Version") -> bool:
101+
return self.cmp_value() >= other.cmp_value()
102+
86103
def __str__(self) -> str:
87104
return self.string
88105

@@ -141,6 +158,8 @@ def compare_versions(a: Union[Version, str], b: Union[Version, str]):
141158
1
142159
>>> compare_versions("1.22.2fs0", "1.22.2")
143160
1
161+
>>> compare_versions("1.26.1.16-fs", "1.26.1.16-fs")
162+
0
144163
"""
145164
# >>> compare_versions("2.6.0beta1", "2.6.0beta1.1")
146165
# -1

fsboot/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ def custom_path(name):
288288
return path
289289

290290

291+
def getBaseDirectory():
292+
return base_dir()
293+
294+
291295
@lru_cache()
292296
def base_dir():
293297
logger.debug("Find base directory")
@@ -349,6 +353,9 @@ def base_dir():
349353

350354
@lru_cache()
351355
def development():
356+
# FIXME: Document option
357+
if "--development-mode=0" in sys.argv:
358+
return False
352359
result = os.path.exists(os.path.join(executable_dir(), "setup.py"))
353360
logger.info("Development mode: %s", result)
354361
return result

fsbuild/archive

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ mkdir -p fsbuild/_dist
1818

1919
cd fsbuild/_build
2020

21-
if [ "$SYSTEM_OS" = "Windows" ]; then
22-
ARCHIVE=${PACKAGE_NAME_PRETTY}_${PACKAGE_VERSION}_${OS}_${ARCH}.zip
23-
zip -r ../_dist/$ARCHIVE ${PACKAGE_NAME_PRETTY}
24-
else
21+
# if [ "$SYSTEM_OS" = "Windows" ]; then
22+
# ARCHIVE=${PACKAGE_NAME_PRETTY}_${PACKAGE_VERSION}_${OS}_${ARCH}.zip
23+
# zip -r ../_dist/$ARCHIVE ${PACKAGE_NAME_PRETTY}
24+
# else
2525
ARCHIVE=${PACKAGE_NAME_PRETTY}_${PACKAGE_VERSION}_${OS}_${ARCH}.tar.xz
2626
tar cfJv ../_dist/$ARCHIVE ${PACKAGE_NAME_PRETTY}
27-
fi
27+
# fi
2828

2929
echo -------------------------------------------------------------------------\
3030
-------

fscore/mainloop.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# FIXME: Use pluggable main loop implementation
2+
3+
from typing import Callable
4+
5+
6+
class MainLoop:
7+
@classmethod
8+
def schedule(cls, action: Callable):
9+
print("MainLoop.schedule", action)
10+
from fsui import call_after
11+
call_after(action)

fscore/observable.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
from fscore.mainloop import MainLoop
2+
3+
# from rx import Observable as BaseObservable
4+
import rx
5+
import rx.operators
6+
import rx.disposable
7+
8+
amapOperator = rx.operators.map
9+
10+
def mapOperator(*args):
11+
return rx.operators.map(*args)
12+
13+
# class Observable(BaseObservable):
14+
# pass
15+
16+
17+
Observable = rx.Observable
18+
Disposable = rx.disposable.Disposable
19+
20+
21+
def isObservable(obj):
22+
return hasattr(obj, "subscribe")
23+
24+
25+
class Disposer:
26+
def __init__(self, *args):
27+
self.disposables = args
28+
29+
def __call__(self):
30+
print("Disposer.__call__")
31+
for disposable in self.disposables:
32+
print(disposable, "-> dispose()")
33+
disposable.dispose()
34+
35+
36+
import logging
37+
38+
from datetime import timedelta
39+
from typing import Any, Optional, Set
40+
41+
from rx.core import typing
42+
from rx.disposable import CompositeDisposable, Disposable, SingleAssignmentDisposable
43+
44+
from rx.scheduler.periodicscheduler import PeriodicScheduler
45+
46+
47+
log = logging.getLogger(__name__)
48+
49+
50+
# FIXME: Might also put this in fsui instead?
51+
52+
class MainLoopScheduler(PeriodicScheduler):
53+
54+
def __init__(self):
55+
super().__init__()
56+
# elf._qtcore = qtcore
57+
# FIXME
58+
# timer_class: Any = self._qtcore.QTimer
59+
# self._periodic_timers: Set[timer_class] = set()
60+
61+
def schedule(self,
62+
action: typing.ScheduledAction,
63+
state: Optional[typing.TState] = None
64+
) -> typing.Disposable:
65+
"""Schedules an action to be executed.
66+
67+
Args:
68+
action: Action to be executed.
69+
state: [Optional] state to be given to the action function.
70+
71+
Returns:
72+
The disposable object used to cancel the scheduled action
73+
(best effort).
74+
"""
75+
print("MainLoopScheduler.schedule")
76+
77+
# print("schedule_relative", duetime, action, state)
78+
# msecs=1
79+
# msecs = max(0, int(self.to_seconds(duetime) * 1000.0))
80+
sad = SingleAssignmentDisposable()
81+
is_disposed = False
82+
83+
def invoke_action() -> None:
84+
print("invoke_action")
85+
if not is_disposed:
86+
sad.disposable = action(self, state)
87+
88+
# Use static method, let Qt C++ handle QTimer lifetime
89+
# self._qtcore.QTimer.singleShot(msecs, invoke_action)
90+
MainLoop.schedule(invoke_action)
91+
92+
def dispose() -> None:
93+
nonlocal is_disposed
94+
is_disposed = True
95+
96+
return CompositeDisposable(sad, Disposable(dispose))
97+
98+
def schedule_relative(self,
99+
duetime: typing.RelativeTime,
100+
action: typing.ScheduledAction,
101+
state: Optional[typing.TState] = None
102+
) -> typing.Disposable:
103+
"""Schedules an action to be executed after duetime.
104+
105+
Args:
106+
duetime: Relative time after which to execute the action.
107+
action: Action to be executed.
108+
state: [Optional] state to be given to the action function.
109+
110+
Returns:
111+
The disposable object used to cancel the scheduled action
112+
(best effort).
113+
"""
114+
raise NotImplementedError
115+
print("schedule_relative", duetime, action, state)
116+
msecs=1
117+
msecs = max(0, int(self.to_seconds(duetime) * 1000.0))
118+
sad = SingleAssignmentDisposable()
119+
is_disposed = False
120+
121+
def invoke_action() -> None:
122+
print("invoke_action")
123+
if not is_disposed:
124+
sad.disposable = action(self, state)
125+
126+
log.debug("relative timeout: %sms", msecs)
127+
128+
# Use static method, let Qt C++ handle QTimer lifetime
129+
self._qtcore.QTimer.singleShot(msecs, invoke_action)
130+
131+
def dispose() -> None:
132+
nonlocal is_disposed
133+
is_disposed = True
134+
135+
return CompositeDisposable(sad, Disposable(dispose))
136+
137+
def schedule_absolute(self,
138+
duetime: typing.AbsoluteTime,
139+
action: typing.ScheduledAction,
140+
state: Optional[typing.TState] = None
141+
) -> typing.Disposable:
142+
"""Schedules an action to be executed at duetime.
143+
144+
Args:
145+
duetime: Absolute time at which to execute the action.
146+
action: Action to be executed.
147+
state: [Optional] state to be given to the action function.
148+
149+
Returns:
150+
The disposable object used to cancel the scheduled action
151+
(best effort).
152+
"""
153+
raise NotImplementedError
154+
155+
delta: timedelta = self.to_datetime(duetime) - self.now
156+
return self.schedule_relative(delta, action, state=state)
157+
158+
def schedule_periodic(self,
159+
period: typing.RelativeTime,
160+
action: typing.ScheduledPeriodicAction,
161+
state: Optional[typing.TState] = None
162+
) -> typing.Disposable:
163+
"""Schedules a periodic piece of work to be executed in the loop.
164+
165+
Args:
166+
period: Period in seconds for running the work repeatedly.
167+
action: Action to be executed.
168+
state: [Optional] state to be given to the action function.
169+
170+
Returns:
171+
The disposable object used to cancel the scheduled action
172+
(best effort).
173+
"""
174+
raise NotImplementedError
175+
msecs = max(0, int(self.to_seconds(period) * 1000.0))
176+
sad = SingleAssignmentDisposable()
177+
178+
def interval() -> None:
179+
nonlocal state
180+
state = action(state)
181+
182+
log.debug("periodic timeout: %sms", msecs)
183+
184+
timer = self._qtcore.QTimer()
185+
timer.setSingleShot(not period)
186+
timer.timeout.connect(interval)
187+
timer.setInterval(msecs)
188+
self._periodic_timers.add(timer)
189+
timer.start()
190+
191+
def dispose() -> None:
192+
timer.stop()
193+
self._periodic_timers.remove(timer)
194+
timer.deleteLater()
195+
196+
return CompositeDisposable(sad, Disposable(dispose))

0 commit comments

Comments
 (0)