aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs200
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"));
+}