#include "oto_kayit.hpp" #include "malzeme_io.hpp" #include #include #include #include #include #include #include "dosya_islemleri.hpp" // yolAc (kurtarılan belgeyi aç) #include "imgui.h" #include "ui_ortak.hpp" #include "uygulama_baglam.hpp" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/gecici.hpp" #include "fiko/cekirdek/gorev.hpp" #include "fiko/core/gunluk.hpp" #include "fiko/io/fiko_format.hpp" namespace fs = std::filesystem; namespace fiko { namespace { // Kirlilik = MODEL değişimi. Seçim/hover/görev/tema gibi görünüm olayları // bilerek DIŞARIDA — aksi halde yalnız gezinen kullanıcıda da anlık görüntü alınır // (dev modelde bedava değil). constexpr OlayTuru kKirliTurler[] = { OlayTuru::VarlikEklendi, OlayTuru::VarlikSilindi, OlayTuru::DonusumDegisti, OlayTuru::BrepDegisti, OlayTuru::GorunurlukDegisti, OlayTuru::RenkDegisti, OlayTuru::OlcumEklendi, OlayTuru::OlcumSilindi, OlayTuru::ParametreDegisti, OlayTuru::FeatureEklendi, OlayTuru::FeatureGuncellendi, OlayTuru::FeatureSilindi, OlayTuru::TelEklendi, OlayTuru::YuzeyEklendi, OlayTuru::SketchCozuldu, OlayTuru::CamOpEklendi, OlayTuru::CamOpSilindi, OlayTuru::CamIslemEklendi, OlayTuru::CamIslemSilindi, OlayTuru::HiyerarsiDegisti, }; u64 simdiSaniye() { return (u64)std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); } bool surecCanliMi(unsigned long pid) { if (pid == 0) return false; HANDLE h = OpenProcess(SYNCHRONIZE, FALSE, (DWORD)pid); if (!h) return false; // erişilemiyor ≈ ölü (yetki farkı nadir; kurtarma teklifi zararsız) const bool canli = WaitForSingleObject(h, 0) == WAIT_TIMEOUT; CloseHandle(h); return canli; } // oturum.txt — satır başına "anahtar=değer" (yol içinde '=' olabilir → ilk '=' ayırır). void metaYaz(const fs::path& dizin, const std::string& kaynakYol, const std::string& belgeAd) { std::ofstream o(dizin / "oturum.txt", std::ios::trunc); if (!o) return; o << "surum=1\n" << "pid=" << GetCurrentProcessId() << "\n" << "zaman=" << simdiSaniye() << "\n" << "ad=" << belgeAd << "\n" << "yol=" << kaynakYol << "\n"; } void metaOku(const fs::path& dizin, std::string& kaynakYol, std::string& belgeAd, unsigned long& pid, u64& zaman) { std::ifstream i(dizin / "oturum.txt"); std::string satir; while (std::getline(i, satir)) { const size_t e = satir.find('='); if (e == std::string::npos) continue; const std::string a = satir.substr(0, e), d = satir.substr(e + 1); if (a == "yol") kaynakYol = d; else if (a == "ad") belgeAd = d; else if (a == "pid") pid = std::strtoul(d.c_str(), nullptr, 10); else if (a == "zaman") zaman = std::strtoull(d.c_str(), nullptr, 10); } } std::string dosyaTaban(const std::string& yol) { std::string ad = yol; if (const size_t s = ad.find_last_of("/\\"); s != std::string::npos) ad = ad.substr(s + 1); if (const size_t d = ad.find_last_of('.'); d != std::string::npos) ad = ad.substr(0, d); return ad; } std::string zamanMetni(u64 unixSn) { const std::time_t t = (std::time_t)unixSn; std::tm tm{}; localtime_s(&tm, &t); char buf[32]; std::strftime(buf, sizeof(buf), "%d.%m.%Y %H:%M", &tm); return buf; } } // namespace bool& otoKayitAktif() { static bool v = true; return v; } f64& otoKayitDakika() { static f64 v = 5.0; return v; } int& otoKayitKusakSayisi() { static int v = 3; return v; } OtoKayit& otoKayit() { static OtoKayit s; return s; } void OtoKayit::bagla(UygulamaBaglam& b) { b_ = &b; const fs::path kok = gecici::altDizin("oturum"); const fs::path benim = kok / std::to_string(GetCurrentProcessId()); std::error_code ec; fs::create_directories(benim, ec); oturumDizin_ = benim.string(); // Model değişimi → kirli işareti (tek atom; sıcak yolda tarama yok). b.cekirdek.olaylar().aboneFiltreli( [](const OlayKaydi& k) { for (OlayTuru t : kKirliTurler) if (t == k.tur) return true; return false; }, [this](const OlayKaydi&) { kirli_ = true; }); // ---- Çökmüş oturumları tara: duran dizin + ölü PID = kurtarılabilir ---- for (fs::directory_iterator it(kok, ec), son; it != son; it.increment(ec)) { if (ec) break; std::error_code e2; if (!it->is_directory(e2) || it->path() == benim) continue; const unsigned long dizinPid = std::strtoul(it->path().filename().string().c_str(), nullptr, 10); if (surecCanliMi(dizinPid)) continue; // başka bir örnek çalışıyor → dokunma // En yeni anlık görüntüyü seç. fs::path enYeni; fs::file_time_type enYeniZaman{}; u64 boyut = 0; for (fs::directory_iterator f(it->path(), e2), fson; f != fson; f.increment(e2)) { if (e2) break; if (f->path().extension() != ".fikocad") continue; const auto z = f->last_write_time(e2); if (enYeni.empty() || z > enYeniZaman) { enYeni = f->path(); enYeniZaman = z; boyut = (u64)f->file_size(e2); } } if (enYeni.empty()) { // anlık görüntüsüz artık dizin → temizle fs::remove_all(it->path(), e2); continue; } KurtarmaAdayi a; a.anlikYol = enYeni.string(); a.oturumDizin = it->path().string(); a.boyut = boyut; unsigned long metaPid = 0; metaOku(it->path(), a.kaynakYol, a.belgeAd, metaPid, a.zaman); if (a.belgeAd.empty()) a.belgeAd = a.kaynakYol.empty() ? "Adsız belge" : dosyaTaban(a.kaynakYol); adaylar_.push_back(std::move(a)); } std::sort(adaylar_.begin(), adaylar_.end(), [](const KurtarmaAdayi& x, const KurtarmaAdayi& y) { return x.zaman > y.zaman; }); kurtarmaSorAcik = !adaylar_.empty(); if (kurtarmaSorAcik) gunluk::uyari("Kurtarılabilir oturum bulundu: {} adet", adaylar_.size()); } void OtoKayit::kurtarmaBasla(const std::string& kaynakYol) { kurtarmaKaynak_ = kaynakYol; kurtarmaBekliyor_ = true; } void OtoKayit::belgeYolu(const std::string& yol) { // Kurtarma yüklemesi: yüklenen dosya geçici anlık görüntüdür; kaynak kullanıcının // dosyasıdır (yoksa adsız kalır) — aksi halde bir sonraki oto-kayıt metası kendi // geçici dosyamızı "kaynak" sanar ve MRU'ya geçici yol sızar. if (kurtarmaBekliyor_ && gecici::kokIcindeMi(yol)) { kurtarmaBekliyor_ = false; kaynakYol_ = kurtarmaKaynak_; kirli_ = false; gecen_ = 0; if (!oturumDizin_.empty()) metaYaz(oturumDizin_, kaynakYol_, kaynakYol_.empty() ? "Kurtarılan belge" : dosyaTaban(kaynakYol_)); return; } kaynakYol_ = yol; kirli_ = false; // taze açıldı/kaydedildi → kayıp yok gecen_ = 0; if (!oturumDizin_.empty()) metaYaz(oturumDizin_, kaynakYol_, dosyaTaban(yol)); } void OtoKayit::kare(f32 dt) { if (!otoKayitAktif() || !kirli_ || yaziliyor_ || oturumDizin_.empty()) return; gecen_ += dt; const f32 esik = (f32)std::max(0.5, otoKayitDakika()) * 60.0f; if (gecen_ < esik) return; anlikAl("süre"); } void OtoKayit::simdiKaydet() { if (oturumDizin_.empty() || yaziliyor_) return; anlikAl("elle"); } void OtoKayit::anlikAl(const char* neden) { if (!b_) return; UygulamaBaglam& b = *b_; // Kullanıcının kendi kaydı sürerken araya girme (aynı OCCT gövdeleri iki kez // serileştirilmesin; disk/CPU çakışması). for (const auto& g : b.cekirdek.gorevler().anlik()) if (g.baslik.rfind("Belge", 0) == 0) return; const u32 kusakSay = (u32)std::max(1, otoKayitKusakSayisi()); const fs::path hedef = fs::path(oturumDizin_) / ("anlik-" + std::to_string(kusak_ % kusakSay + 1) + ".fikocad"); kusak_ = (kusak_ + 1) % kusakSay; gecen_ = 0; kirli_ = false; // bu andan sonraki değişiklik yeni turu tetikler yaziliyor_ = true; // ANA-THREAD ucuz snapshot (brepErtele) — fikoKaydetKomut kalıbı; ağır iş worker'da. auto belge = std::make_shared( io::belgeCikar(b.sahne, b.kamera, &b.partD, /*brepErtele=*/true, &b.olcum, &b.pmiSis)); malzemeleriBelgeyeYaz(b.malzemeKut, b.malzemeAta, *belge); // malzeme + atamalar (v1.16 MLZM) metaYaz(oturumDizin_, kaynakYol_, kaynakYol_.empty() ? "Adsız belge" : dosyaTaban(kaynakYol_)); const std::string yol = hedef.string(); auto tamam = std::make_shared(false); b.cekirdek.gorevler().kuyrukla( "Oto-kayıt", [belge, yol, tamam](GorevBaglam& g) { // WORKER: serialize + sıkıştır + atomik yaz std::string hata; if (!io::belgeBrepSerilestir(*belge, {}, [&] { return g.iptalIstendi(); })) return; std::string veri; if (!io::fikoYaz(*belge, veri, hata)) return; if (g.iptalIstendi()) return; *tamam = io::dosyayaYaz(yol, veri, hata); }, [this, yol, tamam, neden] { yaziliyor_ = false; if (*tamam) gunluk::bilgi("Oto-kayıt ({}): {}", neden, yol); else gunluk::uyari("Oto-kayıt yazılamadı: {}", yol); }); } void OtoKayit::temizKapanis() { if (oturumDizin_.empty()) return; std::error_code ec; fs::remove_all(oturumDizin_, ec); // dizin yoksa = temiz kapanış işareti oturumDizin_.clear(); } void OtoKayit::adaylariUnut() { std::error_code ec; for (const KurtarmaAdayi& a : adaylar_) fs::remove_all(a.oturumDizin, ec); adaylar_.clear(); kurtarmaSorAcik = false; } // ---- Kurtarma penceresi (DM tarzı: toolbar yok, üç net eylem) --------------- void otoKayitKurtarmaCiz(UygulamaBaglam& b) { OtoKayit& ok = otoKayit(); if (!ok.kurtarmaSorAcik || ok.adaylar().empty()) return; ImGui::SetNextWindowSize({520, 0}, ImGuiCond_Appearing); ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, {0.5f, 0.5f}); bool acik = true; if (ImGui::Begin("⚠ Kurtarılabilir çalışma bulundu", &acik, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::TextWrapped( "Önceki oturum düzgün kapanmamış. Otomatik kayıttan aşağıdaki anlık görüntü " "kurtarılabilir — açtıktan sonra kendi dosyanıza kaydetmeyi unutmayın."); ImGui::Separator(); for (const KurtarmaAdayi& a : ok.adaylar()) { ImGui::PushID(a.anlikYol.c_str()); ImGui::Text("%s", a.belgeAd.c_str()); ImGui::SameLine(); ImGui::TextDisabled("(%s · %.1f MB)", zamanMetni(a.zaman).c_str(), (f64)a.boyut / (1024.0 * 1024.0)); if (!a.kaynakYol.empty()) ImGui::TextDisabled("%s", a.kaynakYol.c_str()); if (ImGui::Button("Kurtar")) { const std::string yol = a.anlikYol, kaynak = a.kaynakYol; ok.kurtarmaSorAcik = false; ok.kurtarmaBasla(kaynak); // Ctrl+S hedefi kullanıcının dosyası kalsın yolAc(b, yol); ImGui::PopID(); ImGui::End(); return; } ImGui::PopID(); ImGui::Separator(); } if (ImGui::Button("Sil")) ok.adaylariUnut(); ImGui::SameLine(); if (ImGui::Button("Sonra")) ok.kurtarmaSorAcik = false; } ImGui::End(); if (!acik) ok.kurtarmaSorAcik = false; } } // namespace fiko