diff --git a/src/audio/mod.rs b/src/audio/mod.rs
new file mode 100644
index 0000000..06acc81
--- /dev/null
+++ b/src/audio/mod.rs
@@ -0,0 +1 @@
+pub mod mp3;
\ No newline at end of file
diff --git a/src/audio/mp3.rs b/src/audio/mp3.rs
new file mode 100644
index 0000000..9a502d7
--- /dev/null
+++ b/src/audio/mp3.rs
@@ -0,0 +1,571 @@
+/*
+rip - rip embedded content
+Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+use crate::util::position::Position;
+use crate::util::content_type::ContentType;
+
+const ID3V2_IDENTIFIER: [u8; 3] = [0x49, 0x44, 0x33];
+const ID3V2_HEADER_LENGTH: usize = 10;
+const MP3_HEADER_LENGTH: usize = 4;
+const MP3_HEADER_SYNC_WORD_MASK: u32 = 0xFFE00000; // 11111111111000000000000000000000
+const MP3_HEADER_VERSION_MASK: u32 = 0x180000; // 00000000000110000000000000000000
+const MP3_HEADER_LAYER_MASK: u32 = 0x60000; // 00000000000001100000000000000000
+const MP3_HEADER_BITRATE_MASK: u32 = 0xF000; // 00000000000000001111000000000000
+const MP3_HEADER_SAMPLING_RATE_MASK: u32 = 0xC00; // 00000000000000000000110000000000
+const MP3_HEADER_PADDING_MASK: u32 = 0x200; // 00000000000000000000001000000000
+
+#[derive(Debug)]
+enum AudioVersion {
+ MpegV1,
+ MpegV2,
+ MpegV25,
+}
+
+#[derive(Debug)]
+enum LayerIndex {
+ LayerI,
+ LayerII,
+ LayerIII,
+}
+
+fn get_bitrate(header: u32, audio_version: &AudioVersion, layer: &LayerIndex) -> Option {
+ match header & MP3_HEADER_BITRATE_MASK {
+ // 0001
+ 0x1000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI | LayerIndex::LayerII | LayerIndex::LayerIII => return Some(32),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(32),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(8),
+ }
+ }
+ }
+ }
+
+
+ // 0010
+ 0x2000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(64),
+ LayerIndex::LayerII => return Some(48),
+ LayerIndex::LayerIII => return Some(40),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(48),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(16),
+ }
+ }
+ }
+ }
+
+ // 0011
+ 0x3000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(96),
+ LayerIndex::LayerII => return Some(56),
+ LayerIndex::LayerIII => return Some(48),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(32),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(16),
+ }
+ }
+ }
+ }
+
+ // 0100
+ 0x4000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(128),
+ LayerIndex::LayerII => return Some(64),
+ LayerIndex::LayerIII => return Some(56),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(64),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(32),
+ }
+ }
+ }
+ }
+
+ // 0101
+ 0x5000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(160),
+ LayerIndex::LayerII => return Some(80),
+ LayerIndex::LayerIII => return Some(64),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(80),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(40),
+ }
+ }
+ }
+ }
+
+ // 0110
+ 0x6000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(192),
+ LayerIndex::LayerII => return Some(96),
+ LayerIndex::LayerIII => return Some(80),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(96),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(48),
+ }
+ }
+ }
+ }
+
+ // 0111
+ 0x7000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(224),
+ LayerIndex::LayerII => return Some(112),
+ LayerIndex::LayerIII => return Some(96),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(112),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(56),
+ }
+ }
+ }
+ }
+
+ // 1000
+ 0x8000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(256),
+ LayerIndex::LayerII => return Some(128),
+ LayerIndex::LayerIII => return Some(112),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(128),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(64),
+ }
+ }
+ }
+ }
+
+ // 1001
+ 0x9000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(288),
+ LayerIndex::LayerII => return Some(160),
+ LayerIndex::LayerIII => return Some(128),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(144),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(80),
+ }
+ }
+ }
+ }
+
+ // 1010
+ 0xA000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(320),
+ LayerIndex::LayerII => return Some(192),
+ LayerIndex::LayerIII => return Some(160),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(160),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(96),
+ }
+ }
+ }
+ }
+
+ // 1011
+ 0xB000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(352),
+ LayerIndex::LayerII => return Some(224),
+ LayerIndex::LayerIII => return Some(192),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(176),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(112),
+ }
+ }
+ }
+ }
+
+ // 1100
+ 0xC000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(384),
+ LayerIndex::LayerII => return Some(256),
+ LayerIndex::LayerIII => return Some(224),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(192),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(128),
+ }
+ }
+ }
+ }
+
+ // 1101
+ 0xD000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(416),
+ LayerIndex::LayerII => return Some(320),
+ LayerIndex::LayerIII => return Some(256),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(224),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(144),
+ }
+ }
+ }
+ }
+
+ // 1110
+ 0xE000 => {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return Some(448),
+ LayerIndex::LayerII => return Some(384),
+ LayerIndex::LayerIII => return Some(320),
+ }
+ }
+
+ AudioVersion::MpegV2 | AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return Some(256),
+ LayerIndex::LayerII | LayerIndex::LayerIII => return Some(160),
+ }
+ }
+ }
+ }
+
+ _ => {
+ // invalid bitrate index
+ return None;
+ }
+ }
+}
+
+fn get_samples_per_frame(audio_version: &AudioVersion, layer: &LayerIndex) -> u16 {
+ match audio_version {
+ AudioVersion::MpegV1 => {
+ match layer {
+ LayerIndex::LayerI => return 384,
+ LayerIndex::LayerII => return 1152,
+ LayerIndex::LayerIII => return 1152,
+ }
+ }
+
+ AudioVersion::MpegV2 => {
+ match layer {
+ LayerIndex::LayerI => return 384,
+ LayerIndex::LayerII => return 1152,
+ LayerIndex::LayerIII => return 576,
+ }
+ }
+
+ AudioVersion::MpegV25 => {
+ match layer {
+ LayerIndex::LayerI => return 384,
+ LayerIndex::LayerII => return 1152,
+ LayerIndex::LayerIII => return 576,
+ }
+ }
+ }
+}
+
+fn get_sampling_rate(header: u32, audio_version: &AudioVersion) -> Option {
+ match header & MP3_HEADER_SAMPLING_RATE_MASK {
+ // 00
+ 0x0 => {
+ match audio_version {
+ AudioVersion::MpegV1 => return Some(44100),
+ AudioVersion::MpegV2 => return Some(22050),
+ AudioVersion::MpegV25 => return Some(11025),
+ }
+ }
+
+ // 01
+ 0x400 => {
+ match audio_version {
+ AudioVersion::MpegV1 => return Some(48000),
+ AudioVersion::MpegV2 => return Some(24000),
+ AudioVersion::MpegV25 => return Some(12000),
+ }
+ }
+
+ // 10
+ 0x800 => {
+ match audio_version {
+ AudioVersion::MpegV1 => return Some(32000),
+ AudioVersion::MpegV2 => return Some(16000),
+ AudioVersion::MpegV25 => return Some(8000),
+ }
+ }
+
+ // invalid value
+ _ => return None
+ }
+}
+
+fn get_audio_version(header: u32) -> Option {
+ match header & MP3_HEADER_VERSION_MASK {
+ // 00
+ 0x0 => return Some(AudioVersion::MpegV25),
+ // 10
+ 0x100000 => return Some(AudioVersion::MpegV2),
+ // 11
+ 0x180000 => return Some(AudioVersion::MpegV1),
+ // invalid version id
+ _ => return None,
+ }
+}
+
+fn get_layer(header: u32) -> Option {
+ match header & MP3_HEADER_LAYER_MASK {
+ // 01
+ 0x20000 => return Some(LayerIndex::LayerIII),
+ // 10
+ 0x40000 => return Some(LayerIndex::LayerII),
+ // 11
+ 0x60000 => return Some(LayerIndex::LayerI),
+ // invalid layer index
+ _ => return None,
+ }
+}
+
+pub fn rip_mp3(data: &[u8], start_index: usize) -> Option {
+ if data.len() < ID3V2_HEADER_LENGTH + MP3_HEADER_LENGTH + 1 ||
+ start_index + ID3V2_HEADER_LENGTH + MP3_HEADER_LENGTH + 1 >= data.len() {
+ return None;
+ }
+
+ let mut position: Position = Position{
+ start: usize::MAX,
+ end: usize::MAX,
+ content_type: ContentType::MP3,
+ };
+
+ let mut cursor_index: usize;
+ for i in start_index..data.len() {
+ if i < ID3V2_HEADER_LENGTH && position.start == usize::MAX {
+ if data[i..i + ID3V2_IDENTIFIER.len()] == ID3V2_IDENTIFIER {
+ // found ID3v2 tag (the beginning of the MP3 file)
+ println!("id3 at {}", i);
+
+ // get tag length
+ let mut tag_length_bytes: [u8; 4] = [0; 4];
+ for j in 0..4 {
+ tag_length_bytes[j] = data[i+ID3V2_IDENTIFIER.len()+5+j];
+ }
+ // convert from syncsafe to a normal integer
+ let mut tag_length: u32 = 0;
+ for j in 0..4 {
+ tag_length = tag_length << 7;
+ tag_length = tag_length | tag_length_bytes[j] as u32;
+ }
+
+ let id3v2_end_index: usize = i + ID3V2_HEADER_LENGTH + tag_length as usize;
+ println!("id3v2 end index is {}; the data size is {}", id3v2_end_index, data.len());
+ if id3v2_end_index + MP3_HEADER_LENGTH > data.len() - 1 {
+ println!("not enough data length");
+ // strange: there's a valid ID3 tag but not enough data to store any music
+ break;
+ }
+ position.start = i;
+
+ cursor_index = id3v2_end_index;
+ loop {
+ if cursor_index >= data.len() - 1 {
+ break;
+ }
+
+ // whole header
+ let mut mp3_header_bytes: [u8 ; MP3_HEADER_LENGTH] = [0; MP3_HEADER_LENGTH];
+ for j in 0..MP3_HEADER_LENGTH {
+ mp3_header_bytes[j] = data[cursor_index + j];
+ }
+ let mp3_header: u32 = u32::from_be_bytes(mp3_header_bytes);
+
+ // check for sync word
+ if !mp3_header & MP3_HEADER_SYNC_WORD_MASK == MP3_HEADER_SYNC_WORD_MASK {
+ break;
+ }
+
+ // that's really an MP3 !
+ println!("mpeg header at {}", cursor_index);
+ cursor_index += MP3_HEADER_LENGTH;
+
+ println!("{:#032b}", mp3_header);
+
+ // retrieve audio version
+ let audio_version: AudioVersion;
+ match get_audio_version(mp3_header) {
+ Some(version) => audio_version = version,
+ None => break,
+ }
+
+ println!("audio version is {:?}", audio_version);
+
+ // get layer
+ let layer: LayerIndex;
+ match get_layer(mp3_header) {
+ Some(l) => layer = l,
+ None => break,
+ }
+
+ println!("layer is {:?}", layer);
+
+
+ // decode that HUGE bitrate table
+ let bitrate: u16;
+ match get_bitrate(mp3_header, &audio_version, &layer) {
+ Some(rate) => bitrate = rate,
+ None => break,
+ }
+
+ println!("bitrate is {}", bitrate);
+
+
+ // samples per frame
+ // let samples_per_frame: u16 = get_samples_per_frame(&audio_version, &layer);
+
+ // sampling rate
+ let sampling_rate: u16;
+ match get_sampling_rate(mp3_header, &audio_version) {
+ Some(rate) => sampling_rate = rate,
+ None => break,
+ }
+
+ println!("sampling rate is {}", sampling_rate);
+
+
+ // padding
+ let padding: u8;
+ if mp3_header == MP3_HEADER_PADDING_MASK {
+ padding = 1;
+ } else {
+ padding = 0;
+ }
+
+ println!("padding is {}", padding);
+
+
+ // finally calculate frame size
+ let frame_size: u32;
+ match layer {
+ LayerIndex::LayerI => {
+ frame_size = (12 * bitrate as u32 * 1000 / sampling_rate as u32 + padding as u32) * 4;
+ }
+
+ LayerIndex::LayerII | LayerIndex::LayerIII => {
+ frame_size = 144 * bitrate as u32 * 1000 / (sampling_rate as u32 + padding as u32);
+ }
+ }
+ println!("frame size is {}", frame_size);
+
+ // set cursor to the next frame
+ cursor_index += frame_size as usize;
+ // extend end position
+ position.end = cursor_index;
+ println!("frame end at {}", cursor_index);
+ }
+ }
+ }
+
+ if position.start != usize::MAX && position.end != usize::MAX {
+ break;
+ }
+ }
+
+ if position.start == usize::MAX || position.end == usize::MAX || position.end <= position.start {
+ return None;
+ }
+
+ return Some(position);
+}
\ No newline at end of file
diff --git a/src/img/jpeg.rs b/src/img/jpeg.rs
new file mode 100644
index 0000000..052b946
--- /dev/null
+++ b/src/img/jpeg.rs
@@ -0,0 +1,64 @@
+/*
+rip - rip embedded content
+Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+use crate::util::position::Position;
+use crate::util::content_type::ContentType;
+
+const JPEG_IDENTIFIER: [u8; 3] = [0xFF, 0xD8, 0xFF];
+const JPEG_END_IDENTIFIER: [u8; 2] = [0xFF, 0xD9];
+
+// Reads data from specified start_index position,
+// if valid png bytes were found - returns exact positions of an image
+pub fn rip_jpeg(data: &[u8], start_index: usize) -> Option {
+ if data.len() < JPEG_IDENTIFIER.len() + JPEG_END_IDENTIFIER.len() ||
+ start_index + JPEG_IDENTIFIER.len() + JPEG_END_IDENTIFIER.len() > data.len() {
+ return None;
+ }
+
+ let mut position: Position = Position{
+ start: usize::MAX,
+ end: usize::MAX,
+ content_type: ContentType::JPEG,
+ };
+
+ for i in start_index..data.len() {
+ // start index
+ if i < data.len() - JPEG_IDENTIFIER.len() && position.start == usize::MAX {
+ if data[i..i + JPEG_IDENTIFIER.len()] == JPEG_IDENTIFIER {
+ position.start = i;
+ }
+ }
+
+ // end index
+ if i <= data.len() - JPEG_END_IDENTIFIER.len() && position.end == usize::MAX {
+ if data[i..i + JPEG_END_IDENTIFIER.len()] == JPEG_END_IDENTIFIER {
+ position.end = i;
+ }
+ }
+
+ if position.start != usize::MAX && position.end != usize::MAX {
+ break;
+ }
+ }
+
+ if position.start == usize::MAX || position.end == usize::MAX || position.end < position.start {
+ return None;
+ }
+
+ return Some(position);
+}
\ No newline at end of file
diff --git a/src/img/mod.rs b/src/img/mod.rs
new file mode 100644
index 0000000..a3f9979
--- /dev/null
+++ b/src/img/mod.rs
@@ -0,0 +1,2 @@
+pub mod png;
+pub mod jpeg;
\ No newline at end of file
diff --git a/src/img/png.rs b/src/img/png.rs
new file mode 100644
index 0000000..28db943
--- /dev/null
+++ b/src/img/png.rs
@@ -0,0 +1,65 @@
+/*
+rip - rip embedded content
+Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+use crate::util::position::Position;
+use crate::util::content_type::ContentType;
+
+const PNG_IDENTIFIER: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0xD, 0xA, 0x1A, 0xA];
+const PNG_END_IDENTIFIER: [u8; 8] = [0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82];
+
+// Reads data from specified start_index position,
+// if valid png bytes were found - returns exact positions of an image
+pub fn rip_png(data: &[u8], start_index: usize) -> Option {
+ if data.len() < PNG_IDENTIFIER.len() + PNG_END_IDENTIFIER.len() ||
+ start_index + PNG_IDENTIFIER.len() + PNG_END_IDENTIFIER.len() > data.len() {
+ return None;
+ }
+
+ let mut position: Position = Position{
+ start: usize::MAX,
+ end: usize::MAX,
+ content_type: ContentType::PNG,
+ };
+
+ for i in start_index..data.len() {
+ // start index
+ if i < data.len() - PNG_IDENTIFIER.len() && position.start == usize::MAX {
+ if data[i..i + PNG_IDENTIFIER.len()] == PNG_IDENTIFIER {
+ position.start = i;
+ }
+ }
+
+ // end index
+ if i <= data.len() - PNG_END_IDENTIFIER.len() && position.end == usize::MAX {
+ if data[i..i + PNG_END_IDENTIFIER.len()] == PNG_END_IDENTIFIER {
+ position.end = i + PNG_END_IDENTIFIER.len();
+ println!("end {}", position.end);
+ }
+ }
+
+ if position.start != usize::MAX && position.end != usize::MAX {
+ break;
+ }
+ }
+
+ if position.start == usize::MAX || position.end == usize::MAX || position.end <= position.start {
+ return None;
+ }
+
+ return Some(position);
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 8feec88..8ed9378 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,123 +16,30 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+mod util;
+mod img;
+mod audio;
+
use std::path;
use std::io::{Read, Write};
+use crate::util::position::Position;
+use crate::util::content_type::ContentType;
+use crate::img::png::rip_png;
+use crate::img::jpeg::rip_jpeg;
+use crate::audio::mp3::rip_mp3;
#[derive(Debug)]
-enum ContentType {
- PNG,
- JPEG,
-}
-
-#[derive(Debug)]
-struct Position {
- start: usize,
- end: usize,
- content_type: ContentType,
-}
-
-// Reads data from specified start_index position,
-// if valid png bytes were found - returns exact positions of an image
-fn rip_png(data: &[u8], start_index: usize) -> Option {
- const PNG_IDENTIFIER: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0xD, 0xA, 0x1A, 0xA];
- const PNG_END_IDENTIFIER: [u8; 8] = [0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82];
-
- if data.len() < PNG_IDENTIFIER.len() + PNG_END_IDENTIFIER.len() ||
- start_index + PNG_IDENTIFIER.len() + PNG_END_IDENTIFIER.len() > data.len() {
- return None;
- }
-
- let mut position: Position = Position{
- start: usize::MAX,
- end: usize::MAX,
- content_type: ContentType::PNG,
- };
-
- for i in start_index..data.len() {
- // start index
- if i < data.len() - PNG_IDENTIFIER.len() && position.start == usize::MAX {
- if data[i..i + PNG_IDENTIFIER.len()] == PNG_IDENTIFIER {
- position.start = i;
- }
- }
-
- // end index
- if i <= data.len() - PNG_END_IDENTIFIER.len() && position.end == usize::MAX {
- if data[i..i + PNG_END_IDENTIFIER.len()] == PNG_END_IDENTIFIER {
- position.end = i;
- }
- }
-
- if position.start != usize::MAX && position.end != usize::MAX {
- break;
- }
- }
-
- if position.start == usize::MAX || position.end == usize::MAX || position.end <= position.start {
- return None;
- }
-
- return Some(position);
-}
-
-// Reads data from specified start_index position,
-// if valid png bytes were found - returns exact positions of an image
-fn rip_jpeg(data: &[u8], start_index: usize) -> Option {
- const JPEG_IDENTIFIER: [u8; 3] = [0xFF, 0xD8, 0xFF];
- const JPEG_END_IDENTIFIER: [u8; 2] = [0xFF, 0xD9];
-
- if data.len() < JPEG_IDENTIFIER.len() + JPEG_END_IDENTIFIER.len() ||
- start_index + JPEG_IDENTIFIER.len() + JPEG_END_IDENTIFIER.len() > data.len() {
- return None;
- }
-
- let mut position: Position = Position{
- start: usize::MAX,
- end: usize::MAX,
- content_type: ContentType::JPEG,
- };
-
- for i in start_index..data.len() {
- // start index
- if i < data.len() - JPEG_IDENTIFIER.len() && position.start == usize::MAX {
- if data[i..i + JPEG_IDENTIFIER.len()] == JPEG_IDENTIFIER {
- position.start = i;
- }
- }
-
- // end index
- if i <= data.len() - JPEG_END_IDENTIFIER.len() && position.end == usize::MAX {
- if data[i..i + JPEG_END_IDENTIFIER.len()] == JPEG_END_IDENTIFIER {
- position.end = i;
- }
- }
-
- if position.start != usize::MAX && position.end != usize::MAX {
- break;
- }
- }
-
- if position.start == usize::MAX || position.end == usize::MAX || position.end < position.start {
- return None;
- }
-
- return Some(position);
-}
-
-
-#[derive(Debug)]
-enum RIPTYPE {
+enum RipType {
ALL,
IMG,
+ AUDIO,
}
-
fn main() {
let mut save_directory: &path::Path = path::Path::new(".");
let mut file_paths: Vec<&path::Path> = Vec::new();
let mut max_file_size: u128 = u128::MAX;
- let mut rip_type: RIPTYPE = RIPTYPE::ALL;
+ let mut rip_type: RipType = RipType::ALL;
// work out the arguments
let args: Vec = std::env::args().collect();
@@ -157,15 +64,16 @@ fn main() {
\"-mfs\" or \"--max-file-size\" [SIZE] -> skip files bigger than size (in bytes)\n
\n\
[RIPTYPE]\n\
- ALL -> rip everything that seems like an embedded content\n\
- IMG -> try to look for images only\n\
+ ALL -> rip everything that seems like an embedded content\n\
+ IMG -> try to look for images only\n\
+ AUDIO -> rip audio content\n
"
);
return;
}
else if &args[arg_index] == "-v" || &args[arg_index] == "--version" {
println!(
- "rip v0.1\
+ "rip v0.2\
\n
\n\
(c) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)\
@@ -224,10 +132,18 @@ fn main() {
}
}
else if file_paths.len() == 0 && &args[arg_index].to_lowercase() == "all" {
- rip_type = RIPTYPE::ALL;
+ rip_type = RipType::ALL;
+ println!("Ripping EVERYTHING (JPEG, PNG, MP3)");
}
else if file_paths.len() == 0 && &args[arg_index].to_lowercase() == "img" {
- rip_type = RIPTYPE::IMG;
+ rip_type = RipType::IMG;
+ println!("Ripping IMAGES (JPEG, PNG)");
+ }
+ else if file_paths.len() == 0 && &args[arg_index].to_lowercase() == "audio" {
+ rip_type = RipType::AUDIO;
+ println!("[INFO] Not implemented");
+ return;
+ // println!("Ripping AUDIO (MP3)");
}
else {
// that's a path to the file to be examined
@@ -237,8 +153,6 @@ fn main() {
arg_index += 1;
}
- println!("Riptype {:?}", rip_type);
-
for file_path in file_paths {
print!("\n");
@@ -301,7 +215,7 @@ fn main() {
let mut positions: Vec = Vec::new();
match rip_type {
- RIPTYPE::IMG => {
+ RipType::IMG => {
// find PNG positions
let mut cursor_index: usize = 0;
while (cursor_index as u64) < file_metadata.len() {
@@ -333,7 +247,26 @@ fn main() {
}
}
- RIPTYPE::ALL => {
+ RipType::AUDIO => {
+ // find MP3 positions
+ // let mut cursor_index = 0;
+ // while (cursor_index as u64) < file_metadata.len() {
+ // match rip_mp3(&file_contents, cursor_index) {
+ // Some(pos) => {
+ // cursor_index = pos.end;
+ // println!("b {:?}", pos);
+ // positions.push(pos);
+ // }
+ // None => {
+ // // no MP3s were found
+ // break;
+ // }
+ // }
+ // }
+
+ }
+
+ RipType::ALL => {
// find PNG positions
let mut cursor_index: usize = 0;
while (cursor_index as u64) < file_metadata.len() {
@@ -363,6 +296,21 @@ fn main() {
}
}
}
+
+ // find MP3 positions
+ // cursor_index = 0;
+ // while (cursor_index as u64) < file_metadata.len() {
+ // match rip_mp3(&file_contents, cursor_index) {
+ // Some(pos) => {
+ // cursor_index = pos.end;
+ // positions.push(pos);
+ // }
+ // None => {
+ // // no MP3s were found
+ // break;
+ // }
+ // }
+ // }
}
}
@@ -397,6 +345,10 @@ fn main() {
ContentType::JPEG => {
output_file_path_string = output_file_path_string + &format!("_{}.jpeg", position_index);
}
+
+ ContentType::MP3 => {
+ output_file_path_string = output_file_path_string + &format!("_{}.mp3", position_index);
+ }
}
let mut output_file_handle: std::fs::File;
diff --git a/src/util/content_type.rs b/src/util/content_type.rs
new file mode 100644
index 0000000..41bd128
--- /dev/null
+++ b/src/util/content_type.rs
@@ -0,0 +1,24 @@
+/*
+rip - rip embedded content
+Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#[derive(Debug)]
+pub enum ContentType {
+ PNG,
+ JPEG,
+ MP3,
+}
\ No newline at end of file
diff --git a/src/util/mod.rs b/src/util/mod.rs
new file mode 100644
index 0000000..b3d724f
--- /dev/null
+++ b/src/util/mod.rs
@@ -0,0 +1,2 @@
+pub mod position;
+pub mod content_type;
\ No newline at end of file
diff --git a/src/util/position.rs b/src/util/position.rs
new file mode 100644
index 0000000..7a65650
--- /dev/null
+++ b/src/util/position.rs
@@ -0,0 +1,26 @@
+/*
+rip - rip embedded content
+Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+use crate::util::content_type::ContentType;
+
+#[derive(Debug)]
+pub struct Position {
+ pub start: usize,
+ pub end: usize,
+ pub content_type: ContentType,
+}
\ No newline at end of file