Skip to main content

ygopro_data/data/
query.rs

1//! Card-position and query structures used by game messages.
2//!
3//! Provides [`CardPosition`] and [`InfoLocation`], which describe where a card sits on
4//! the field.
5
6use std::io::prelude::Read;
7use std::io::prelude::Write;
8use std::io::prelude::Seek;
9
10use binrw::BinRead;
11use binrw::BinReaderExt;
12use binrw::BinWrite;
13use binrw::VecArgs;
14
15use crate::constants::*;
16use crate::message::game_message::CardCode;
17use crate::message::game_message::GameMessage;
18use ygopro_derive::GameMessage;
19
20#[derive(BinRead, BinWrite, Clone, Debug, GameMessage)]
21pub struct CardPosition<const CODE: bool, const SUB_SEQUENCE: bool, const DESCRIPTION: bool> {
22    #[brw(if(CODE))]
23    #[mask]
24    #[mask_if(self.controller != player)]
25    pub code: CardCode,
26    pub controller: CorePlayer,
27    pub location: Location,
28    pub sequence: i8,
29    #[brw(if(SUB_SEQUENCE))]
30    pub sub_sequence: i8,
31    #[brw(if(DESCRIPTION))]
32    pub description: i32
33}
34
35// In ygopro, these fields are packed as a u32.
36// We don't need pack them.
37#[derive(BinRead, BinWrite, Debug, Copy, Clone, PartialEq, Eq)]
38pub struct InfoLocation {
39    pub controller: CorePlayer,
40    pub location: Location,
41    pub sequence: u8,
42    #[br(if(!location.contains(Location::Overlay), Position::Any))]
43    #[bw(if(!location.contains(Location::Overlay)))]
44    pub position: Position,
45    #[brw(if(location.contains(Location::Overlay)))]
46    pub overlay_sequence: u8
47}
48
49impl InfoLocation {
50    pub fn should_mask(&self) -> bool {
51        if self.position.intersects(Position::Reveal) {
52            return false;
53        }
54        if self.location.contains(Location::Hand) {
55            !self.position.intersects(Position::Faceup)
56        } else {
57            self.position.intersects(Position::Facedown)
58        }
59    }
60}
61
62#[derive(Clone, Debug)]
63pub enum QueryData {
64    Clear,
65    Code(i32),
66    Position(InfoLocation),
67    Alias(i32),
68    Type(Type),
69    Level(i32),
70    Rank(i32),
71    Attribute(Attribute),
72    Race(Race),
73    Attack(i32),
74    Defense(i32),
75    BaseAttack(i32),
76    BaseDefense(i32),
77    Reason(Reason),
78    ReasonCard(i32),
79    EquipCard(CardPosition<false, true, false>),
80    TargetCard(Vec<CardPosition<false, true, false>>),
81    OverlayCard(Vec<u32>),
82    Counters(Vec<(u16, u16)>),
83    Owner(CorePlayer),
84    Status(Status),
85    LeftScale(i32),
86    RightScale(i32),
87    Link(i32, Linkmarkers)
88}
89
90pub(crate) struct QueryDatas(Vec<QueryData>);
91
92impl BinRead for QueryDatas {
93    type Args<'a> = ();
94
95    fn read_options<R: Read + Seek>(reader: &mut R, endian: binrw::Endian, _: Self::Args<'_>,) -> binrw::prelude::BinResult<Self> {
96        let query = Query::read_options(reader, endian, ())?;
97        let mut query_datas = Vec::new();
98        if query.is_empty() { query_datas.push(QueryData::Clear); }
99        if query.contains(Query::Code) { query_datas.push(QueryData::Code(i32::read_options(reader, endian, ())?)); }
100        if query.contains(Query::Position) { 
101            query_datas.push(QueryData::Position(InfoLocation::read_options(reader, endian, ())?));
102        }
103        if query.contains(Query::Alias)       { query_datas.push(QueryData::Alias(i32::read_options(reader,           endian, ())?)); }
104        if query.contains(Query::Type)        { query_datas.push(QueryData::Type(Type::read_options(reader,           endian, ())?)); }
105        if query.contains(Query::Level)       { query_datas.push(QueryData::Level(i32::read_options(reader,           endian, ())?)); }
106        if query.contains(Query::Rank)        { query_datas.push(QueryData::Rank(i32::read_options(reader,            endian, ())?)); }
107        if query.contains(Query::Attribute)   { query_datas.push(QueryData::Attribute(Attribute::read_options(reader, endian, ())?)); }
108        if query.contains(Query::Race)        { query_datas.push(QueryData::Race(Race::read_options(reader,           endian, ())?)); }
109        if query.contains(Query::Attack)      { query_datas.push(QueryData::Attack(i32::read_options(reader,          endian, ())?)); }
110        if query.contains(Query::Defense)     { query_datas.push(QueryData::Defense(i32::read_options(reader,         endian, ())?)); }
111        if query.contains(Query::BaseAttack)  { query_datas.push(QueryData::BaseAttack(i32::read_options(reader,      endian, ())?)); }
112        if query.contains(Query::BaseDefense) { query_datas.push(QueryData::BaseDefense(i32::read_options(reader,     endian, ())?)); }
113        if query.contains(Query::Reason)      { query_datas.push(QueryData::Reason(Reason::read_options(reader,       endian, ())?)); }
114        if query.contains(Query::ReasonCard)  { query_datas.push(QueryData::ReasonCard(i32::read_options(reader,      endian, ())?)); }
115        if query.contains(Query::EquipCard)   { query_datas.push(QueryData::EquipCard(CardPosition::<false,true,false>::read_options(reader, endian, ())?)); }
116        if query.contains(Query::TargetCard) {
117            let count = u32::read_options(reader, endian, ())? as usize;
118            query_datas.push(QueryData::TargetCard(Vec::<CardPosition<false, true, false>>::read_options(reader, endian, VecArgs { count, inner: () })?));
119        }
120        if query.contains(Query::OverlayCard) {
121            let count = u32::read_options(reader, endian, ())? as usize;
122            query_datas.push(QueryData::OverlayCard(Vec::<u32>::read_options(reader, endian, VecArgs { count, inner: () })?)); 
123        }
124        if query.contains(Query::Counters) { 
125            let count = u32::read_options(reader, endian, ())? as usize;
126            query_datas.push(QueryData::Counters(Vec::<(u16, u16)>::read_options(reader, endian, VecArgs { count, inner: () })?)); 
127        }
128        if query.contains(Query::Owner) { 
129            query_datas.push(QueryData::Owner(CorePlayer::read_options(reader, endian, ())?)); 
130            reader.read_le::<[u8; 3]>()?; // padding 3 bytes
131        }
132        if query.contains(Query::Status)     { query_datas.push(QueryData::Status(Status::read_options(reader,  endian, ())?)); }
133        if query.contains(Query::LeftScale)  { query_datas.push(QueryData::LeftScale(i32::read_options(reader,  endian, ())?)); }
134        if query.contains(Query::RightScale) { query_datas.push(QueryData::RightScale(i32::read_options(reader, endian, ())?)); }
135        if query.contains(Query::Link)       { query_datas.push(QueryData::Link(i32::read_options(reader, endian, ())?, Linkmarkers::read_options(reader, endian, ())?)); }
136        Ok(QueryDatas(query_datas))
137    }
138}
139
140impl<'a> From<&'a QueryData> for Query {
141    fn from(value: &'a QueryData) -> Self {
142        match value {
143            QueryData::Clear          => Query::empty(),
144            QueryData::Code(_)        => Query::Code,
145            QueryData::Position(_)    => Query::Position,
146            QueryData::Alias(_)       => Query::Alias,
147            QueryData::Type(_)        => Query::Type,
148            QueryData::Level(_)       => Query::Level,
149            QueryData::Rank(_)        => Query::Rank,
150            QueryData::Attribute(_)   => Query::Attribute,
151            QueryData::Race(_)        => Query::Race,
152            QueryData::Attack(_)      => Query::Attack,
153            QueryData::Defense(_)     => Query::Defense,
154            QueryData::BaseAttack(_)  => Query::BaseAttack,
155            QueryData::BaseDefense(_) => Query::BaseDefense,
156            QueryData::Reason(_)      => Query::Reason,
157            QueryData::ReasonCard(_)  => Query::ReasonCard,
158            QueryData::EquipCard(_)   => Query::EquipCard,
159            QueryData::TargetCard(_)  => Query::TargetCard,
160            QueryData::OverlayCard(_) => Query::OverlayCard,
161            QueryData::Counters(_)    => Query::Counters,
162            QueryData::Owner(_)       => Query::Owner,
163            QueryData::Status(_)      => Query::Status,
164            QueryData::LeftScale(_)   => Query::LeftScale,
165            QueryData::RightScale(_)  => Query::RightScale,
166            QueryData::Link(_, _)        => Query::Link,
167        }
168    }
169}
170
171#[derive(Clone, Debug)]
172pub enum UpdateCardInfo {
173    Empty,
174    Data(Vec<QueryData>)
175}
176
177impl GameMessage for UpdateCardInfo {
178    fn mask(&mut self) {
179        if !self.should_mask(CorePlayer::None) { return }
180        if let UpdateCardInfo::Data(data) = self {
181            data.fill(QueryData::Clear);
182        }
183    }
184
185    fn should_mask(&self, _player: CorePlayer) -> bool {
186        let data = match self {
187            UpdateCardInfo::Data(data) => data,
188            _ => return false
189        };
190        data.iter().find_map(|q| if let QueryData::Position(p) = q { 
191            Some(p.should_mask()) 
192        } else { None }).unwrap_or(false)
193    }
194}
195
196impl BinRead for UpdateCardInfo {
197    type Args<'a> = ();
198
199    fn read_options<R: Read + Seek>(reader: &mut R, endian: binrw::Endian, _: Self::Args<'_>) -> binrw::prelude::BinResult<Self> {
200        let len = u32::read_options(reader, endian, ())?;
201        if len == 4 { return Ok(UpdateCardInfo::Empty); }
202        let pos = reader.stream_position()?;
203        let datas = QueryDatas::read_options(reader, endian, ())?;
204        reader.seek(std::io::SeekFrom::Start(pos + len as u64 - 4))?;
205        Ok(UpdateCardInfo::Data(datas.0))
206    }
207}
208
209impl BinWrite for UpdateCardInfo {
210    type Args<'a> = ();
211
212    fn write_options<W: Write + Seek>(&self, writer: &mut W, endian: binrw::Endian, _: Self::Args<'_>) -> binrw::prelude::BinResult<()> {
213        let args = ();
214        let queries = match self {
215            UpdateCardInfo::Empty => return u32::write_options(&4, writer, endian, args),
216            UpdateCardInfo::Data(data) => data
217        };
218        let flag: u32 = queries.iter().map(|query| Query::from(query).bits()).sum();
219        let mut len = 0u32;
220        let pos = writer.stream_position()?;
221        len.write_options(writer, endian, ())?;
222        flag.write_options(writer, endian, ())?;
223        for query in queries {
224            match query {
225                QueryData::Clear                => (),
226                QueryData::Code(code)           => code.write_options(writer,     endian, args)?,
227                QueryData::Position(info_location)   => info_location.write_options(writer, endian, args)?,
228                QueryData::Alias(alias)         => alias.write_options(writer, endian, args)?,
229                QueryData::Type(_type)          => _type.write_options(writer, endian, args)?,
230                QueryData::Level(level)         => level.write_options(writer, endian, args)?,
231                QueryData::Rank(rank)           => rank.write_options(writer, endian, args)?,
232                QueryData::Attribute(attribute) => attribute.write_options(writer, endian, args)?,
233                QueryData::Race(race)           => race.write_options(writer, endian, args)?,
234                QueryData::Attack(attack)       => attack.write_options(writer, endian, args)?,
235                QueryData::Defense(defense)     => defense.write_options(writer, endian, args)?,
236                QueryData::BaseAttack(attack)      => attack.write_options(writer, endian, args)?,
237                QueryData::BaseDefense(defense)     => defense.write_options(writer, endian, args)?,
238                QueryData::Reason(reason)       => reason.write_options(writer, endian, args)?,
239                QueryData::ReasonCard(card)     => card.write_options(writer, endian, args)?,
240                QueryData::EquipCard(card)      => card.write_options(writer, endian, args)?,
241                QueryData::TargetCard(cards)    => {
242                    let len = cards.len() as u32;
243                    len.write_options(writer, endian, args)?;
244                    cards.write_options(writer, endian, args)?;
245                },
246                QueryData::OverlayCard(cards)   => {
247                    let len = cards.len() as u32;
248                    len.write_options(writer, endian, args)?;
249                    cards.write_options(writer, endian, args)?;
250                },
251                QueryData::Counters(counters)   => {
252                    let len = counters.len() as u32;
253                    len.write_options(writer, endian, args)?;
254                    counters.write_options(writer, endian, args)?;
255                },
256                QueryData::Owner(owner)         => {
257                    owner.write_options(writer, endian, args)?;
258                    [0u8; 3].write_options(writer, endian, args)?;
259                },
260                QueryData::Status(status)       => status.write_options(writer, endian, args)?,
261                QueryData::LeftScale(scale)     => scale.write_options(writer, endian, args)?,
262                QueryData::RightScale(scale)    => scale.write_options(writer, endian, args)?,
263                QueryData::Link(link, linkmarkers) => {
264                    link.write_options(writer, endian, args)?;
265                    linkmarkers.write_options(writer, endian, args)?
266                },
267            }
268        }
269        let current_pos = writer.stream_position()?;
270        len = u32::try_from(current_pos - pos).map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
271        writer.seek(std::io::SeekFrom::Current(-(len as i64)))?;
272        len.write_options(writer, endian, args)?;
273        writer.seek(std::io::SeekFrom::Current((len - 4) as i64))?;
274        Ok(())
275    }
276}
277
278mod test {
279    #![allow(unused_imports)]
280
281    use std::io::Cursor;
282    use binrw::{BinRead, BinWrite, VecArgs};
283    use crate::data::UpdateCardInfo;
284
285    #[test]
286    fn test_deserialize_query() {
287        let arr = vec![16, 0, 0, 0, 3, 0, 0, 0, 122, 178, 159, 1, 1, 2, 0, 10, 16, 0, 0, 0, 3, 0, 0, 0, 17, 231, 97, 3, 1, 2, 1, 10, 16, 0, 0, 0, 3, 0, 0, 0, 59, 73, 201, 5, 1, 2, 2, 10, 16, 0, 0, 0, 3, 0, 0, 0, 239, 39, 81, 0, 1, 2, 3, 10, 16, 0, 0, 0, 3, 0, 0, 0, 143, 77, 182, 3, 1, 2, 4, 1];
288        let re: Vec<u8> = vec![16, 0, 0, 0, 3, 0, 0, 0, 122, 178, 159, 1, 1, 2, 0, 10, 16, 0, 0, 0, 3, 0, 0, 0, 17, 231, 97, 3, 1, 2, 1, 10, 16, 0, 0, 0, 3, 0, 0, 0, 59, 73, 201, 5, 1, 2, 2, 10, 16, 0, 0, 0, 3, 0, 0, 0, 239, 39, 81, 0, 1, 2, 3, 10, 16, 0, 0, 0, 3, 0, 0, 0, 143, 77, 182, 3, 1, 2, 4, 1];
289        let mut reader = Cursor::new(arr);
290        let replay = Vec::<UpdateCardInfo>::read_le_args(&mut reader, VecArgs {count: 5, inner: {}}).unwrap();
291        let mut writer = Cursor::new(Vec::new());
292        replay.write_le(&mut writer).unwrap();
293        let result = writer.into_inner();
294        assert_eq!(result, re, "round-trip mismatch");
295    }
296}