У тапок формат очень простой: два байта длины, флаг, массив байт, чексумма; два байта длины, флаг... и т.д. для каждого файла внутри тапки
Вид для печати
У кого-то есть игра "The Last ninja 2" дисковая версия (не TAP) с содержимым L.NINJA2 "Disked by BILL GILBERT 1988" ?
Походу ZXK+ZXS - это дисковая версия.
SNC файлы начинаются с TAP заголовков, содержат нормальные TAP блоки, но TAP файлами в строгом смысле этого определения не являются, там есть ещё какой-то мусор. Странная разбивка на блоки и несовпадающие контрольные суммы, просто мусор. Хотя, многие можно загрузить в эмуляторе, зависит от того, проверяет эмулятор структуру всего файла (например, FUSE) или просто читает корректные блоки игнорируя мусор (например, xpeccy). Я думаю, SNC файлы можно пересобрать в корректный TAP, выбрав блоки с правильными контрольными суммами, и они потом отлично заработают.
Кстати. Когда я читал всё подряд про спектрум, а это было совсем недавно, я наткнулся на описание какого-то древнего эмулятора (сам эмулятор найти не смог, поэтому и без деталей), который хранил ленту вперемешку с дампами памяти. Вот ZXS точно в SPECCY EMULATOR FILE FORMATS (last updated on 2 May 2000) упоминается. Это я к чему, какие эмуляторы были в прошлом веке?
Барбарян
Большинство чексумм внутри SNC совпадает (не сошлось у F16), действительно есть вариант сконвертить данные:
https://pastebin.com/rD6yHrMs
Upd:
сконвертил https://www.dropbox.com/scl/fi/tnb3t...dbjckmuvn&dl=0
Чем конвертил?
Кстати, да, посмотрел ещё раз -- там, похоже, просто файлы SNC по размеру зачем-то выровнены то ли на 16 байт, то ли на 128 байт и «добиты» нулями. И эти нули честные TAP читалки с ума сводят. Вот, что за ZXS файл, интересно...
P.S. А ещё там «f%cking Хоррошоу» есть... Интересная подборка с точки зрения цифровой археологии.
Секретного ничего нет:
https://pastebin.com/CU3Zy1Bk
- - - Добавлено - - -
ZXS - 30 байт хедер, потом идёт бейсик
https://i.imgur.com/QqHVBOh.png
TAP - можно представить как архив, когда несколько файлов объединяются в один, друг за другом "паровозиком".
Чтобы разделить этот "архив" на исходные файлы, требуется дополнительная информация - длина файла, который идёт дальше (один или больше).
Кроме длины файла, самих данных, ещё добавляют байт контрольной суммы.
Т.е. внутри "архива" в общем случае, лежат:
1) длина первого файла и флаг, определяющий тип (заголовок, или блок данных)
2) первый файл (заголовок, как на ленте)
3) контрольная сумма
4) длина второго файла и флаг
5) второй файл (обычно бейсик)
6) контрольная сумма
7)....
далее идут картинка и основной блок
В итоге, SNC - следует похожей логике, только файлы внутри "архива" выровнены до определённого размера, и хвосты забиты нулями.
ZXS - что-то близкое, но "архив" начинается более длинным заголовком (похоже на 30 байт, где кодируется название файла, длина, начальный адрес).
ZXS точно не совпадают по структуре с неким ZXS, который понимает древний TAPER (других программ заявляющих поддержку ZXS я пока не нашёл). ZXS хорошо режется по принципу ДЛИНА-ДАННЫЕ, как TAP (LL + LL bytes of data), но содержимое блоков странновато, даже если выкинуть нули:
Вложение 81912
Вложение 81913
Не совсем понятно, откуда взять длину «настоящих» данных второго блока (собственно BASIC кода)?
В принципе, если предположить, что для блоков данных (0xff) структура опять же LL xx [LL-2 байт данных как в TAP], то примерно что-то вырисовывается. xx, скорее всего, длина данных в блоке минус «добивка» для выравнивания. Но это не работает для BASIC блоков. Или я что-то упускаю?
Ага, так тоже можно, но есть лучше идея. А что, если там какое-то RLE, через который пропущен обычный TAP. Формат что-то вроде: CC FF data, CC - длина/количество, FF -- как интерпретировать (00 -- использовать data как есть, 0x8x -- повторяем один байт CC+x раз). Насчёт FF надо ещё подумать, но, вроде, почти всё сходится.
Скрипт можете написать?
Разгадал загадку, с помощью киножурнала «Хочу всё знать!»:) ZXS это комбинация length-block, только там внутринеонкаRLE. Зачем так сложно, не знаю, может, чтобы блоки можно было индивидуально распаковывать.
https://mega.nz/file/YS9CmBgD#1QmsUl...Y7NtmXHuVjeZiU
Значит, формат такой:
- файл состоит из блоков, блоки записаны как в TAP: два байта длина и соответствующее количество байт
- каждый блок, в свою очередь, запакован RLE: там последовательность cc CC b1 [... bN], где cc CC количество байт в фрагменте. Если старший бит CC выставлен в 1, то дальше идёт один байт, который надо повторить cc+((CC&0x7f)<<8) раз. Если старший бит CC -- 0, то следует последовательность байт длинной cc+CC<<8
- RLE блок заканчивается двумя нулевыми байтами, тоже не совсем понятно, зачем, но потоковое раскодирование упрощает :)
zxs2tap.py
Код:#!/usr/bin/env python3
# Copyright 2025 TIsland Crew
# SPDX-License-Identifier: Apache-2.0
# ZXS archive unpacker, see
# https://zx-pk.ru/threads/11422-versii-igr-i-softa-najdennye-na-kassetakh.html?p=1210270&viewfull=1#post1210270
from sys import argv
from struct import pack, unpack
DEBUG = False
def chksum(*args):
chksum = 0
for data in args:
for b in data: chksum ^= b
return chksum
def zxs_unpack(n: str):
print(n, 'to TAP')
with open(f'{n}.tap', 'wb') as t:
with open(n, 'rb') as f:
zxs_block_read(f, t)
print(n, 'TAP ready')
def zxs_block_read(f, t):
# phase 1: read "container" blocks
blkn = 0
while buf := f.read(2):
blkn += 1
size = unpack('<H', buf)[0]
if 0 == size: break
print('#{: 2d} @0x{:06x} length {:d}'.format(blkn, f.tell()-2, size))
# phase 2: unpack RLE encoded data for each block
# NOTE: so far in all files we've seen the RLE sequence ends with 00 00
# hence there was no need to track RLE block size
zxs_rle_unpack(f, t)
def zxs_rle_unpack(f, t):
print(' ', end='')
data = bytearray(2) # placeholder for unpacked size (2 bytes)
while buf := f.read(2):
( count, flag ) = unpack('<BB', buf)
if 0 == count and 0 == flag:
break # end of RLE encoded block
count += (flag&0x7f)<<8
if 0x80 == flag&0x80: # repeating byte
b = f.read(1)
if DEBUG: print('0x{}X{:d};'.format(b.hex(), count), end='')
data.extend(b * count)
else: # arbitrary sequence
if DEBUG: print('b{:d};'.format(count), end='')
data.extend(f.read(count))
cs = chksum(data[2:-1]) # unpacked data checksum
csa = unpack('<B', data[-1:])[0] # recorded checksum
data[0:2] = pack('<H', len(data)-2) # unpacked block length
if DEBUG: print(' checksum {:d} vs {:d}'.format(cs, csa))
if csa != cs:
print(' WARNING: checksum mismatch, fixed')
data[-1] = cs
print(' Unpacked size: {:d}'.format(len(data)-2))
t.write(data)
if __name__ == '__main__':
for arg in argv[1:]:
if '-d' == arg:
DEBUG = not DEBUG
else:
zxs_unpack(arg)
# EOF vim: et:ai:ts=4:sw=4:
[свернуть]
P.S. В принципе, формат остроумный, позволяет декодировать файл используя только буфер в 2 байта (не считая переменных, конечно). Опять же, зачем такая оптимизация, когда есть как минимум 640k... Было бы очень интересно узнать откуда есть пошло то ПО для обработки/записи.
Кстати, если вдруг у кого остались в процессе каких-то ранних оцифровок хакнутые версии которые по факту оказались с битыми блоками или допустим сохранился только загрузчик - можете скинуть я посмотрю что с этим можно будет сделать.
Народ, сможете поглядеть эти два релиза? после загрузки не откликается на нажатие клавиш https://transfiles.ru/eqcs1
- - - Добавлено - - -
p.s: и еще https://transfiles.ru/xsole тут второму блоку конечно хорошо досталось, но может хотя бы можно перевести и подправить загрузчик чтобы без второго грузилось? во втором конечно какая-то бегущая строка должна была быть:
Цитата:
�HELLO DEAR USERSDїZX-SPECTRUM.DMITRY LABUTCKY PRESENT FOR YOU e}e odnu igru na{ihџ ote~estwennyh programmistow
Короче, в Krakout3 если я правильно понял не происходит самого главного, а именно randomize usr 16384, ибо если я за ранее просто дампами загружу блоки в 25000 и уже распакованную в конце картинку в 16384 и сделаю randomize usr 16384 то у меня все работает, вопрос как это пофиксить в загрузчике?
https://i.postimg.cc/HxW0rryW/Screenshot-6.jpg
p.s: тоже самое и с joe blade 2. я спокойно могу загрузить в память кодовый блок в адрес 27000, вызвать его, затем загрузить последний, вызвать 61000 и затем вызвать 16384 и у меня все прекрасно работает. т.е явно какая-то проблема именно с загрузчиками
удалось разобраться.
распаковщик (последней) картинки для своей работы использует/портит IY !!!,
но в оригинальной версии IY восстанавливается (перед возвратом в бейсик),
тут этого не происходит и в результате сбивается указатель на системные переменные (меняется режим ввода).
(исправить можно заменив IY/IX)
в последнем блоке надо поменять все #FD на #DD, последняя команда #FDCB035E
глянул, там всё аналогично.
goodboy, а вот не работает, уже 2 раза перепроверил
goodboy, я понял что расположение одинаковое что у jblade и что у krakout но я в третий раз повторяю что после замены FD на DD (всего 28 таких байтов) ничего не изменяется и опрос клавиатуры не происходит.
в JoeBlade2 всё настолько коряво сделано что нормально стартует только выбор на русском.
goodboy, сколько проверял что в 48 что в 128 оба языка запускаются нормально
ага, это я ошибся (перед опросом цифр на стек помещается адрес старта).
выложи исправленные TAPки
https://transfiles.ru/m3pub
krakout как не работал после фикса так и не работает