mirror of
https://git.eta.st/eta/rsp6-decoder.git
synced 2024-12-01 05:55:38 +00:00
additional fields
This commit is contained in:
parent
11b41c3281
commit
1bb6e5cff8
@ -91,7 +91,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
eprintln!("[+] checksum ok");
|
eprintln!("[+] checksum ok");
|
||||||
}
|
}
|
||||||
let ticket = Rsp6Ticket::decode(unpadded)?;
|
let ticket = Rsp6Ticket::decode(unpadded, issuer_id.into())?;
|
||||||
serde_json::to_writer_pretty(std::io::stdout(), &ticket)?;
|
serde_json::to_writer_pretty(std::io::stdout(), &ticket)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,6 +17,13 @@ pub enum CouponType {
|
|||||||
ReturnInbound = 3,
|
ReturnInbound = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize)]
|
||||||
|
pub enum DepartTimeFlag {
|
||||||
|
Mystery = 1,
|
||||||
|
Specific = 2,
|
||||||
|
Suggested = 3,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct TicketPurchaseDetails {
|
pub struct TicketPurchaseDetails {
|
||||||
pub purchase_time: PrimitiveDateTime,
|
pub purchase_time: PrimitiveDateTime,
|
||||||
@ -32,6 +39,7 @@ pub struct Reservation {
|
|||||||
pub seat_number: u8,
|
pub seat_number: u8,
|
||||||
pub seat_letter: Option<char>,
|
pub seat_letter: Option<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reservation {
|
impl Reservation {
|
||||||
pub fn decode(resv: &BitSlice<u8, Msb0>) -> anyhow::Result<Self> {
|
pub fn decode(resv: &BitSlice<u8, Msb0>) -> anyhow::Result<Self> {
|
||||||
if resv.len() != 45 {
|
if resv.len() != 45 {
|
||||||
@ -57,6 +65,7 @@ impl Reservation {
|
|||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct Rsp6Ticket {
|
pub struct Rsp6Ticket {
|
||||||
pub manually_inspect: bool,
|
pub manually_inspect: bool,
|
||||||
|
pub issuer_id: String,
|
||||||
pub ticket_reference: String,
|
pub ticket_reference: String,
|
||||||
pub checksum: char,
|
pub checksum: char,
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
@ -71,7 +80,7 @@ pub struct Rsp6Ticket {
|
|||||||
pub discount_code: u16,
|
pub discount_code: u16,
|
||||||
pub route_code: u32,
|
pub route_code: u32,
|
||||||
pub start_date: Date,
|
pub start_date: Date,
|
||||||
pub depart_time_flag: u8,
|
pub depart_time_flag: Option<DepartTimeFlag>,
|
||||||
pub depart_time: Time,
|
pub depart_time: Time,
|
||||||
pub passenger_id: Option<String>,
|
pub passenger_id: Option<String>,
|
||||||
pub passenger_name: Option<String>,
|
pub passenger_name: Option<String>,
|
||||||
@ -82,6 +91,9 @@ pub struct Rsp6Ticket {
|
|||||||
pub purchase_details: Option<TicketPurchaseDetails>,
|
pub purchase_details: Option<TicketPurchaseDetails>,
|
||||||
pub reservations: Vec<Reservation>,
|
pub reservations: Vec<Reservation>,
|
||||||
pub free_text: Option<String>,
|
pub free_text: Option<String>,
|
||||||
|
pub osi_nlc: Option<String>,
|
||||||
|
pub mystery_flag: bool,
|
||||||
|
pub mystery_header: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rsp6Ticket {
|
impl Rsp6Ticket {
|
||||||
@ -132,10 +144,12 @@ impl Rsp6Ticket {
|
|||||||
Some(format!("{}{:04}", prefix, (id % 10000)))
|
Some(format!("{}{:04}", prefix, (id % 10000)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(tkt: &[u8]) -> anyhow::Result<Self> {
|
pub fn decode(tkt: &[u8], issuer_id: String) -> anyhow::Result<Self> {
|
||||||
let bit_tkt = tkt.view_bits::<Msb0>();
|
let bit_tkt = tkt.view_bits::<Msb0>();
|
||||||
|
|
||||||
let manually_inspect = bit_tkt[0];
|
let manually_inspect = bit_tkt[0];
|
||||||
|
let mystery_header: u8 = bit_tkt[1..8].load_be();
|
||||||
|
let mystery_header = format!("{:07b}", mystery_header);
|
||||||
let ticket_reference = Self::base64(tkt, 8, 62);
|
let ticket_reference = Self::base64(tkt, 8, 62);
|
||||||
let checksum = Self::base64(tkt, 62, 68).chars().next().unwrap();
|
let checksum = Self::base64(tkt, 62, 68).chars().next().unwrap();
|
||||||
let version: u8 = bit_tkt[68..72].load_be();
|
let version: u8 = bit_tkt[68..72].load_be();
|
||||||
@ -162,18 +176,29 @@ impl Rsp6Ticket {
|
|||||||
let start_time_secs: u32 = bit_tkt[225..236].load_be();
|
let start_time_secs: u32 = bit_tkt[225..236].load_be();
|
||||||
let start_time: PrimitiveDateTime =
|
let start_time: PrimitiveDateTime =
|
||||||
CapitalismDateTime::new(start_time_days, start_time_secs).into();
|
CapitalismDateTime::new(start_time_days, start_time_secs).into();
|
||||||
let depart_time_flag: u8 = bit_tkt[236..238].load_be();
|
let depart_time_flag = match bit_tkt[236..238].load_be::<u8>() {
|
||||||
|
0 => None,
|
||||||
|
1 => Some(DepartTimeFlag::Mystery),
|
||||||
|
2 => Some(DepartTimeFlag::Specific),
|
||||||
|
3 => Some(DepartTimeFlag::Suggested),
|
||||||
|
_ => unreachable!(), // only 2-bit int
|
||||||
|
};
|
||||||
let depart_time = start_time.time();
|
let depart_time = start_time.time();
|
||||||
let start_date = start_time.date();
|
let start_date = start_time.date();
|
||||||
|
|
||||||
let passenger_id = Self::decode_passenger_id(bit_tkt[238..255].load_be());
|
let passenger_id = Self::decode_passenger_id(bit_tkt[238..255].load_be());
|
||||||
let passenger_name = Self::base64(tkt, 255, 327);
|
let passenger_name = Self::base64(tkt, 255, 327);
|
||||||
let passenger_name = (!passenger_name.trim().is_empty()).then_some(passenger_name);
|
let passenger_name =
|
||||||
|
(!passenger_name.trim().is_empty()).then(|| passenger_name.trim().to_owned());
|
||||||
let passenger_gender: u8 = bit_tkt[327..329].load_be();
|
let passenger_gender: u8 = bit_tkt[327..329].load_be();
|
||||||
let passenger_gender = (passenger_gender != 0).then_some(passenger_gender);
|
let passenger_gender = (passenger_gender != 0).then_some(passenger_gender);
|
||||||
|
|
||||||
let restriction_code = Self::base64(tkt, 329, 347);
|
let restriction_code = Self::base64(tkt, 329, 347);
|
||||||
let restriction_code = (!restriction_code.trim().is_empty()).then_some(restriction_code);
|
let osi_nlc = Self::base64(tkt, 347, 371);
|
||||||
|
let osi_nlc = (!osi_nlc.trim().is_empty()).then(|| osi_nlc.trim().to_owned());
|
||||||
|
let mystery_flag = bit_tkt[371];
|
||||||
|
let restriction_code =
|
||||||
|
(!restriction_code.trim().is_empty()).then(|| restriction_code.trim().to_owned());
|
||||||
let bidirectional = bit_tkt[372];
|
let bidirectional = bit_tkt[372];
|
||||||
let limited_duration = Self::decode_limited_duration(bit_tkt[379..383].load_be());
|
let limited_duration = Self::decode_limited_duration(bit_tkt[379..383].load_be());
|
||||||
|
|
||||||
@ -186,8 +211,8 @@ impl Rsp6Ticket {
|
|||||||
CapitalismDateTime::new(purchase_time_days, purchase_time_secs).into();
|
CapitalismDateTime::new(purchase_time_days, purchase_time_secs).into();
|
||||||
let price_pence: u32 = bit_tkt[415..436].load_be();
|
let price_pence: u32 = bit_tkt[415..436].load_be();
|
||||||
let purchase_reference = Self::base64(tkt, 449, 497);
|
let purchase_reference = Self::base64(tkt, 449, 497);
|
||||||
let purchase_reference =
|
let purchase_reference = (!purchase_reference.trim().is_empty())
|
||||||
(!purchase_reference.trim().is_empty()).then_some(purchase_reference);
|
.then(|| purchase_reference.trim().to_owned());
|
||||||
let mut days_of_validity = bit_tkt[497..506].load_be();
|
let mut days_of_validity = bit_tkt[497..506].load_be();
|
||||||
if days_of_validity == 0 {
|
if days_of_validity == 0 {
|
||||||
days_of_validity = 1;
|
days_of_validity = 1;
|
||||||
@ -212,8 +237,22 @@ impl Rsp6Ticket {
|
|||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
let has_free_text = bit_tkt[385];
|
||||||
|
let free_text_is_extended = bit_tkt[383];
|
||||||
|
let mut free_text = None;
|
||||||
|
if has_free_text {
|
||||||
|
let reservations_end = reservations_start + (45 * reservations_count as usize);
|
||||||
|
let end = if free_text_is_extended { 863 } else { 783 };
|
||||||
|
let length = 6 * ((end - reservations_end) / 6);
|
||||||
|
let text = Self::base64(tkt, reservations_end, reservations_end + length);
|
||||||
|
if !text.trim().is_empty() {
|
||||||
|
free_text = Some(text.trim().to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
manually_inspect,
|
manually_inspect,
|
||||||
|
issuer_id,
|
||||||
ticket_reference,
|
ticket_reference,
|
||||||
checksum,
|
checksum,
|
||||||
version,
|
version,
|
||||||
@ -238,7 +277,10 @@ impl Rsp6Ticket {
|
|||||||
limited_duration,
|
limited_duration,
|
||||||
purchase_details,
|
purchase_details,
|
||||||
reservations,
|
reservations,
|
||||||
free_text: None,
|
free_text,
|
||||||
|
osi_nlc,
|
||||||
|
mystery_flag,
|
||||||
|
mystery_header,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user