1use super::{ByteInput as Input, cbv::cstr, prelude::*};
4
5use super::cbh::optional;
6pub use super::{
7 cbh::{Date, le_date},
8 cbp::{Header, header},
9};
10
11#[derive(Clone, Debug)]
12pub struct Record {
13 pub title: String,
14 pub place: String,
15 pub date: Date,
16 pub mode: Option<Mode>,
17 pub speed: Option<Speed>,
18 pub nation: Option<u8>,
19 pub category: Option<u8>,
20 pub rounds: Option<u8>,
21 pub games: usize,
22 pub first_game: usize,
23}
24
25pub fn record(input: &mut Input<'_>) -> ModalResult<Record> {
26 let _what = be_u16.parse_next(input)?;
27 let _ = take(7u8).parse_next(input)?;
34
35 let title = take(40u8).map(cstr).parse_next(input)?;
36 let place = take(30u8).map(cstr).parse_next(input)?;
37
38 let date = le_date.parse_next(input)?;
39
40 let _byte82 = u8.parse_next(input)?;
41 let (mode, speed) = u8.map(byte83).parse_next(input)?;
42
43 let _byte84 = u8.parse_next(input)?;
44 let nation = u8.map(optional).parse_next(input)?;
45
46 let _byte86 = u8.parse_next(input)?;
47 let category = u8.verify(|x| *x < 100).map(optional).parse_next(input)?;
48
49 let _byte88 = u8.parse_next(input)?;
50 let rounds = u8.map(optional).parse_next(input)?;
51 let _byte90 = u8.parse_next(input)?;
52 let games = le_u32.parse_next(input)? as usize;
53 let first_game = le_u32.parse_next(input)? as usize;
54
55 Ok(Record {
56 title,
57 place,
58 date,
59 mode,
60 speed: Some(speed),
61 nation,
62 category,
63 rounds,
64 games,
65 first_game,
66 })
67}
68
69#[repr(u8)]
70#[derive(Clone, Copy, Debug)]
71pub enum Mode {
72 Game = 1,
73 Match = 2,
74 Tournament = 3,
75 Swiss = 4,
76 Team = 5,
77 Knockout = 6,
78 Simultaneous = 7,
79 Scheveningen = 8,
80}
81
82#[repr(u8)]
83#[derive(Clone, Copy, Debug)]
84pub enum Speed {
85 Normal,
86 Blitz,
87 Rapid,
88 Correspondence,
89}
90
91fn byte83(byte: u8) -> (Option<Mode>, Speed) {
92 let mode = byte & 0b11111;
93 let mode = if mode == 0 {
94 None
95 } else {
96 use Mode::*;
97 Some(match mode {
98 1 => Game,
99 2 => Match,
100 3 => Tournament,
101 4 => Swiss,
102 5 => Team,
103 6 => Knockout,
104 7 => Simultaneous,
105 8 => Scheveningen,
106 _ => unreachable!(),
107 })
108 };
109 let blitz = (byte >> 5) & 1 != 0;
110 let rapid = (byte >> 6) & 1 != 0;
111 let correspondence = (byte >> 7) & 1 != 0;
112 assert!(blitz as u8 + rapid as u8 + correspondence as u8 <= 1);
113 let mut speed = Speed::Normal;
114 if blitz {
115 speed = Speed::Blitz
116 };
117 if rapid {
118 speed = Speed::Rapid
119 };
120 if correspondence {
121 speed = Speed::Correspondence
122 };
123
124 (mode, speed)
125}
126
127#[derive(Clone, Debug)]
128pub struct Tournaments {
129 pub header: Header,
130 pub records: Vec<Record>,
131}
132
133pub fn tournaments(input: &mut Input<'_>) -> ModalResult<Tournaments> {
134 let header = header.parse_next(input)?;
135 assert_eq!(header.record_len + 9, 99);
136 let records: Vec<Record> = repeat(..=header.len, record).parse_next(input)?;
137 Ok(Tournaments { header, records })
138}
139
140#[test]
141fn example() {
142 let input = include_bytes!("../../examples/twic1616.cbt");
143 let header = header.parse_next(&mut input.as_slice()).expect("can parse CBT header");
144 println!("{header:?}");
145 let tournaments =
146 tournaments.parse(input.as_slice()).map_err(drop).expect("can parse CBT file");
147 println!("{:?}", tournaments.header);
148 for record in tournaments.records.iter().take(5) {
149 println!("{record:?}");
150 }
151 for record in tournaments.records.iter().rev().take(5) {
152 println!("{record:?}");
153 }
154}