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