summaryrefslogtreecommitdiffstats
path: root/src/xcursor.rs
blob: ff6c3d6393c792572b65d24e3ea5bee04a4289b4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#[derive(Debug, Clone)]
pub struct ImageInfo {
  pub r#type: u32,
  pub subtype: u32,
  pub width: u32,
  pub height: u32,
  pub xhot: u32,
  pub yhot: u32,
  pub delay: u32,
  pub data: Vec<u8>,
}

pub struct XCursorEncoder {
  pub images: Vec<ImageInfo>,
}

const MAGIC: [u8; 4] = [0x58, 0x63, 0x75, 0x72];
const IMAGE_HEADER: [u8; 4] = [0x02, 0x00, 0xfd, 0xff];

impl XCursorEncoder {
  pub fn new(images: Vec<ImageInfo>) -> Self {
    XCursorEncoder { images }
  }

  fn image_pos(&self, position: usize) -> usize {
    let mut val = 12 * self.images.len() + 16;
    for n in 0..position {
      val += 36 + ((self.images[n].width as usize) * (self.images[n].height as usize) * 4)
    }
    val
  }

  pub fn pack(&mut self) -> Vec<u8> {
    fn insert_bytes(data: &mut Vec<u8>, new_data: impl IntoIterator<Item = u8>) {
      for int in new_data {
        data.push(int);
      }
    }
    fn insert_int(data: &mut Vec<u8>, int: u32) {
      data.push((int & 0xff) as u8);
      data.push(((int >> 8) & 0xff) as u8);
      data.push(((int >> 16) & 0xff) as u8);
      data.push(((int >> 24) & 0xff) as u8);
    }

    let mut data: Vec<u8> = Vec::new();

    // File Header
    {
      // MAGIC string ("Xcur")
      insert_bytes(&mut data, MAGIC);
      // CARD32 bytes in this header
      insert_int(&mut data, 16);
      // CARD32 file version
      insert_int(&mut data, 1);
      // CARD32 number of toc entries
      insert_int(&mut data, self.images.len().try_into().unwrap());
    }

    // ntoc entries
    {
      let mut img_idx: usize = 0;
      for img in self.images.clone().into_iter() {
        // Some header
        insert_bytes(&mut data, IMAGE_HEADER);
        // CARD32 type-specific label - size for images
        insert_int(&mut data, img.r#type);
        // CARD32 absolute byte position of table in file
        insert_int(&mut data, self.image_pos(img_idx).try_into().unwrap());

        img_idx += 1;
      }
    }

    // images
    {
      for img in self.images.clone().into_iter() {
        // Header Size (36)
        insert_int(&mut data, 36);
        // Image Type
        insert_bytes(&mut data, IMAGE_HEADER);
        // Subtype, for nominal size
        insert_int(&mut data, img.subtype);
        // Version
        insert_int(&mut data, 1);
        // Image dimensions
        insert_int(&mut data, img.width);
        insert_int(&mut data, img.height);
        // Cursor positioning
        insert_int(&mut data, img.xhot);
        insert_int(&mut data, img.yhot);
        // Milliseconds till next frame
        insert_int(&mut data, img.delay);
        // Raw image data
        insert_bytes(&mut data, img.data);
      }
    }

    data
  }
}