feat: 完成后端状态的优化

This commit is contained in:
381848900@qq.com 2024-12-12 14:55:07 +08:00
parent 175189bbdf
commit bfe54cae1f
9 changed files with 164 additions and 233 deletions

View File

@ -1,6 +1,6 @@
use thiserror; use thiserror;
use crate::pe_parse::error::{self, PEParseError}; use crate::pe_parse::error::PEParseError;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum AppError { pub enum AppError {
@ -11,8 +11,16 @@ pub enum AppError {
#[error("文件未打开!")] #[error("文件未打开!")]
NoFileOpened, NoFileOpened,
// 文件没有初始化打开
#[error("文件未初始化打开!")]
NotInitOpenFile,
#[error(transparent)] #[error(transparent)]
PeParseError(#[from] PEParseError), PeParseError(#[from] PEParseError),
/// 初始化打开文件失败
#[error("初始化打开文件失败!: {0}")]
InitOpenFileFailed(String),
} }
impl serde::Serialize for AppError { impl serde::Serialize for AppError {

View File

@ -0,0 +1,30 @@
use memmap2::Mmap;
use crate::app_error::AppError;
use crate::{pe_parse::pe::ReadOnlyPE, services::file};
/// 应用的状态结构体
#[derive(Default)]
pub struct AppState {
/// 文件路径
pub file_path: Option<String>,
/// 文件Mmap的只读内存映射
mmap: Option<Mmap>,
/// 文件是否是64位
pub is_64_bit: Option<bool>
}
impl AppState {
/// 初始化打开文件
pub fn init_open_file(&mut self, file_path: &str) -> Result<(), AppError> {
self.file_path = Some(file_path.to_string());
self.mmap = Some(file::mmap_mut_file(file_path)?);
// 读取PE格式来判断是否是64位
let mmap: &Mmap = self.mmap.as_ref().unwrap();
self.is_64_bit = Some(mmap.is_64_bit()?);
Ok(())
}
/// 获取文件内存映射
pub fn get_mmap_ref(&self) -> Result<&Mmap, AppError> {
self.mmap.as_ref().ok_or(AppError::NoFileOpened)
}
}

View File

@ -1,10 +1,13 @@
use std::sync::Mutex;
use crate::{ use crate::{
app_error::AppError, app_error::AppError,
pe_parse::pe::PE, app_state::AppState,
services::{self, file::*, GLOBAL_FILE_DATA}, pe_parse::pe::ReadOnlyPE,
}; };
use serde::Serialize; use serde::Serialize;
use tauri::{AppHandle, Manager}; use serde_json::{json, Value};
use tauri::{AppHandle, Manager, State};
#[derive(Serialize)] #[derive(Serialize)]
pub struct PeNodeTreeData { pub struct PeNodeTreeData {
@ -12,7 +15,7 @@ pub struct PeNodeTreeData {
key: String, key: String,
children: Vec<PeNodeTreeData>, children: Vec<PeNodeTreeData>,
} }
// set_complete /// 设置闪屏的切换
#[tauri::command] #[tauri::command]
pub fn set_complete(app: AppHandle) -> Result<(), AppError> { pub fn set_complete(app: AppHandle) -> Result<(), AppError> {
let splash_window = app.get_webview_window("splashscreen"); let splash_window = app.get_webview_window("splashscreen");
@ -26,18 +29,37 @@ pub fn set_complete(app: AppHandle) -> Result<(), AppError> {
Ok(()) Ok(())
} }
// 命令,打开文件
#[tauri::command] #[tauri::command]
pub fn command_get_file_pe_node_tree_data() -> Result<Vec<PeNodeTreeData>, AppError> { pub fn command_open_file(
// 1. 如果没有打开文件,则返回错误 file_path: &str,
if GLOBAL_FILE_DATA.lock().unwrap().is_none() { app_state: State<'_, Mutex<AppState>>,
return Err(AppError::NoFileOpened); ) -> Result<(), AppError> {
app_state
.lock()
.unwrap()
.init_open_file(file_path)
.map_err(|e| AppError::InitOpenFileFailed(e.to_string()))?;
Ok(())
}
#[tauri::command]
pub fn command_get_file_pe_node_tree_data(
app_state: State<'_, Mutex<AppState>>,
) -> Result<Vec<PeNodeTreeData>, AppError> {
let app_state = app_state.lock().unwrap();
// 如果没有打开文件,则返回错误
if app_state.file_path.is_none() {
return Err(AppError::NotInitOpenFile);
} }
// 从文件路径中获取文件名 let file_path = app_state.file_path.as_ref().unwrap();
let file_name: &str = unsafe { let file_name = std::path::Path::new(file_path)
let file_path = services::GLOBAL_FILE_PATH.as_ref().unwrap(); .file_name()
let file_name = std::path::Path::new(file_path).file_name().unwrap(); .unwrap()
file_name.to_str().unwrap() .to_str()
}; .unwrap();
let result = PeNodeTreeData { let result = PeNodeTreeData {
title: format!("文件: {}", file_name), title: format!("文件: {}", file_name),
key: "file_name".to_string(), key: "file_name".to_string(),
@ -77,65 +99,73 @@ pub fn command_get_file_pe_node_tree_data() -> Result<Vec<PeNodeTreeData>, AppEr
Ok(vec![result]) Ok(vec![result])
} }
// 命令,打开文件
#[tauri::command]
pub fn command_open_file(file_path: &str) -> Result<(), AppError> {
let mmap = Some(services::file::mmap_mut_file(file_path)?);
// 将内存clone到Vec中
let mmap = mmap.as_ref().unwrap();
let mmap = mmap.to_vec();
*services::GLOBAL_FILE_DATA.lock().unwrap() = Some(services::PeData(mmap));
unsafe {
services::GLOBAL_FILE_PATH = Some(file_path.to_string());
}
// 判断文件是否是64位
let binding = GLOBAL_FILE_DATA.lock().unwrap();
let file_data = binding.as_ref().unwrap();
let is64bit = file_data.is_64_bit()?;
unsafe {
services::GLOBAL_IS_64_BIT = Some(is64bit);
}
Ok(())
}
// 命令获取DOS头数据 // 命令获取DOS头数据
#[tauri::command] #[tauri::command]
pub fn command_get_pe_data_dos_header() -> Result<ResponseDOSHeaderData, AppError> { pub fn command_get_pe_data_dos_header(
let binding = GLOBAL_FILE_DATA.lock().unwrap(); app_state: State<'_, Mutex<AppState>>,
let file_data = binding.as_ref().unwrap(); ) -> Result<Value, AppError> {
let dos_header = file_data.get_dos_header()?; let app_state = app_state.lock().unwrap();
let result = ResponseDOSHeaderData { let mmap = app_state.get_mmap_ref()?;
fields: dos_header.clone(), let dos_header = mmap.get_dos_header()?;
base_offset: 0, Ok(json!({
}; "fields": dos_header.clone(),
Ok(result) "base_offset": 0,
}))
} }
// 命令获取NT头数据 // 命令获取NT头数据
#[tauri::command] #[tauri::command]
pub fn command_get_pe_data_nt_header() -> Result<ResponseNTHeaderData, AppError> { pub fn command_get_pe_data_nt_header(
let result = get_nt_headers_data()?; app_state: State<'_, Mutex<AppState>>,
Ok(result) ) -> Result<Value, AppError> {
let app_state = app_state.lock().unwrap();
let mmap = app_state.get_mmap_ref()?;
let nt_header: crate::pe_parse::header::ImageNTHeader = mmap.get_nt_header()?;
let nt_offset = mmap.get_nt_headers_offset()?;
Ok(json! ({
"fields": nt_header,
"base_offset": nt_offset,
}))
} }
// 命令,获取文件头数据 // 命令,获取文件头数据
#[tauri::command] #[tauri::command]
pub fn command_get_pe_data_file_header() -> Result<ResponseFileHeaderData, AppError> { pub fn command_get_pe_data_file_header(app_state: State<'_, Mutex<AppState>>) -> Result<Value, AppError> {
let result = services::file::get_file_header_data()?; let app_state = app_state.lock().unwrap();
Ok(result) let mmap = app_state.get_mmap_ref()?;
let file_header = mmap.get_file_header()?;
let file_header_offset = mmap.get_file_header_offset()?;
Ok(json!({
"fields": file_header.clone(),
"base_offset": file_header_offset,
}))
} }
// 命令,获取可选头数据 // 命令,获取可选头数据
#[tauri::command] #[tauri::command]
pub fn command_get_pe_data_optional_header() -> Result<ResponseOptionalHeaderData, AppError> { pub fn command_get_pe_data_optional_header(app_state: State<'_, Mutex<AppState>>) -> Result<Value, AppError> {
let result = services::file::get_optional_header_data()?; let app_state = app_state.lock().unwrap();
Ok(result) let mmap = app_state.get_mmap_ref()?;
let optional_header = mmap.get_optional_header()?;
let optional_header_offset = mmap.get_optional_header_offset()?;
let is_64_bit = mmap.is_64_bit()?;
Ok(json!({
"fields": optional_header.clone(),
"base_offset": optional_header_offset,
"is_64_bit": is_64_bit,
}))
} }
// 命令,获取节区头数据 // 命令,获取节区头数据
#[tauri::command] #[tauri::command]
pub fn command_get_pe_data_section_headers() -> Result<ResponseSectionHeaderData, AppError> { pub fn command_get_pe_data_section_headers(app_state: State<'_, Mutex<AppState>>) -> Result<Value, AppError> {
let result = services::file::get_section_header_data()?; let app_state = app_state.lock().unwrap();
Ok(result) let mmap = app_state.get_mmap_ref()?;
let section_headers = mmap.get_section_headers()?;
let section_headers_offset = mmap.get_section_headers_offset()?;
let vec_section_headers = section_headers.to_vec();
Ok(json!({
"fields": vec_section_headers,
"base_offset": section_headers_offset,
}))
} }

View File

@ -1,8 +1,13 @@
use std::sync::Mutex;
use tauri::Manager;
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
pub mod app_error; pub mod app_error;
pub mod commands; pub mod commands;
pub mod pe_parse; pub mod pe_parse;
pub mod services; pub mod services;
pub mod app_state;
#[cfg_attr(mobile, tauri::mobile_entry_point)] #[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() { pub fn run() {
@ -19,6 +24,10 @@ pub fn run() {
commands::command_get_pe_data_section_headers, commands::command_get_pe_data_section_headers,
commands::set_complete, commands::set_complete,
]) ])
.setup(|app| {
app.manage(Mutex::new(app_state::AppState::default()));
Ok(())
})
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");
} }

View File

@ -28,7 +28,7 @@ pub struct ImageDosHeader {
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Serialize)] #[derive(Debug, Clone, Copy, Serialize)]
pub struct ImageNTHeaders32 { pub struct ImageNTHeader32 {
pub signature: u32, pub signature: u32,
pub file_header: ImageFileHeader, pub file_header: ImageFileHeader,
pub optional_header: ImageOptionalHeader32, pub optional_header: ImageOptionalHeader32,
@ -36,7 +36,7 @@ pub struct ImageNTHeaders32 {
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, Serialize)] #[derive(Debug, Clone, Copy, Serialize)]
pub struct ImageNTHeaders64 { pub struct ImageNTHeader64 {
pub signature: u32, pub signature: u32,
pub file_header: ImageFileHeader, pub file_header: ImageFileHeader,
pub optional_header: ImageOptionalHeader64, pub optional_header: ImageOptionalHeader64,
@ -44,9 +44,9 @@ pub struct ImageNTHeaders64 {
#[derive(Serialize, Clone, Copy)] #[derive(Serialize, Clone, Copy)]
#[serde(untagged)] #[serde(untagged)]
pub enum ImageNTHeaders{ pub enum ImageNTHeader{
NTHeaders32(ImageNTHeaders32), NTHeader32(ImageNTHeader32),
NTHeaders64(ImageNTHeaders64), NTHeader64(ImageNTHeader64),
} }
#[repr(C)] #[repr(C)]

View File

@ -1,7 +1,7 @@
use super::{error::PEParseError, header::*}; use super::{error::PEParseError, header::*};
use std::{ops::{Deref, DerefMut}, option}; use std::ops::Deref;
pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized { pub trait ReadOnlyPE: Deref<Target = [u8]> + Sized {
/// Get a reference to a type at a given offset. /// Get a reference to a type at a given offset.
fn get_ref<T>(&self, offset: usize) -> Result<&T, PEParseError> { fn get_ref<T>(&self, offset: usize) -> Result<&T, PEParseError> {
let len = std::mem::size_of::<T>(); let len = std::mem::size_of::<T>();
@ -12,16 +12,6 @@ pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized {
Ok(unsafe { &*ptr }) Ok(unsafe { &*ptr })
} }
/// Get a mutable reference to a type at a given offset.
fn get_mut<T>(&mut self, offset: usize) -> Result<&mut T, PEParseError> {
let len = std::mem::size_of::<T>();
if offset + len > self.len() {
return Err(PEParseError::OutOfBounds);
}
let ptr = self.as_mut_ptr().wrapping_offset(offset as isize) as *mut T;
Ok(unsafe { &mut *ptr })
}
// 判断是64位还是32位 // 判断是64位还是32位
fn is_64_bit(&self) -> Result<bool, PEParseError> { fn is_64_bit(&self) -> Result<bool, PEParseError> {
// 先以32位加载可选头通过可选头的Magic字段判断 // 先以32位加载可选头通过可选头的Magic字段判断
@ -30,36 +20,27 @@ pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized {
Ok(is_64_bit) Ok(is_64_bit)
} }
/// Get the DOS header without verifying its contents. /// Get the DOS header without verifying its contents.
fn get_dos_header(&self) -> Result<&ImageDosHeader, PEParseError> { fn get_dos_header(&self) -> Result<&ImageDosHeader, PEParseError> {
let result = self.get_ref::<ImageDosHeader>(0)?; let result = self.get_ref::<ImageDosHeader>(0)?;
Ok(result) Ok(result)
} }
/// Get the DOS header without mutating its contents.
fn get_dos_header_mut(&mut self) -> Result<&mut ImageDosHeader, PEParseError> {
let result = self.get_mut::<ImageDosHeader>(0)?;
Ok(result)
}
fn get_nt_header32(&self) -> Result<&ImageNTHeader32, PEParseError> {
fn get_nt_headers32(&self) -> Result<&ImageNTHeaders32, PEParseError> {
let nt_offset = self.get_nt_headers_offset()?; let nt_offset = self.get_nt_headers_offset()?;
let result = self.get_ref::<ImageNTHeaders32>(nt_offset)?; let result = self.get_ref::<ImageNTHeader32>(nt_offset)?;
Ok(result) Ok(result)
} }
fn get_nt_headers64(&self) -> Result<&ImageNTHeaders64, PEParseError> { fn get_nt_header64(&self) -> Result<&ImageNTHeader64, PEParseError> {
let nt_offset = self.get_nt_headers_offset()?; let nt_offset = self.get_nt_headers_offset()?;
let result = self.get_ref::<ImageNTHeaders64>(nt_offset)?; let result = self.get_ref::<ImageNTHeader64>(nt_offset)?;
Ok(result) Ok(result)
} }
fn get_nt_headers(&self) -> Result<ImageNTHeaders, PEParseError> { fn get_nt_header(&self) -> Result<ImageNTHeader, PEParseError> {
let is_64_bit = self.is_64_bit()?; let is_64_bit = self.is_64_bit()?;
let result = match is_64_bit { let result = match is_64_bit {
true => ImageNTHeaders::NTHeaders64(*self.get_nt_headers64()?), true => ImageNTHeader::NTHeader64(*self.get_nt_header64()?),
false => ImageNTHeaders::NTHeaders32(*self.get_nt_headers32()?), false => ImageNTHeader::NTHeader32(*self.get_nt_header32()?),
}; };
Ok(result) Ok(result)
} }
@ -71,13 +52,6 @@ pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized {
Ok(nt_offset) Ok(nt_offset)
} }
/// Get the NT headers without mutating its contents.
fn get_nt_headers_mut(&mut self) -> Result<&mut ImageNTHeaders32, PEParseError> {
let nt_offset = self.get_nt_headers_offset()?;
let result = self.get_mut::<ImageNTHeaders32>(nt_offset)?;
Ok(result)
}
// 获取文件头的偏移 // 获取文件头的偏移
fn get_file_header_offset(&self) -> Result<usize, PEParseError> { fn get_file_header_offset(&self) -> Result<usize, PEParseError> {
// 1. 获取nt头偏移 // 1. 获取nt头偏移
@ -93,12 +67,6 @@ pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized {
let result = self.get_ref::<ImageFileHeader>(file_header_offset)?; let result = self.get_ref::<ImageFileHeader>(file_header_offset)?;
Ok(result) Ok(result)
} }
// 获取文件头数据
fn get_file_header_mut(&mut self) -> Result<&mut ImageFileHeader, PEParseError> {
let file_header_offset = self.get_file_header_offset()?;
let result = self.get_mut::<ImageFileHeader>(file_header_offset)?;
Ok(result)
}
// 获取节区数量 // 获取节区数量
fn get_number_of_sections(&self) -> Result<usize, PEParseError> { fn get_number_of_sections(&self) -> Result<usize, PEParseError> {
@ -147,7 +115,8 @@ pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized {
// 节区头偏移在可选头之后,可选头大小是可变的,所以需要计算 // 节区头偏移在可选头之后,可选头大小是可变的,所以需要计算
let optional_header_size = self.get_size_of_optional_header()?; let optional_header_size = self.get_size_of_optional_header()?;
let file_header_offset = self.get_file_header_offset()?; let file_header_offset = self.get_file_header_offset()?;
let section_header_offset = file_header_offset + std::mem::size_of::<ImageFileHeader>() + optional_header_size; let section_header_offset =
file_header_offset + std::mem::size_of::<ImageFileHeader>() + optional_header_size;
Ok(section_header_offset) Ok(section_header_offset)
} }
@ -159,7 +128,10 @@ pub trait PE: Deref<Target = [u8]> + DerefMut<Target = [u8]> + Sized {
let section_header_offset = self.get_section_headers_offset()?; let section_header_offset = self.get_section_headers_offset()?;
// 3. 获取节区头数据 // 3. 获取节区头数据
unsafe { unsafe {
let ptr = self.as_ptr().wrapping_offset(section_header_offset as isize) as *const ImageSectionHeader; let ptr = self
.as_ptr()
.wrapping_offset(section_header_offset as isize)
as *const ImageSectionHeader;
let result = std::slice::from_raw_parts(ptr, number_of_sections); let result = std::slice::from_raw_parts(ptr, number_of_sections);
Ok(result) Ok(result)
} }

View File

@ -1,103 +1,11 @@
use memmap2::*; use memmap2::*;
use serde::Serialize;
use crate::{app_error::AppError, pe_parse::{header::{ImageDosHeader, ImageFileHeader, ImageNTHeaders, ImageNTHeaders32, ImageOptionalHeader, ImageSectionHeader}, pe::PE}};
use super::{GLOBAL_FILE_DATA, GLOBAL_IS_64_BIT};
/// 以只读的方式创建文件映射
pub fn mmap_mut_file(file_path: &str) -> Result<Mmap, std::io::Error> { pub fn mmap_mut_file(file_path: &str) -> Result<Mmap, std::io::Error> {
let file = std::fs::OpenOptions::new().read(true).open(file_path)?; let file = std::fs::OpenOptions::new().read(true).open(file_path)?;
unsafe { unsafe {
MmapOptions::new() MmapOptions::new()
.map(&file) .map(&file)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
} }
} }
#[derive(Serialize)]
pub struct ResponseDOSHeaderData {
pub fields: ImageDosHeader,
pub base_offset: usize,
}
#[derive(Serialize)]
pub struct ResponseNTHeaderData {
fields: ImageNTHeaders,
base_offset: usize,
}
pub fn get_nt_headers_data<'a>() -> Result<ResponseNTHeaderData, AppError> {
// pub static GLOBAL_FILE_DATA: Mutex<Option<PeData>> = Mutex::new(None);
let binding = GLOBAL_FILE_DATA.lock().unwrap();
// `binding` does not live long enough borrowed value does not live long enough
let file_data = binding.as_ref().unwrap();
let nt_header_enmu= file_data.get_nt_headers()?;
let nt_offset = file_data.get_nt_headers_offset()?;
let result = ResponseNTHeaderData {
fields: nt_header_enmu,
base_offset: nt_offset,
};
Ok(result)
}
// 获取文件头数据
#[derive(Serialize)]
pub struct ResponseFileHeaderData {
pub fields: ImageFileHeader,
pub base_offset: usize,
}
pub fn get_file_header_data() -> Result<ResponseFileHeaderData, AppError> {
let binding = GLOBAL_FILE_DATA.lock().unwrap();
let file_data = binding.as_ref().unwrap();
let file_header = file_data.get_file_header()?;
let file_header_offset = file_data.get_file_header_offset()?;
let result = ResponseFileHeaderData {
fields: file_header.clone(),
base_offset: file_header_offset,
};
Ok(result)
}
// 获取可选头数据
#[derive(Serialize)]
pub struct ResponseOptionalHeaderData {
pub fields: ImageOptionalHeader,
pub base_offset: usize,
pub is_64_bit: bool,
}
pub fn get_optional_header_data() -> Result<ResponseOptionalHeaderData, AppError> {
let binding = GLOBAL_FILE_DATA.lock().unwrap();
let file_data = binding.as_ref().unwrap();
let optional_header = file_data.get_optional_header()?;
let optional_header_offset = file_data.get_optional_header_offset()?;
let is_64_bit = file_data.is_64_bit()?;
let result = ResponseOptionalHeaderData {
fields: optional_header.clone(),
base_offset: optional_header_offset,
is_64_bit,
};
Ok(result)
}
// 获取节区头数据
#[derive(Serialize)]
pub struct ResponseSectionHeaderData {
pub fields: Vec<ImageSectionHeader>,
pub base_offset: usize,
}
pub fn get_section_header_data() -> Result<ResponseSectionHeaderData, AppError> {
let binding = GLOBAL_FILE_DATA.lock().unwrap();
let file_data = binding.as_ref().unwrap();
let section_headers = file_data.get_section_headers()?;
let section_headers_offset = file_data.get_section_headers_offset()?;
let vec_section_headers = section_headers.to_vec();
let result = ResponseSectionHeaderData {
fields: vec_section_headers,
base_offset: section_headers_offset,
};
Ok(result)
}

View File

@ -1,32 +1,6 @@
use memmap2::{Mmap, MmapMut}; use memmap2::Mmap;
use std::{ use crate::pe_parse::pe::ReadOnlyPE;
ops::{Deref, DerefMut},
sync::Mutex,
};
use crate::pe_parse::pe::PE;
pub mod file; pub mod file;
// 全局文件路径 // 为文件映射实现PE结构
pub static mut GLOBAL_FILE_PATH: Option<String> = None; impl ReadOnlyPE for Mmap {}
// 文件是否是64位
pub static mut GLOBAL_IS_64_BIT: Option<bool> = None;
pub(crate) struct PeData(pub Vec<u8>);
// 实现Deref
impl Deref for PeData {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
// 实现DerefMut
impl DerefMut for PeData {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl PE for PeData {}
// 文件的内存映射clone
pub static GLOBAL_FILE_DATA: Mutex<Option<PeData>> = Mutex::new(None);

View File

@ -15,7 +15,7 @@
"label": "main", "label": "main",
"title": "My CFF", "title": "My CFF",
"width": 1000, "width": 1000,
"height": 800, "height": 600,
"visible": false "visible": false
}, },
{ {