1use core::fmt::{self, Write};
10use core::num::NonZeroU16;
11use core::ops::Deref;
12use core::convert::{TryFrom, AsRef};
13use core::ptr::NonNull;
14use core::marker::PhantomPinned;
15use std::borrow::Cow;
16use std::pin::Pin;
17use std::io;
18
19#[allow(unused_imports)]
20use log::{error, warn, info, debug, trace};
21
22use memchr::memchr;
23use nom::bytes::complete::tag;
24use nom::combinator::{iterator, map};
25use nom::error::{ErrorKind, context, ParseError, ContextError};
26use nom::multi::many1;
27use nom::number::complete::{be_u16, be_u8};
28use nom::sequence::tuple;
29use nom::{Offset, IResult, Err};
30
31use spectrusty_core::z80emu::{Cpu, Prefix, Reg8, CpuFlags, InterruptMode};
32use spectrusty_core::memory::ZxMemory;
34
35pub type PinAyFile = Pin<Box<AyFile>>;
37
38pub struct AyFile {
46 pub raw: Box<[u8]>,
48 pub meta: AyMeta,
50 pub songs: Box<[AySong]>,
52 _pin: PhantomPinned,
53}
54
55#[derive(Debug)]
57pub struct AyMeta {
58 pub file_version: u8,
60 pub player_version: u8,
62 pub special_player: bool,
64 pub author: AyString,
66 pub misc: AyString,
68 pub first_song: u8,
70}
71
72#[derive(Debug)]
74pub struct AySong {
75 pub name: AyString,
77 pub chan_a: u8,
79 pub chan_b: u8,
81 pub chan_c: u8,
83 pub noise: u8,
85 pub song_duration: u16,
87 pub fade_duration: u16,
89 pub hi_reg: u8,
91 pub lo_reg: u8,
93 pub stack: u16,
95 pub init: u16,
97 pub interrupt: u16,
99 pub blocks: Box<[AySongBlock]>
101}
102
103#[derive(Debug)]
105pub struct AySongBlock {
106 pub address: u16,
108 pub data: AyBlob,
110}
111
112pub struct AyBlob(NonNull<[u8]>);
114pub struct AyString(NonNull<[u8]>);
116
117impl fmt::Debug for AyBlob {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 write!(f, "AyBlob: {{ {} }}", self.len())
120 }
121}
122
123impl fmt::Debug for AyString {
124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125 write!(f, "AyString: {:?}", self.to_str_lossy())
126 }
127}
128
129impl fmt::Display for AyString {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 write!(f, "{}", self.to_str_lossy())
132 }
133}
134
135impl fmt::Debug for AyFile {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 write!(f, "AyFile {{ raw: ({:?}), meta: {:?}, songs: {:?} }}", self.raw.len(), self.meta, self.songs)
138 }
139}
140
141impl AsRef<[u8]> for AyBlob {
142 fn as_ref(&self) -> &[u8] {
143 self.as_slice()
144 }
145}
146
147impl AsRef<[u8]> for AyString {
148 fn as_ref(&self) -> &[u8] {
149 self.as_slice()
150 }
151}
152
153impl Deref for AyBlob {
154 type Target = [u8];
155 fn deref(&self) -> &[u8] {
156 self.as_slice()
157 }
158}
159
160impl AyBlob {
161 fn new(slice: &[u8]) -> Self {
162 AyBlob(NonNull::from(slice))
163 }
164
165 pub fn as_slice(&self) -> &[u8] {
167 unsafe { self.0.as_ref() } }
169}
170
171impl AyString {
172 fn new(slice: &[u8]) -> Self {
173 AyString(NonNull::from(slice))
174 }
175
176 pub fn as_slice(&self) -> &[u8] {
178 unsafe { self.0.as_ref() } }
180
181 pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
183 core::str::from_utf8(self.as_slice())
184 }
185
186 pub fn to_str_lossy(&self) -> Cow<str> {
189 String::from_utf8_lossy(self.as_slice())
190 }
191}
192
193static PLAYER_ONE: &[u8] = &[
1940xF3, 0xCD,0x00,0x00, 0xED,0x5E, 0xFB, 0x76, 0x18,0xFA, ];
201static PLAYER_TWO: &[u8] = &[
2020xF3, 0xCD,0x00,0x00, 0xED,0x56, 0xFB, 0x76, 0xCD,0x00,0x00, 0x18,0xF7, ];
210const PLAYER_INIT_OFFSET: usize = 2;
211const PLAYER_TWO_INTERRUPT_OFFSET: usize = 9;
212
213impl AyFile {
214 pub fn initialize_player<C, M>(&self, cpu: &mut C, memory: &mut M, song_index: usize)
223 where C: Cpu, M: ZxMemory
224 {
225 if self.meta.special_player {
226 panic!("can't initialize file with a special player");
227 }
228 let song = &self.songs[song_index];
229 debug!("loading song: ({}) {}", song_index, song.name.to_str_lossy());
230 let rawmem = memory.mem_mut();
231 for p in rawmem[0x0000..0x0100].iter_mut() { *p = 0xC9 };
232 for p in rawmem[0x0100..0x4000].iter_mut() { *p = 0xFF };
233 for p in rawmem[0x4000..].iter_mut() { *p = 0x00 };
234 rawmem[0x0038] = 0xFB;
235 debug!("INIT: ${:x}", song.init);
236 let init = if song.init == 0 {
237 debug!("INIT using: ${:x}", song.blocks[0].address);
238 song.blocks[0].address
239 }
240 else {
241 song.init
242 }.to_le_bytes();
243 debug!("INTERRUPT: ${:x}", song.interrupt);
244 let player = if song.interrupt == 0 {
245 debug!("PLAYER ONE");
246 PLAYER_ONE
247 }
248 else {
249 debug!("PLAYER TWO");
250 PLAYER_TWO
251 };
252 rawmem[0x0000..player.len()].copy_from_slice(player);
253 if song.interrupt != 0 {
254 let intr = song.interrupt.to_le_bytes();
255 rawmem[PLAYER_TWO_INTERRUPT_OFFSET..PLAYER_TWO_INTERRUPT_OFFSET+intr.len()].copy_from_slice(&intr);
256 }
257 rawmem[PLAYER_INIT_OFFSET..PLAYER_INIT_OFFSET+init.len()].copy_from_slice(&init);
258 for block in song.blocks.iter() {
259 debug!("Block: ${:x} <- {:?}", block.address, block.data);
260 let address = block.address as usize;
261 let mut data = block.data.as_slice();
262 if address + data.len() > (u16::max_value() as usize) + 1 {
263 debug!("Block too large: ${:x}", address + data.len());
264 data = &data[..u16::max_value() as usize - address];
265 }
266 rawmem[address..address+data.len()].copy_from_slice(data);
267 }
268 debug!("STACK: ${:x}", song.stack);
269 cpu.reset();
271 cpu.set_i(self.meta.player_version);
272 cpu.set_reg(Reg8::H, None, song.hi_reg);
273 cpu.set_reg(Reg8::L, None, song.lo_reg);
274 cpu.set_reg(Reg8::D, None, song.hi_reg);
275 cpu.set_reg(Reg8::E, None, song.lo_reg);
276 cpu.set_reg(Reg8::B, None, song.hi_reg);
277 cpu.set_reg(Reg8::C, None, song.lo_reg);
278 cpu.exx();
279 cpu.set_acc(song.hi_reg);
280 cpu.set_flags(CpuFlags::from_bits_truncate(song.lo_reg));
281 cpu.ex_af_af();
282 cpu.set_reg(Reg8::H, None, song.hi_reg);
283 cpu.set_reg(Reg8::L, None, song.lo_reg);
284 cpu.set_reg(Reg8::D, None, song.hi_reg);
285 cpu.set_reg(Reg8::E, None, song.lo_reg);
286 cpu.set_reg(Reg8::B, None, song.hi_reg);
287 cpu.set_reg(Reg8::C, None, song.lo_reg);
288 cpu.set_reg(Reg8::H, Some(Prefix::Yfd), song.hi_reg);
289 cpu.set_reg(Reg8::L, Some(Prefix::Yfd), song.lo_reg);
290 cpu.set_reg(Reg8::H, Some(Prefix::Xdd), song.hi_reg);
291 cpu.set_reg(Reg8::L, Some(Prefix::Xdd), song.lo_reg);
292 cpu.set_acc(song.hi_reg);
293 cpu.set_flags(CpuFlags::from_bits_truncate(song.lo_reg));
294 cpu.disable_interrupts();
295 cpu.set_sp(song.stack);
296 cpu.set_im(InterruptMode::Mode0);
297 cpu.set_pc(0x0000);
298 }
299}
300
301#[derive(Clone, Debug, PartialEq, Eq)]
305struct VerboseBytesError<'a> {
306 errors: Vec<(&'a [u8], VerboseBytesErrorKind)>,
307}
308
309trait UnwrapErr<E> {
310 fn unwrap(self) -> E;
311}
312
313impl<E> UnwrapErr<E> for Err<E> {
314 fn unwrap(self) -> E {
315 match self {
316 Err::Failure(e)|Err::Error(e) => e,
317 Err::Incomplete(..) => panic!("can't unwrap an incomplete error")
318 }
319 }
320}
321
322impl<'a> VerboseBytesError<'a> {
323 fn describe(&self, input: &'a[u8]) -> String {
324 let mut res = String::new();
325 for (i, (subs, kind)) in self.errors.iter().enumerate() {
326 let offset = input.offset(subs);
327 match kind {
328 VerboseBytesErrorKind::Context(s) => writeln!(&mut res,
329 "{i}: at byte {offset} of {len}, {context}",
330 i = i,
331 context = s,
332 len = input.len(),
333 offset = offset
334 ),
335 VerboseBytesErrorKind::Nom(e) => writeln!(&mut res,
336 "{i}: at byte {offset} of {len}, in {nom_err:?}",
337 i = i,
338 len = input.len(),
339 offset = offset,
340 nom_err = e
341 ),
342 }.unwrap()
343 }
344 res
345 }
346}
347
348#[derive(Clone, Debug, PartialEq, Eq)]
349enum VerboseBytesErrorKind {
351 Context(&'static str),
353 Nom(ErrorKind),
355}
356
357impl<'a> ParseError<&'a[u8]> for VerboseBytesError<'a> {
358 fn from_error_kind(input: &'a[u8], kind: ErrorKind) -> Self {
359 VerboseBytesError {
360 errors: vec![(input, VerboseBytesErrorKind::Nom(kind))],
361 }
362 }
363
364 fn append(input: &'a[u8], kind: ErrorKind, mut other: Self) -> Self {
365 other.errors.push((input, VerboseBytesErrorKind::Nom(kind)));
366 other
367 }
368}
369
370impl<'a> ContextError<&'a[u8]> for VerboseBytesError<'a> {
371 fn add_context(input: &'a[u8], ctx: &'static str, mut other: Self) -> Self {
372 other.errors.push((input, VerboseBytesErrorKind::Context(ctx)));
373 other
374 }
375}
376
377fn c_string<'a, E: ParseError<&'a [u8]>>()
378 -> impl Fn(&'a[u8]) -> IResult<&'a[u8], &'a[u8], E>
379{
380 move |input| {
381 match memchr(0, input) {
382 Some(offs) => Ok((&input[offs..], &input[..offs])),
383 None => Err(Err::Error(E::from_error_kind(input, ErrorKind::Eof)))
384 }
385 }
386}
387
388fn parse_at<'a, T: 'a, E: ParseError<&'a[u8]>, F>(
389 offset: usize,
390 f: F
391 ) -> impl FnOnce(&'a[u8]) -> Result<T, Err<E>>
392 where F: FnOnce(&'a[u8])->Result<T, Err<E>>
393{
394 move |input| {
395 input.get(offset..).ok_or_else(|| Err::Failure(
396 E::from_error_kind(input, ErrorKind::Eof)
397 )).and_then(f)
398 }
399}
400
401fn fail_err<I: Clone, O, E, F>(
402 mut f: F
403 ) -> impl FnMut(I) -> IResult<I, O, E>
404 where F: FnMut(I) -> IResult<I, O, E>
405{
406 move |input: I| {
407 f(input).map_err(|e| e.into_failure())
408 }
409}
410
411trait IntoFailure<E> {
412 fn into_failure(self) -> Self;
413}
414
415impl<E> IntoFailure<E> for Err<E> {
416 fn into_failure(self) -> Self {
417 if let Err::Error(e) = self {
418 Err::Failure(e)
419 }
420 else {
421 self
422 }
423 }
424}
425
426fn parse_ay_raw<'a, E: Clone + ContextError<&'a [u8]> + ParseError<&'a [u8]>>(
427 raw: &'a [u8]
428 ) -> Result<(AyMeta, Box<[AySong]>), Err<E>>
429{
430
431 let ay_blob = |offset: usize, len: usize| {
432 if offset+len > raw.len() {
433 debug!("truncating data off: {} len: {} end: {} > {}", offset, len, offset+len, raw.len());
434 raw.get(offset..)
435 }
436 else {
437 raw.get(offset..offset+len)
438 }.map(|blob| {
439 AyBlob::new(blob)
440 }).ok_or_else(|| {
441 let (inp, context) = (&raw[raw.len()..], "data offset exceeding file size");
442 Err::Failure(E::add_context(inp, context,
443 E::from_error_kind(inp, ErrorKind::Eof)
444 ))
445 })
446 };
447
448 let ay_string = |ctx, offset: Option<usize>| {
449 offset.map(|offset|
450 parse_at(offset, context(ctx, c_string()))( raw )
451 ).unwrap_or_else(||
452 Ok( (raw, b"") )
453 ).map(|(_,s)| AyString::new(s))
454 };
455
456 let maybe_offs_to = |inp| {
457 let offset = isize::try_from(raw.offset(inp)).unwrap();
458 let (inp, offs) = map(be_u16, NonZeroU16::new)(inp)?;
459 let res = offs.map(|offs| {
460 usize::try_from(offset + offs.get() as isize).unwrap()
461 });
462 Ok((inp, res))
463 };
464
465 let offset_to = |inp| {
466 let (inp, offs) = maybe_offs_to(inp)?;
467 match offs {
468 None => Err(Err::Error(E::add_context(inp, "offset",
469 E::from_error_kind(inp, ErrorKind::NonEmpty)
470 ))),
471 Some(offs) => Ok((inp, offs))
472 }
473 };
474
475 let parse_address = |inp| {
476 let (inp, address) = context("address", fail_err(be_u16))(inp)?;
477 if address == 0 {
478 Err(Err::Error(E::from_error_kind(inp, ErrorKind::Complete)))
479 }
480 else {
481 let (inp, (len, offset)) = fail_err(tuple(
482 (context("data length", be_u16), offset_to)
483 ))( inp )?;
484 Ok((inp, AySongBlock {
485 address, data: ay_blob(offset, len.into())?
486 }))
487 }
488 };
489
490 let parse_song_data = |data_offs| {
491 parse_at(data_offs,
492 context("song data",
493 map(many1(parse_address), |vec| vec.into_boxed_slice()))
494 )( raw ).map(|(_,s)| s)
495 };
496
497 let parse_song_info = |info_offs, name: AyString| {
498 let (_, (chan_a, chan_b, chan_c, noise,
499 song_duration, fade_duration,
500 hi_reg, lo_reg,
501 init_offs, data_offs)) = parse_at(info_offs, context("missing song metadata", tuple(
502 (be_u8, be_u8, be_u8, be_u8,
503 be_u16, be_u16,
504 be_u8, be_u8,
505 offset_to, offset_to))
506 ))( raw )?;
507
508 let (_, (stack, init, interrupt)) = parse_at(init_offs, context("play routines",
509 tuple((context("stack", be_u16), be_u16, be_u16)))
510 )( raw )?;
511 let blocks = parse_song_data(data_offs)?;
512 Ok(AySong {
513 name, chan_a, chan_b, chan_c, noise,
514 song_duration, fade_duration,
515 hi_reg, lo_reg,
516 stack, init, interrupt,
517 blocks
518 })
519 };
520
521 let parse_song = |inp| {
522 let (inp, (name_offs, info_offs)) = tuple((maybe_offs_to, offset_to))(inp)?;
523 let name = ay_string("song name", name_offs)?;
524 let song = parse_song_info(info_offs, name)?;
525 Ok((inp, song))
526 };
527
528 let parse_songs = |songs_offs, num| {
529 parse_at(songs_offs, |inp| {
530 let mut it = iterator(inp, parse_song);
531 let songs = it.take(num).collect::<Box<[_]>>();
532 let (_,_) = it.finish()?;
533 Ok(songs)
534 })( raw )
535 };
536
537
538 let (_, (
539 _tag1, _tag2, file_version, player_version,
540 special_player_offs, author_offs, misc_offs,
541 num_of_songs, first_song,
542 songs_offs)
543 ) = context("missing header parts",
544 tuple((
545 context("tag ZXAY", tag(b"ZXAY")),
546 context("tag ZXAY", tag(b"EMUL")),
547 context("file version", be_u8),
548 context("player version", be_u8),
549 context("spec. player", maybe_offs_to),
550 context("author offs.", maybe_offs_to),
551 context("misc. offs.", maybe_offs_to),
552 context("num. of songs", be_u8),
553 context("firs song index", be_u8),
554 context("songs offs.", offset_to)
555 ))
556 )( raw )?;
557 let special_player = special_player_offs.is_some();
558 let author = ay_string("author", author_offs)?;
559 let misc = ay_string("misc", misc_offs)?;
560 let songs = parse_songs(songs_offs, num_of_songs as usize + 1)?;
561 let meta = AyMeta {
562 file_version, player_version,
563 special_player, author, misc, first_song
564 };
565
566 Ok((meta, songs))
567}
568
569#[derive(Clone, Debug, PartialEq, Eq)]
571pub struct AyParseError {
572 pub data: Box<[u8]>,
574 pub description: String,
576}
577
578impl std::error::Error for AyParseError {}
579impl fmt::Display for AyParseError {
580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581 write!(f, "{}", self.description)
582 }
583}
584
585impl From<(Box<[u8]>, String)> for AyParseError {
586 fn from((data, description): (Box<[u8]>, String)) -> Self {
587 AyParseError { data, description }
588 }
589}
590
591
592pub fn parse_ay<B: Into<Box<[u8]>>>(
597 data: B
598 ) -> Result<PinAyFile, AyParseError>
599{
600 let raw: Box<_> = data.into();
601 let res = parse_ay_raw::<VerboseBytesError>(&raw)
602 .map_err(|e|
603 e.unwrap().describe(&raw)
604 );
605
606 let (meta, songs) = match res {
607 Ok(ok) => ok,
608 Err(err) => return Err(AyParseError::from((raw, err)))
609 };
610
611 Ok(Box::pin(AyFile {
612 raw, meta, songs,
613 _pin: PhantomPinned
614 }))
615}
616
617pub fn read_ay<R: io::Read>(
624 mut rd: R
625 ) -> io::Result<PinAyFile>
626{
627 let mut data = Vec::new();
628 rd.read_to_end(&mut data)?;
629 parse_ay(data).map_err(|e|
630 io::Error::new(io::ErrorKind::InvalidData, e)
631 )
632}
633
634pub trait IoErrorExt: Sized {
636 fn is_ay_parse(&self) -> bool {
637 self.ay_parse_ref().is_some()
638 }
639 fn into_ay_parse(self) -> Option<Box<AyParseError>>;
640 fn ay_parse_ref(&self) -> Option<&AyParseError>;
641 fn ay_parse_mut(&mut self) -> Option<&mut AyParseError>;
642 fn into_ay_parse_data(self) -> Option<Box<[u8]>> {
643 self.into_ay_parse().map(|ay| ay.data)
644 }
645}
646
647impl IoErrorExt for io::Error {
648 fn into_ay_parse(self) -> Option<Box<AyParseError>> {
649 if let Some(inner) = self.into_inner() {
650 if let Ok(ay_err) = inner.downcast::<AyParseError>() {
651 return Some(ay_err)
652 }
653 }
654 None
655 }
656 fn ay_parse_ref(&self) -> Option<&AyParseError> {
657 if let Some(inner) = self.get_ref() {
658 if let Some(ay_err) = inner.downcast_ref::<AyParseError>() {
659 return Some(ay_err)
660 }
661 }
662 None
663 }
664 fn ay_parse_mut(&mut self) -> Option<&mut AyParseError> {
665 if let Some(inner) = self.get_mut() {
666 if let Some(ay_err) = inner.downcast_mut::<AyParseError>() {
667 return Some(ay_err)
668 }
669 }
670 None
671 }
672}
673