diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..733dbcc --- /dev/null +++ b/src/main.rs @@ -0,0 +1,200 @@ +#![feature(array_chunks)] +use anyhow::{Result, bail}; +use glam::Vec3; +use log::{debug, error}; +use prost::{Message, bytes::Bytes}; +use proto::{BulkMetadata, NodeData, NodeMetadata, PlanetoidMetadata}; +use reqwest::{ + Client, + header::{HeaderMap, HeaderName, HeaderValue}, +}; +use std::path::PathBuf; +use tokio::{ + fs::{File, create_dir, create_dir_all}, + io::{AsyncReadExt, AsyncWriteExt}, +}; +use weareshared::{ + Affine3A, Vec3A, + resources::{MeshPart, Prefab, RespackEntry}, + respack::save_full_respack, + store::ResourceStore, + vec3a, +}; + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::init_from_env("LOG"); + + let c = GeClient::new().await?; + let entry = c.planetoid_metdata().await?; + + eprintln!("{:#?}", entry); + + let mut bulk = c + .bulk_metadata("", entry.root_node_metadata.unwrap().bulk_metadata_epoch()) + .await?; + + eprintln!("{:#?}", bulk); + + let store = ResourceStore::new_memory(); + + let mut meshes = Vec::new(); + + for node_meta in &bulk.node_metadata { + let Ok(mut node_data) = c.node_data(&bulk, &node_meta).await else { + continue; + }; + + for m in node_data.meshes { + let mut index_strip = Vec::new(); + + let strip_len = m.indices[0]; + let mut zeros = 0; + for i in 0..strip_len { + let val = m.indices[i as usize + 1]; + index_strip.push((zeros - val) as u32); + if val == 0 { + zeros += 1; + } + } + let mut index = Vec::new(); + for i in 0..index_strip.len() - 2 { + if i & 1 == 0 { + index.push([index_strip[i + 0], index_strip[i + 1], index_strip[i + 2]]); + } else { + index.push([index_strip[i + 0], index_strip[i + 2], index_strip[i + 1]]); + } + } + + let mut positions = Vec::new(); + let vert = m.vertices(); + let vertex_count = vert.len() / 3; + let (mut x, mut y, mut z) = (0u8, 0u8, 0u8); + for i in 0..vertex_count { + x = x.wrapping_add(vert[vertex_count * 0 + i]); + y = y.wrapping_add(vert[vertex_count * 1 + i]); + z = z.wrapping_add(vert[vertex_count * 2 + i]); + positions.push(vec3a(x as f32, y as f32, z as f32)); + } + // eprintln!("{index:?}"); + // eprintln!("{positions:?}"); + + meshes.push(( + Affine3A::from_scale(Vec3::splat(0.01)), + store.set(&MeshPart { + index: Some(store.set(&index)?), + va_position: Some(store.set(&positions)?), + g_double_sided: Some(()), + ..Default::default() + })?, + )) + } + } + eprintln!("{}", meshes.len()); + + let prefab = store.set(&Prefab { + mesh: meshes, + ..Default::default() + })?; + + let entry = store.set(&RespackEntry { + c_prefab: vec![prefab], + ..Default::default() + })?; + + let file = std::fs::File::create("/tmp/a.respack")?; + save_full_respack(file, &store, Some(entry))?; + + Ok(()) +} + +struct GeClient { + client: Client, + cachedir: PathBuf, +} +impl GeClient { + pub async fn new() -> Result<Self> { + let cachedir = xdg::BaseDirectories::with_prefix("weareearth") + .unwrap() + .create_cache_directory("download") + .unwrap(); + create_dir_all(cachedir.join("BulkMetadata")).await?; + create_dir_all(cachedir.join("NodeData")).await?; + Ok(Self { + cachedir, + client: Client::builder().default_headers(HeaderMap::from_iter([ + (HeaderName::from_static("user-agent"), HeaderValue::from_static("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36")), + (HeaderName::from_static("referer"), HeaderValue::from_static("https://earth.google.com/")) + ])).build().unwrap() + }) + } + pub async fn download(&self, path: &str) -> Result<Bytes> { + let cachepath = self.cachedir.join(path); + if cachepath.exists() { + debug!("cached {path:?}"); + let mut buf = Vec::new(); + File::open(cachepath).await?.read_to_end(&mut buf).await?; + Ok(buf.into()) + } else { + debug!("download {path:?}"); + let res = self + .client + .get(format!("https://kh.google.com/rt/earth/{path}")) + .send() + .await?; + if !res.status().is_success() { + error!("error response: {:?}", res.text().await?); + bail!("error response") + } + let buf = res.bytes().await?; + File::create(cachepath).await?.write_all(&buf).await?; + Ok(buf) + } + } + + pub async fn planetoid_metdata(&self) -> Result<PlanetoidMetadata> { + let buf = self.download("PlanetoidMetadata").await?; + Ok(PlanetoidMetadata::decode(buf)?) + } + pub async fn bulk_metadata(&self, path: &str, epoch: u32) -> Result<BulkMetadata> { + let buf = self + .download(&format!("BulkMetadata/pb=!1m2!1s{path}!2u{epoch}")) + .await?; + Ok(BulkMetadata::decode(buf)?) + } + pub async fn node_data(&self, bulk: &BulkMetadata, node: &NodeMetadata) -> Result<NodeData> { + let (path, flags) = unpack_path_and_id(node.path_and_flags()); + + let texture_format = bulk.default_available_texture_formats(); + let imagery_epoch = node.imagery_epoch.unwrap_or(bulk.default_imagery_epoch()); + let node_epoch = bulk.head_node_key.as_ref().unwrap().epoch.unwrap(); // ? + + let image_epoch_part = if flags & 16 != 0 { + format!("!3u{imagery_epoch}") + } else { + String::new() + }; + let url = format!( + "NodeData/pb=!1m2!1s{path}!2u{node_epoch}!2e{texture_format}{image_epoch_part}!4b0" + ); + + let buf = self.download(&url).await?; + Ok(NodeData::decode(buf)?) + } +} + +fn unpack_path_and_id(mut path_id: u32) -> (String, u32) { + let level = 1 + (path_id & 3); + path_id >>= 2; + let mut path = String::new(); + for _ in 0..level { + path += &(path_id & 7).to_string(); + path_id >>= 3; + } + let flags = path_id; + (path, flags) +} + +pub mod proto { + include!(concat!(env!("OUT_DIR"), "/earth.proto.rs")); +} |