Browse Source

Splitted project into separate parts; started working on mp3 decoding

master
parent
commit
e89b266c6e
  1. 1
      src/audio/mod.rs
  2. 571
      src/audio/mp3.rs
  3. 64
      src/img/jpeg.rs
  4. 2
      src/img/mod.rs
  5. 65
      src/img/png.rs
  6. 180
      src/main.rs
  7. 24
      src/util/content_type.rs
  8. 2
      src/util/mod.rs
  9. 26
      src/util/position.rs

1
src/audio/mod.rs

@ -0,0 +1 @@
pub mod mp3;

571
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 <https://www.gnu.org/licenses/>.
*/
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<u16> {
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<u16> {
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<AudioVersion> {
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<LayerIndex> {
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<Position> {
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);
}

64
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 <https://www.gnu.org/licenses/>.
*/
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<Position> {
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);
}

2
src/img/mod.rs

@ -0,0 +1,2 @@
pub mod png;
pub mod jpeg;

65
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 <https://www.gnu.org/licenses/>.
*/
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<Position> {
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);
}

180
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 <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
mod util;
mod img;
mod audio;
use std::path; use std::path;
use std::io::{Read, Write}; 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)] #[derive(Debug)]
enum ContentType { enum RipType {
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<Position> {
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<Position> {
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 {
ALL, ALL,
IMG, IMG,
AUDIO,
} }
fn main() { fn main() {
let mut save_directory: &path::Path = path::Path::new("."); let mut save_directory: &path::Path = path::Path::new(".");
let mut file_paths: Vec<&path::Path> = Vec::new(); let mut file_paths: Vec<&path::Path> = Vec::new();
let mut max_file_size: u128 = u128::MAX; 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 // work out the arguments
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
@ -157,15 +64,16 @@ fn main() {
\"-mfs\" or \"--max-file-size\" [SIZE] -> skip files bigger than size (in bytes)\n \"-mfs\" or \"--max-file-size\" [SIZE] -> skip files bigger than size (in bytes)\n
\n\ \n\
[RIPTYPE]\n\ [RIPTYPE]\n\
ALL -> rip everything that seems like an embedded content\n\ ALL -> rip everything that seems like an embedded content\n\
IMG -> try to look for images only\n\ IMG -> try to look for images only\n\
AUDIO -> rip audio content\n
" "
); );
return; return;
} }
else if &args[arg_index] == "-v" || &args[arg_index] == "--version" { else if &args[arg_index] == "-v" || &args[arg_index] == "--version" {
println!( println!(
"rip v0.1\ "rip v0.2\
\n \n
\n\ \n\
(c) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)\ (c) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)\
@ -224,10 +132,18 @@ fn main() {
} }
} }
else if file_paths.len() == 0 && &args[arg_index].to_lowercase() == "all" { 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" { 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 { else {
// that's a path to the file to be examined // that's a path to the file to be examined
@ -237,8 +153,6 @@ fn main() {
arg_index += 1; arg_index += 1;
} }
println!("Riptype {:?}", rip_type);
for file_path in file_paths { for file_path in file_paths {
print!("\n"); print!("\n");
@ -301,7 +215,7 @@ fn main() {
let mut positions: Vec<Position> = Vec::new(); let mut positions: Vec<Position> = Vec::new();
match rip_type { match rip_type {
RIPTYPE::IMG => { RipType::IMG => {
// find PNG positions // find PNG positions
let mut cursor_index: usize = 0; let mut cursor_index: usize = 0;
while (cursor_index as u64) < file_metadata.len() { 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 // find PNG positions
let mut cursor_index: usize = 0; let mut cursor_index: usize = 0;
while (cursor_index as u64) < file_metadata.len() { 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 => { ContentType::JPEG => {
output_file_path_string = output_file_path_string + &format!("_{}.jpeg", position_index); 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; let mut output_file_handle: std::fs::File;

24
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 <https://www.gnu.org/licenses/>.
*/
#[derive(Debug)]
pub enum ContentType {
PNG,
JPEG,
MP3,
}

2
src/util/mod.rs

@ -0,0 +1,2 @@
pub mod position;
pub mod content_type;

26
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 <https://www.gnu.org/licenses/>.
*/
use crate::util::content_type::ContentType;
#[derive(Debug)]
pub struct Position {
pub start: usize,
pub end: usize,
pub content_type: ContentType,
}
Loading…
Cancel
Save