diff --git a/mechanic-pwa/frontend/index.html b/mechanic-pwa/frontend/index.html index 0919561..1f1da65 100644 --- a/mechanic-pwa/frontend/index.html +++ b/mechanic-pwa/frontend/index.html @@ -3,8 +3,16 @@ + + + + + - + + + + Premium Механик diff --git a/mechanic-pwa/frontend/public/favicon.svg b/mechanic-pwa/frontend/public/favicon.svg index 6893eb1..fb2b1b7 100644 --- a/mechanic-pwa/frontend/public/favicon.svg +++ b/mechanic-pwa/frontend/public/favicon.svg @@ -1 +1,10 @@ - \ No newline at end of file + + + + + + + diff --git a/mechanic-pwa/frontend/public/icons/apple-touch-icon.png b/mechanic-pwa/frontend/public/icons/apple-touch-icon.png new file mode 100644 index 0000000..b601f73 Binary files /dev/null and b/mechanic-pwa/frontend/public/icons/apple-touch-icon.png differ diff --git a/mechanic-pwa/frontend/public/icons/favicon-16.png b/mechanic-pwa/frontend/public/icons/favicon-16.png new file mode 100644 index 0000000..e54ce0c Binary files /dev/null and b/mechanic-pwa/frontend/public/icons/favicon-16.png differ diff --git a/mechanic-pwa/frontend/public/icons/favicon-32.png b/mechanic-pwa/frontend/public/icons/favicon-32.png new file mode 100644 index 0000000..e8e2179 Binary files /dev/null and b/mechanic-pwa/frontend/public/icons/favicon-32.png differ diff --git a/mechanic-pwa/frontend/public/icons/favicon-48.png b/mechanic-pwa/frontend/public/icons/favicon-48.png new file mode 100644 index 0000000..19c30a5 Binary files /dev/null and b/mechanic-pwa/frontend/public/icons/favicon-48.png differ diff --git a/mechanic-pwa/frontend/public/icons/icon-192.png b/mechanic-pwa/frontend/public/icons/icon-192.png index 452ea36..ec592ba 100644 Binary files a/mechanic-pwa/frontend/public/icons/icon-192.png and b/mechanic-pwa/frontend/public/icons/icon-192.png differ diff --git a/mechanic-pwa/frontend/public/icons/icon-512.png b/mechanic-pwa/frontend/public/icons/icon-512.png index 946d039..29da25a 100644 Binary files a/mechanic-pwa/frontend/public/icons/icon-512.png and b/mechanic-pwa/frontend/public/icons/icon-512.png differ diff --git a/mechanic-pwa/frontend/public/icons/icon-maskable-512.png b/mechanic-pwa/frontend/public/icons/icon-maskable-512.png new file mode 100644 index 0000000..d6cfa55 Binary files /dev/null and b/mechanic-pwa/frontend/public/icons/icon-maskable-512.png differ diff --git a/mechanic-pwa/frontend/public/manifest.webmanifest b/mechanic-pwa/frontend/public/manifest.webmanifest index b0edfb2..8bf04ee 100644 --- a/mechanic-pwa/frontend/public/manifest.webmanifest +++ b/mechanic-pwa/frontend/public/manifest.webmanifest @@ -4,10 +4,11 @@ "start_url": "/", "display": "standalone", "orientation": "portrait", - "background_color": "#f5f1ea", - "theme_color": "#2a2a2a", + "background_color": "#000000", + "theme_color": "#000000", "icons": [ - { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, - { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" } + { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, + { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, + { "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } ] } diff --git a/mechanic-pwa/frontend/src/App.tsx b/mechanic-pwa/frontend/src/App.tsx index 34bc5f4..72df7f7 100644 --- a/mechanic-pwa/frontend/src/App.tsx +++ b/mechanic-pwa/frontend/src/App.tsx @@ -6,6 +6,7 @@ import Home from "@/pages/Home"; import VehicleCard from "@/pages/VehicleCard"; import InspectionEditor from "@/pages/InspectionEditor"; import InspectionReview from "@/pages/InspectionReview"; +import TtStateDetail from "@/pages/TtStateDetail"; function RequireAuth({ children }: { children: React.ReactNode }) { const token = useAuth((s) => s.token); @@ -22,6 +23,7 @@ export default function App() { } /> } /> } /> + } /> } /> ); diff --git a/mechanic-pwa/frontend/src/api/auth.ts b/mechanic-pwa/frontend/src/api/auth.ts index 7cad487..2d1c152 100644 --- a/mechanic-pwa/frontend/src/api/auth.ts +++ b/mechanic-pwa/frontend/src/api/auth.ts @@ -23,6 +23,8 @@ export async function login(input: LoginInput): Promise { const body = new URLSearchParams(); body.set("username", input.username); body.set("password", input.password); + // PWA — длинная сессия (30 дней) чтобы механики не вводили пароль каждые 8 ч. + body.set("long_session", "true"); const res = await fetch("/api/auth/login", { method: "POST", @@ -42,7 +44,7 @@ export async function login2fa(input: TwoFactorInput): Promise { const res = await fetch("/api/auth/2fa", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(input), + body: JSON.stringify({ ...input, long_session: true }), credentials: "include", // accept the trusted_device cookie on Set-Cookie }); if (!res.ok) { diff --git a/mechanic-pwa/frontend/src/api/inspections.ts b/mechanic-pwa/frontend/src/api/inspections.ts index a921650..b933f09 100644 --- a/mechanic-pwa/frontend/src/api/inspections.ts +++ b/mechanic-pwa/frontend/src/api/inspections.ts @@ -27,6 +27,7 @@ export interface PhotoSummary { slot_index: number; storage_key: string; thumb_key?: string | null; + annotated_key?: string | null; width?: number | null; height?: number | null; status: string; @@ -37,6 +38,7 @@ export interface MarkerSummary { id: number; inspection_id: number; photo_id?: number | null; + tt_image_id?: string | null; side?: string | null; x?: number | null; y?: number | null; @@ -103,4 +105,12 @@ export async function createMarker( .json(); } +export async function deleteInspection(inspectionId: number): Promise { + await api.delete(`inspections/${inspectionId}`); +} + +export async function deleteMarker(markerId: number): Promise { + await api.delete(`markers/${markerId}`); +} + export type { InspectionSummary }; diff --git a/mechanic-pwa/frontend/src/api/photos.ts b/mechanic-pwa/frontend/src/api/photos.ts index bf3b570..87b790a 100644 --- a/mechanic-pwa/frontend/src/api/photos.ts +++ b/mechanic-pwa/frontend/src/api/photos.ts @@ -14,12 +14,27 @@ export interface PhotoConfirmResponse { slot_index: number; storage_key: string; thumb_key?: string | null; + annotated_key?: string | null; width?: number | null; height?: number | null; status: string; taken_at: string; } +export async function uploadPhotoAnnotation( + inspectionId: number, + photoId: number, + blob: Blob, +): Promise { + const form = new FormData(); + form.append("file", blob, "annotation.jpg"); + return api + .post(`inspections/${inspectionId}/photos/${photoId}/annotation`, { + body: form, + }) + .json(); +} + export async function requestUploadUrl( inspectionId: number, args: { side: string; slot_index: number; content_type: string; size: number } diff --git a/mechanic-pwa/frontend/src/api/vehicles.ts b/mechanic-pwa/frontend/src/api/vehicles.ts index 80fb5a7..bf2fb89 100644 --- a/mechanic-pwa/frontend/src/api/vehicles.ts +++ b/mechanic-pwa/frontend/src/api/vehicles.ts @@ -12,6 +12,8 @@ export interface VehicleSummary { export interface VehicleDetail extends VehicleSummary { recent_inspections: InspectionSummary[]; + starline_mileage?: number | null; + starline_mileage_at?: string | null; } export interface InspectionSummary { @@ -36,3 +38,74 @@ export async function listVehicles(q?: string): Promise { export async function getVehicle(id: number): Promise { return api.get(`vehicles/${id}`).json(); } + +// ── TT-Control vendor archive (Element Mechanic) ─────────────────────────── + +export interface TtStateSummary { + id: number; + vendor_state_id: string; + unix_time: number | null; + mechanic_name: string | null; + mileage: number | null; + photos_count: number; + damages_count: number; +} + +export interface TtHistory { + vendor_vehicle_id: string | null; + plate?: string | null; + brand?: string | null; + model?: string | null; + states: TtStateSummary[]; +} + +export interface TtPhotoRef { + image_id: string; + image_with_lines_id: string | null; + unix_time: number | null; + guid: string | null; +} + +export interface TtDamage { + damage_type_id: number | null; + degree: number | null; + points: { x: number; y: number }[]; + guid: string | null; + unix_time: number | null; +} + +export interface TtSidePhoto { + image_id: string; + miniature_id: string | null; + photo_type: number | null; +} + +export interface TtStateDetail { + id: number; + vendor_state_id: string; + unix_time: number | null; + mechanic_name: string | null; + mileage: number | null; + side_photos: TtSidePhoto[]; + photos_by_zone: Record; + damages_by_zone: Record; +} + +export async function getTtHistory(vehicleId: number): Promise { + return api.get(`vehicles/${vehicleId}/tt-history`).json(); +} + +export async function getTtState(stateId: number): Promise { + return api.get(`tt-states/${stateId}`).json(); +} + +export async function deleteTtState(stateId: number): Promise { + await api.delete(`tt-states/${stateId}`); +} + +export function ttImageUrl(imageId: string, withLines: boolean = false): string { + // tt-image endpoint без auth-guard'а (vendor сам публично отдаёт эти JPEG'и), + // поэтому подходит для прямого . + const base = import.meta.env.VITE_API_BASE ?? "/api/v1/mechanic"; + return `${base}/tt-image/${imageId}${withLines ? "?lines=true" : ""}`; +} diff --git a/mechanic-pwa/frontend/src/components/damage-flow/DamageClassifyDialog.tsx b/mechanic-pwa/frontend/src/components/damage-flow/DamageClassifyDialog.tsx index 588cc33..5826abe 100644 --- a/mechanic-pwa/frontend/src/components/damage-flow/DamageClassifyDialog.tsx +++ b/mechanic-pwa/frontend/src/components/damage-flow/DamageClassifyDialog.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { cn } from "@/lib/utils"; -import { loadDamageVocabulary } from "@/data/damageVocabulary"; +import { loadDamageVocabulary, damageTypeRuToEnum } from "@/data/damageVocabulary"; interface Props { open: boolean; @@ -29,8 +29,12 @@ export function DamageClassifyDialog({ open, zoneId, zoneLabel, pointsCount, onC // Salon zones (44=водительское сидение, 45=пассажирское, 46=задний диван) use salon_selectable; else exterior const isSalon = zoneId === 44 || zoneId === 45 || zoneId === 46; + // Фильтруем типы для которых нет соответствия в backend DamageType enum — + // иначе backend вернёт 422 на createMarker. const list = vocab - ? (isSalon ? vocab.salon_selectable : vocab.exterior_selectable) + ? (isSalon ? vocab.salon_selectable : vocab.exterior_selectable).filter( + (dt) => damageTypeRuToEnum(dt) != null, + ) : []; return ( diff --git a/mechanic-pwa/frontend/src/components/damage-flow/PhotoAnnotateStep.tsx b/mechanic-pwa/frontend/src/components/damage-flow/PhotoAnnotateStep.tsx new file mode 100644 index 0000000..0ca66cc --- /dev/null +++ b/mechanic-pwa/frontend/src/components/damage-flow/PhotoAnnotateStep.tsx @@ -0,0 +1,247 @@ +import { useEffect, useRef, useState } from "react"; +import { Button } from "@/components/ui/button"; + +interface Props { + photoFile: File; + zoneLabel: string; + /** Called when user submits. annotated=null если "без линий". */ + onSubmit: (annotated: Blob | null) => void | Promise; + onCancel: () => void; +} + +interface Stroke { + pts: { x: number; y: number }[]; // in image-space pixels +} + +/** Pen-on-photo step: показывает только что снятое фото, даёт обвести + * пальцем красным. Flatten в JPEG при submit. Координаты strokes в native + * пикселях фото — canvas resize'нут на тот же размер. */ +export function PhotoAnnotateStep({ photoFile, zoneLabel, onSubmit, onCancel }: Props) { + const canvasRef = useRef(null); + const imgRef = useRef(null); + const [imgLoaded, setImgLoaded] = useState(false); + const [strokes, setStrokes] = useState([]); + const currentStroke = useRef(null); + const [submitting, setSubmitting] = useState(false); + + // Load image once + useEffect(() => { + const url = URL.createObjectURL(photoFile); + const img = new Image(); + img.onload = () => { + imgRef.current = img; + const canvas = canvasRef.current; + if (canvas) { + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + } + setImgLoaded(true); + URL.revokeObjectURL(url); + redraw([]); + }; + img.onerror = () => URL.revokeObjectURL(url); + img.src = url; + return () => { + URL.revokeObjectURL(url); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [photoFile]); + + function redraw(allStrokes: Stroke[]) { + const canvas = canvasRef.current; + const img = imgRef.current; + if (!canvas || !img) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + // Linewidth scaled to image — на 4К фото 4px тонко смотрится + const lw = Math.max(6, Math.round(canvas.width / 250)); + ctx.strokeStyle = "rgb(220, 38, 38)"; + ctx.lineWidth = lw; + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + for (const s of allStrokes) { + if (s.pts.length < 2) { + // single tap → dot + if (s.pts.length === 1) { + ctx.fillStyle = "rgb(220, 38, 38)"; + ctx.beginPath(); + ctx.arc(s.pts[0].x, s.pts[0].y, lw / 2, 0, Math.PI * 2); + ctx.fill(); + } + continue; + } + ctx.beginPath(); + ctx.moveTo(s.pts[0].x, s.pts[0].y); + for (let i = 1; i < s.pts.length; i++) { + ctx.lineTo(s.pts[i].x, s.pts[i].y); + } + ctx.stroke(); + } + } + + // Re-draw whenever strokes change + useEffect(() => { + if (imgLoaded) redraw(strokes); + }, [strokes, imgLoaded]); + + function pointFromEvent(e: React.PointerEvent): { x: number; y: number } | null { + const canvas = canvasRef.current; + if (!canvas) return null; + const rect = canvas.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return null; + // CSS px → canvas px + const x = ((e.clientX - rect.left) / rect.width) * canvas.width; + const y = ((e.clientY - rect.top) / rect.height) * canvas.height; + return { x, y }; + } + + function onPointerDown(e: React.PointerEvent) { + e.preventDefault(); + (e.target as HTMLCanvasElement).setPointerCapture(e.pointerId); + const pt = pointFromEvent(e); + if (!pt) return; + currentStroke.current = { pts: [pt] }; + // Immediate visual feedback: draw a partial stroke without setState + drawPartialStroke(currentStroke.current); + } + + function onPointerMove(e: React.PointerEvent) { + if (!currentStroke.current) return; + const pt = pointFromEvent(e); + if (!pt) return; + currentStroke.current.pts.push(pt); + drawPartialStroke(currentStroke.current); + } + + function onPointerUp(e: React.PointerEvent) { + if (!currentStroke.current) return; + try { + (e.target as HTMLCanvasElement).releasePointerCapture(e.pointerId); + } catch { + // ignore — pointer wasn't captured + } + const finished = currentStroke.current; + currentStroke.current = null; + if (finished.pts.length > 0) { + setStrokes((s) => [...s, finished]); + } + } + + /** Avoid re-running full redraw on every move — just append last segment. */ + function drawPartialStroke(s: Stroke) { + const canvas = canvasRef.current; + if (!canvas || s.pts.length < 2) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; + const lw = Math.max(6, Math.round(canvas.width / 250)); + ctx.strokeStyle = "rgb(220, 38, 38)"; + ctx.lineWidth = lw; + ctx.lineCap = "round"; + ctx.lineJoin = "round"; + const a = s.pts[s.pts.length - 2]; + const b = s.pts[s.pts.length - 1]; + ctx.beginPath(); + ctx.moveTo(a.x, a.y); + ctx.lineTo(b.x, b.y); + ctx.stroke(); + } + + function clearAll() { + setStrokes([]); + } + + function undoLast() { + setStrokes((s) => s.slice(0, -1)); + } + + async function finishWithAnnotation() { + const canvas = canvasRef.current; + if (!canvas) return; + setSubmitting(true); + try { + const blob = await new Promise((resolve) => + canvas.toBlob(resolve, "image/jpeg", 0.85) + ); + await onSubmit(blob); + } finally { + setSubmitting(false); + } + } + + async function finishWithoutAnnotation() { + setSubmitting(true); + try { + await onSubmit(null); + } finally { + setSubmitting(false); + } + } + + return ( +
+
+
+
Обведите повреждение
+

{zoneLabel}

+
+
+ + +
+
+ +
+ +
+ +
+ + + +
+
+ ); +} diff --git a/mechanic-pwa/frontend/src/components/tt/TtDamageMap.tsx b/mechanic-pwa/frontend/src/components/tt/TtDamageMap.tsx new file mode 100644 index 0000000..2d4a494 --- /dev/null +++ b/mechanic-pwa/frontend/src/components/tt/TtDamageMap.tsx @@ -0,0 +1,157 @@ +import { useEffect, useRef, useState } from "react"; + +interface Point { + x: number; + y: number; +} + +interface DamageOverlay { + zoneId: number; + points: Point[]; +} + +interface Props { + damagedZoneIds: number[]; + /** Координаты точек повреждений в композитной SVG-системе (827×1209). */ + damages?: DamageOverlay[]; + onZoneClick?: (zoneId: number) => void; +} + +const SVG_NS = "http://www.w3.org/2000/svg"; +const POINT_RADIUS = 9; + +/** Композитная SVG-схема экстерьера машины с подсветкой зон + точками + * повреждений. Координаты точек в системе 827×1209 (та же что у vendor). */ +export function TtDamageMap({ damagedZoneIds, damages, onZoneClick }: Props) { + const containerRef = useRef(null); + const [svgText, setSvgText] = useState(null); + const damagedSet = new Set(damagedZoneIds.map(String)); + + useEffect(() => { + let cancelled = false; + fetch("/scheme/exterior.svg") + .then((r) => r.text()) + .then((text) => { + if (cancelled) return; + let mut = text; + mut = mut.replace(//gi, ""); + mut = mut.replace( + /(]*?)\s(?:width|height)\s*=\s*"[^"]*"/gi, + "$1" + ); + mut = mut.replace( + / { + cancelled = true; + }; + }, []); + + // Tinting + cursors + useEffect(() => { + if (!svgText) return; + const el = containerRef.current; + if (!el) return; + const svg = el.querySelector("svg") as SVGSVGElement | null; + if (!svg) return; + + svg.querySelectorAll("path[class]").forEach((p) => { + const cls = p.getAttribute("class") ?? ""; + if (damagedSet.has(cls)) { + p.setAttribute("fill", "rgba(239, 68, 68, 0.35)"); + p.setAttribute("stroke", "rgb(239, 68, 68)"); + p.setAttribute("stroke-width", "2"); + (p as SVGElement).style.cursor = onZoneClick ? "pointer" : "default"; + } else { + p.setAttribute("fill", "rgba(0,0,0,0.04)"); + p.setAttribute("stroke", "rgba(0,0,0,0.25)"); + p.setAttribute("stroke-width", "0.5"); + (p as SVGElement).style.cursor = "default"; + } + }); + svg.querySelectorAll("path:not([class])").forEach((p) => { + (p as SVGElement).style.opacity = "0.4"; + (p as SVGElement).style.pointerEvents = "none"; + }); + }, [svgText, damagedSet, onZoneClick]); + + // Point overlay — рисуем поверх SVG в его user-space (827×1209) чтобы + // точки идеально совмещались с подсвеченными зонами при любом масштабе. + useEffect(() => { + if (!svgText) return; + const el = containerRef.current; + if (!el) return; + const svg = el.querySelector("svg") as SVGSVGElement | null; + if (!svg) return; + + const existing = svg.querySelector("g[data-tt-damage-points]"); + if (existing) existing.remove(); + + if (!damages || damages.length === 0) return; + + const group = document.createElementNS(SVG_NS, "g"); + group.setAttribute("data-tt-damage-points", "1"); + + let counter = 1; + for (const dmg of damages) { + for (const pt of dmg.points || []) { + if ( + typeof pt.x !== "number" || + typeof pt.y !== "number" || + !Number.isFinite(pt.x) || + !Number.isFinite(pt.y) + ) { + continue; + } + const circle = document.createElementNS(SVG_NS, "circle"); + circle.setAttribute("cx", String(pt.x)); + circle.setAttribute("cy", String(pt.y)); + circle.setAttribute("r", String(POINT_RADIUS)); + circle.setAttribute("fill", "rgb(220, 38, 38)"); + circle.setAttribute("stroke", "white"); + circle.setAttribute("stroke-width", "2"); + (circle as SVGElement).style.pointerEvents = "none"; + group.appendChild(circle); + + const text = document.createElementNS(SVG_NS, "text"); + text.setAttribute("x", String(pt.x)); + text.setAttribute("y", String(pt.y)); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("dominant-baseline", "central"); + text.setAttribute("fill", "white"); + text.setAttribute("font-size", "11"); + text.setAttribute("font-weight", "bold"); + (text as SVGElement).style.pointerEvents = "none"; + (text as SVGElement).style.userSelect = "none"; + text.textContent = String(counter); + group.appendChild(text); + counter += 1; + } + } + + svg.appendChild(group); + }, [svgText, damages]); + + function handleClick(e: React.MouseEvent) { + if (!onZoneClick) return; + const target = e.target as Element; + const cls = target.getAttribute?.("class"); + if (cls && damagedSet.has(cls)) { + const zoneId = Number(cls); + if (Number.isFinite(zoneId)) onZoneClick(zoneId); + } + } + + return ( +
+ ); +} diff --git a/mechanic-pwa/frontend/src/components/vehicle-scheme/VendorVehicleScheme.tsx b/mechanic-pwa/frontend/src/components/vehicle-scheme/VendorVehicleScheme.tsx index d74e319..2c5bbee 100644 --- a/mechanic-pwa/frontend/src/components/vehicle-scheme/VendorVehicleScheme.tsx +++ b/mechanic-pwa/frontend/src/components/vehicle-scheme/VendorVehicleScheme.tsx @@ -14,8 +14,17 @@ export interface SchemeMarker { damage_type?: string | null; severity?: string | null; resolved: boolean; + /** Координаты точек повреждения в композитной SVG-системе (827×1209). */ + polygon?: { x: number; y: number }[] | null; } +const SVG_NS = "http://www.w3.org/2000/svg"; +const POINT_RADIUS = 9; +const POINT_FILL_ACTIVE = "rgb(220, 38, 38)"; +const POINT_FILL_RESOLVED = "rgb(107, 114, 128)"; +const COMPOSITE_W = 827; +const COMPOSITE_H = 1209; + interface Props { markers: SchemeMarker[]; onZoneTap?: (zoneId: number) => void; @@ -110,6 +119,69 @@ export function VendorVehicleScheme({ }); }, [svgText, zoneStats, hoverZone, editable]); + // Overlay точек повреждений (multi-point координаты в композитной 827×1209) + useEffect(() => { + if (!svgText) return; + const el = containerRef.current; + if (!el) return; + const svg = el.querySelector("svg") as SVGSVGElement | null; + if (!svg) return; + + // Удаляем предыдущий overlay + const existing = svg.querySelector("g[data-damage-points]"); + if (existing) existing.remove(); + + const group = document.createElementNS(SVG_NS, "g"); + group.setAttribute("data-damage-points", "1"); + + let counter = 1; + for (const m of markers) { + const pts = m.polygon || []; + if (pts.length === 0) continue; + const fill = m.resolved ? POINT_FILL_RESOLVED : POINT_FILL_ACTIVE; + + for (const pt of pts) { + if ( + typeof pt.x !== "number" || + typeof pt.y !== "number" || + !Number.isFinite(pt.x) || + !Number.isFinite(pt.y) + ) { + continue; + } + // Polygon хранится в normalised 0..1 (см. ZoneDetailView). SVG viewBox + // в композитной 827×1209, поэтому умножаем перед отрисовкой. + const cx = pt.x * COMPOSITE_W; + const cy = pt.y * COMPOSITE_H; + const circle = document.createElementNS(SVG_NS, "circle"); + circle.setAttribute("cx", String(cx)); + circle.setAttribute("cy", String(cy)); + circle.setAttribute("r", String(POINT_RADIUS)); + circle.setAttribute("fill", fill); + circle.setAttribute("stroke", "white"); + circle.setAttribute("stroke-width", "2"); + (circle as SVGElement).style.pointerEvents = "none"; + group.appendChild(circle); + + const text = document.createElementNS(SVG_NS, "text"); + text.setAttribute("x", String(cx)); + text.setAttribute("y", String(cy)); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("dominant-baseline", "central"); + text.setAttribute("fill", "white"); + text.setAttribute("font-size", "11"); + text.setAttribute("font-weight", "bold"); + (text as SVGElement).style.pointerEvents = "none"; + (text as SVGElement).style.userSelect = "none"; + text.textContent = String(counter); + group.appendChild(text); + counter += 1; + } + } + + svg.appendChild(group); + }, [svgText, markers]); + // Walk up from event.target to find a path[class="N"] (numeric zone id) function zoneFromEvent(e: React.MouseEvent | React.TouchEvent): number | null { let node = e.target as Element | null; diff --git a/mechanic-pwa/frontend/src/data/damageVocabulary.ts b/mechanic-pwa/frontend/src/data/damageVocabulary.ts index 8fd784b..7619470 100644 --- a/mechanic-pwa/frontend/src/data/damageVocabulary.ts +++ b/mechanic-pwa/frontend/src/data/damageVocabulary.ts @@ -24,3 +24,42 @@ export async function loadDamageVocabulary(): Promise { }; return cache; } + +/** + * Mapping русского названия из damage_vocabulary.json в DamageType enum + * нашего backend'а (см. mechanic_schemas.DamageType Literal). + * Возвращает null для типов которым нет точного соответствия — + * вызывающий код должен такие отфильтровать из UI. + */ +export const DAMAGE_RU_TO_ENUM: Record = { + "Вмятина": "dent", + "Царапина": "scratch", + "Трещина": "crack", + "Повреждено": "damaged", + "Скол": "chip", + "Отсутствует": "missing", + "Прожжено": "burn", + "Погнуто": "bent", + "Требуется мойка": null, + "Не работает": "not-working", + "Порез": "cut", + "Прокол": "puncture", + "Порвано": "torn", + "Пятна": "stain", + "Грязный салон": "stain", + "Запах табака": null, + "Повреждение салонных ковриков": "damaged", + "Повреждение обшивки дверей": "damaged", + "Сломанные ручки": "damaged", + "Установлено": "removed", + "Снято": "removed", + "Контроль": null, + "Требуется химчистка": "stain", + "Затертость": "scuff", + "Грыжа": "bulge", + "Штат": null, +}; + +export function damageTypeRuToEnum(ru: string): string | null { + return DAMAGE_RU_TO_ENUM[ru] ?? null; +} diff --git a/mechanic-pwa/frontend/src/pages/InspectionEditor.tsx b/mechanic-pwa/frontend/src/pages/InspectionEditor.tsx index eb51a29..04bcb06 100644 --- a/mechanic-pwa/frontend/src/pages/InspectionEditor.tsx +++ b/mechanic-pwa/frontend/src/pages/InspectionEditor.tsx @@ -6,9 +6,10 @@ import { getInspection, patchInspection, createMarker, + deleteMarker, type MarkerSummary, } from "@/api/inspections"; -import { uploadPhoto } from "@/api/photos"; +import { uploadPhoto, uploadPhotoAnnotation } from "@/api/photos"; import { PhotoSlot } from "@/components/camera/PhotoSlot"; import { CameraCapture } from "@/components/camera/CameraCapture"; import { VendorVehicleScheme } from "@/components/vehicle-scheme/VendorVehicleScheme"; @@ -16,12 +17,15 @@ import { TireWearDialog } from "@/components/inspection/TireWearDialog"; import { ZoneConfirmDialog } from "@/components/damage-flow/ZoneConfirmDialog"; import { ZoneDetailView } from "@/components/damage-flow/ZoneDetailView"; import { DamageClassifyDialog } from "@/components/damage-flow/DamageClassifyDialog"; +import { PhotoAnnotateStep } from "@/components/damage-flow/PhotoAnnotateStep"; import { AuthImg } from "@/components/AuthImg"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { loadZoneLabels } from "@/data/zoneLabels"; +import { damageTypeRuToEnum } from "@/data/damageVocabulary"; +import { ttImageUrl } from "@/api/vehicles"; -const SLOTS: { side: string; label: string }[] = [ +const SLOTS_GENERAL: { side: string; label: string }[] = [ { side: "front", label: "Перед" }, { side: "rear", label: "Зад" }, { side: "left", label: "Левый бок" }, @@ -32,6 +36,15 @@ const SLOTS: { side: string; label: string }[] = [ { side: "free", label: "Свободное" }, ]; +const SLOT_JACK = { side: "jack", label: "Домкрат" }; + +/** На приёмке (return) общие ракурсы не нужны — машина уже знакома, фокус + * на проверке повреждений и комплектации. Домкрат остаётся всегда. */ +function slotsForType(type: string): typeof SLOTS_GENERAL { + if (type === "return") return [SLOT_JACK]; + return [...SLOTS_GENERAL, SLOT_JACK]; +} + const SEVERITY_LABELS: Record = { cosmetic: "косметика", minor: "лёгкое", @@ -61,11 +74,18 @@ const DAMAGE_TYPE_LABELS: Record = { // Vendor 3-position severity index → backend literal const SEVERITY_MAP = ["minor", "moderate", "severe"] as const; -function humanizeMarkerSide(side: string | null | undefined): string { +function humanizeMarkerSide( + side: string | null | undefined, + zoneLabels?: Record | null, +): string { if (!side) return "—"; if (side === "tires") return "Резина"; const zm = /^zone-(\d+)$/.exec(side); - if (zm) return `Зона ${zm[1]}`; + if (zm) { + const label = zoneLabels?.[zm[1]]; + if (label) return label; + return `Зона ${zm[1]}`; + } const map: Record = { top: "Сверху", front: "Перед", @@ -93,7 +113,8 @@ type DamageFlow = | { kind: "confirm"; zoneId: number; zoneLabel: string } | { kind: "placePoints"; zoneId: number; zoneLabel: string } | { kind: "classify"; zoneId: number; zoneLabel: string; points: { x: number; y: number }[] } - | { kind: "capture"; zoneId: number; zoneLabel: string; points: { x: number; y: number }[]; damageType: string; severity: 0 | 1 | 2 }; + | { kind: "capture"; zoneId: number; zoneLabel: string; points: { x: number; y: number }[]; damageType: string; severity: 0 | 1 | 2 } + | { kind: "annotate"; zoneId: number; zoneLabel: string; points: { x: number; y: number }[]; damageType: string; severity: 0 | 1 | 2; photoFile: File }; export default function InspectionEditor() { const { id } = useParams<{ id: string }>(); @@ -121,6 +142,15 @@ export default function InspectionEditor() { onError: () => toast.error("Не удалось завершить"), }); + const removeMarker = useMutation({ + mutationFn: (markerId: number) => deleteMarker(markerId), + onSuccess: () => { + void qc.invalidateQueries({ queryKey: ["inspection", insId] }); + toast.success("Метка удалена"); + }, + onError: () => toast.error("Не удалось удалить"), + }); + if (isLoading) return
Загружаю…
; if (isError || !ins) return
Осмотр не найден.
; @@ -128,6 +158,9 @@ export default function InspectionEditor() { // Photos lookup by side+slot for initialPhotoId const photosBySide = new Map(); for (const p of ins.photos) photosBySide.set(`${p.side}-${p.slot_index}`, p); + // Photos lookup by id — нужен для marker thumb (annotated vs original) + const photosById = new Map(); + for (const p of ins.photos) photosById.set(p.id, p); const editable = ins.status === "in_progress"; @@ -166,6 +199,7 @@ export default function InspectionEditor() { damage_type: m.damage_type, severity: m.severity, resolved: m.resolved, + polygon: m.polygon, }))} onZoneTap={editable ? handleZoneTap : undefined} onTireWearClick={editable ? () => setTireWearOpen(true) : undefined} @@ -221,21 +255,64 @@ export default function InspectionEditor() { {flow.kind === "capture" && ( setFlow({ kind: "idle" })} - onPhotoUploaded={async (photoId) => { + onPhotoCaptured={(file) => + setFlow({ + kind: "annotate", + zoneId: flow.zoneId, + zoneLabel: flow.zoneLabel, + points: flow.points, + damageType: flow.damageType, + severity: flow.severity, + photoFile: file, + }) + } + /> + )} + + {flow.kind === "annotate" && ( + + setFlow({ + kind: "capture", + zoneId: flow.zoneId, + zoneLabel: flow.zoneLabel, + points: flow.points, + damageType: flow.damageType, + severity: flow.severity, + }) + } + onSubmit={async (annotated) => { try { + const damageEnum = damageTypeRuToEnum(flow.damageType); + if (!damageEnum) { + toast.error(`Не поддерживается: ${flow.damageType}`); + setFlow({ kind: "idle" }); + return; + } + const result = await uploadPhoto(insId, "free", 0, flow.photoFile); + if (annotated) { + try { + await uploadPhotoAnnotation(insId, result.id, annotated); + } catch (err) { + // Аннотация — best-effort. Фото оригинал уже сохранилось. + console.warn("annotation upload failed (non-fatal)", err); + } + } await createMarker(insId, { side: `zone-${flow.zoneId}`, polygon: flow.points, - damage_type: flow.damageType, + damage_type: damageEnum, severity: SEVERITY_MAP[flow.severity], - photo_id: photoId, + photo_id: result.id, }); void qc.invalidateQueries({ queryKey: ["inspection", insId] }); toast.success("Повреждение записано"); - } catch { + } catch (err) { + console.error("save failed", err); toast.error("Не удалось сохранить"); } finally { setFlow({ kind: "idle" }); @@ -258,7 +335,7 @@ export default function InspectionEditor() {

Фото

- {SLOTS.map((s) => ( + {slotsForType(ins.type).map((s) => (
- - {humanizeMarkerSide(m.side)} + + {humanizeMarkerSide(m.side, zoneLabels)} {" — "} @@ -303,26 +380,64 @@ export default function InspectionEditor() { )}
- {m.carried_over_from_id && ( - - перенесено - - )} +
+ {m.carried_over_from_id && ( + + перенесено + + )} + {!m.carried_over_from_id && + m.description?.startsWith("Перенесено из Element Mechanic") && ( + + из Element Mechanic + + )} + {editable && ( + + )} +
{m.description && (
{m.description}
)} - {m.photo_id && ( + {m.photo_id ? (
- )} + ) : m.tt_image_id ? ( +
+ Фото из Element Mechanic +
+ ) : null} ))} @@ -344,29 +459,14 @@ export default function InspectionEditor() { } function CapturePhotoStep({ - inspectionId, zoneLabel, onCancel, - onPhotoUploaded, + onPhotoCaptured, }: { - inspectionId: number; zoneLabel: string; onCancel: () => void; - onPhotoUploaded: (photoId: number) => void | Promise; + onPhotoCaptured: (file: File) => void; }) { - const [uploading, setUploading] = useState(false); - - async function handleFile(file: File) { - setUploading(true); - try { - const result = await uploadPhoto(inspectionId, "free", 0, file); - await onPhotoUploaded(result.id); - } catch { - toast.error("Ошибка загрузки фото"); - setUploading(false); - } - } - return (
@@ -375,13 +475,12 @@ function CapturePhotoStep({
+
+ )}
); } diff --git a/mechanic-pwa/frontend/src/pages/TtStateDetail.tsx b/mechanic-pwa/frontend/src/pages/TtStateDetail.tsx new file mode 100644 index 0000000..54563b8 --- /dev/null +++ b/mechanic-pwa/frontend/src/pages/TtStateDetail.tsx @@ -0,0 +1,394 @@ +import { useState, useMemo, useRef, useEffect } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { toast } from "sonner"; +import { + getTtState, + deleteTtState, + ttImageUrl, + type TtPhotoRef, + type TtSidePhoto, +} from "@/api/vehicles"; +import { getMe } from "@/api/me"; +import { Card } from "@/components/ui/card"; +import { TtDamageMap } from "@/components/tt/TtDamageMap"; + +interface ZoneLabels { + [zoneId: string]: string; +} + +interface DamageVocabulary { + damage_types_ordered: string[]; + [k: string]: unknown; +} + +let zoneLabelsCache: ZoneLabels | null = null; +let damageVocabCache: DamageVocabulary | null = null; + +async function loadZoneLabels(): Promise { + if (zoneLabelsCache) return zoneLabelsCache; + const res = await fetch("/data/zone_labels.json"); + zoneLabelsCache = await res.json(); + return zoneLabelsCache!; +} + +async function loadDamageVocabulary(): Promise { + if (damageVocabCache) return damageVocabCache; + const res = await fetch("/data/damage_vocabulary.json"); + damageVocabCache = await res.json(); + return damageVocabCache!; +} + +const DEGREE_LABELS: Record = { + 0: "Лёгкое", + 1: "Среднее", + 2: "Тяжёлое", +}; + +function damageTypeName( + id: number | null, + vocab: DamageVocabulary | undefined, +): string { + if (id == null) return "Без типа"; + // Vendor enum is 1-based: type=1 → damage_types_ordered[0] ("Вмятина"). + const idx = id - 1; + const arr = vocab?.damage_types_ordered; + if (arr && idx >= 0 && idx < arr.length) return arr[idx]; + return `Тип ${id}`; +} + +const SIDE_LABELS: Record = { + 1: "Перед", + 2: "Зад", + 3: "Левый борт", + 4: "Правый борт", + 5: "Сверху", + 6: "Передний угол", + 7: "Задний угол", + 8: "Салон", + 9: "Дополнительно", +}; + +type PreviewItem = + | { kind: "zone"; photo: TtPhotoRef } + | { kind: "side"; photo: TtSidePhoto }; + +export default function TtStateDetail() { + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const stateId = Number(id); + const zoneRefs = useRef>({}); + + const { data: state, isLoading, isError } = useQuery({ + queryKey: ["tt-state", stateId], + queryFn: () => getTtState(stateId), + enabled: Number.isFinite(stateId), + }); + + const { data: labels } = useQuery({ + queryKey: ["zone-labels"], + queryFn: loadZoneLabels, + staleTime: Infinity, + }); + + const { data: vocab } = useQuery({ + queryKey: ["damage-vocabulary"], + queryFn: loadDamageVocabulary, + staleTime: Infinity, + }); + + const { data: me } = useQuery({ + queryKey: ["me"], + queryFn: getMe, + staleTime: 5 * 60_000, + }); + const isAdmin = me?.permissions?.includes("admin") ?? false; + + const queryClient = useQueryClient(); + const deleteMutation = useMutation({ + mutationFn: () => deleteTtState(stateId), + onSuccess: () => { + toast.success("Осмотр удалён"); + // Drop any cached tt-history for vehicle list so новый рендер не покажет удалённый. + queryClient.invalidateQueries({ queryKey: ["vehicle"] }); + navigate(-1); + }, + onError: () => toast.error("Не удалось удалить осмотр"), + }); + + const [preview, setPreview] = useState(null); + const [showLines, setShowLines] = useState(false); + + const damagedZoneIds = useMemo(() => { + if (!state) return []; + return Object.keys(state.damages_by_zone).map((k) => Number(k)); + }, [state]); + + const damageOverlays = useMemo(() => { + if (!state) return []; + return Object.entries(state.damages_by_zone).flatMap(([zid, dmgs]) => + dmgs.map((d) => ({ zoneId: Number(zid), points: d.points || [] })) + ); + }, [state]); + + // Reset refs when state changes + useEffect(() => { + zoneRefs.current = {}; + }, [stateId]); + + if (isLoading) + return
Загружаю…
; + if (isError || !state) + return
Осмотр не найден.
; + + const date = state.unix_time + ? new Date(state.unix_time * 1000).toLocaleString("ru-RU") + : "—"; + + const zoneIds = Array.from( + new Set([ + ...Object.keys(state.photos_by_zone), + ...Object.keys(state.damages_by_zone), + ]) + ).sort((a, b) => Number(a) - Number(b)); + + function scrollToZone(zoneId: number) { + const el = zoneRefs.current[String(zoneId)]; + if (el) { + el.scrollIntoView({ behavior: "smooth", block: "start" }); + } + } + + return ( +
+ + + +
Element Mechanic
+
+ {state.mechanic_name || "Осмотр"} +
+
+ {date} + {state.mileage != null && ( + <> · {state.mileage.toLocaleString("ru-RU")} км + )} +
+
+ + {/* Общие 9 ракурсов */} + {state.side_photos.length > 0 && ( +
+

+ Общие фото +

+
+ {state.side_photos.map((p) => { + const label = p.photo_type + ? SIDE_LABELS[p.photo_type] || `Ракурс ${p.photo_type}` + : "Ракурс"; + const thumbSrc = p.miniature_id + ? ttImageUrl(p.miniature_id) + : ttImageUrl(p.image_id); + return ( + + ); + })} +
+
+ )} + + {/* Карта повреждений */} + {damagedZoneIds.length > 0 && ( +
+

+ Карта повреждений + + {damagedZoneIds.length}{" "} + {damagedZoneIds.length === 1 ? "зона" : "зон"} + +

+ +
+ Тап на зону — прокрутка к деталям +
+
+ )} + + {/* Детали по зонам с повреждениями */} + {zoneIds.length === 0 ? ( + + В осмотре нет ни фото, ни повреждений. + + ) : ( + zoneIds.map((zid) => { + const photos = state.photos_by_zone[zid] || []; + const damages = state.damages_by_zone[zid] || []; + const label = labels?.[zid] || `Зона ${zid}`; + return ( +
{ + zoneRefs.current[zid] = el; + }} + className="scroll-mt-4" + > +

+ {label} + {damages.length > 0 && ( + + ⚠ {damages.length}{" "} + {damages.length === 1 ? "повреждение" : "повреждений"} + + )} +

+ {damages.length > 0 && ( +
+ {damages.map((d, idx) => { + const tname = damageTypeName(d.damage_type_id, vocab); + const dname = + d.degree != null ? DEGREE_LABELS[d.degree] : null; + return ( + + {tname} + {dname && ( + <> + · + {dname} + + )} + + ); + })} +
+ )} + {photos.length > 0 ? ( +
+ {photos.map((p, idx) => ( + + ))} +
+ ) : ( + + Фото нет — только координаты повреждений + + )} +
+ ); + }) + )} + + {isAdmin && ( +
+ +
+ )} + + {preview && ( +
setPreview(null)} + > +
+ + {preview.kind === "zone" && + preview.photo.image_with_lines_id && ( + + )} +
+
e.stopPropagation()} + > + Просмотр +
+
+ )} +
+ ); +} diff --git a/mechanic-pwa/frontend/src/pages/VehicleCard.tsx b/mechanic-pwa/frontend/src/pages/VehicleCard.tsx index 7cb34ff..41bf162 100644 --- a/mechanic-pwa/frontend/src/pages/VehicleCard.tsx +++ b/mechanic-pwa/frontend/src/pages/VehicleCard.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { useParams, useNavigate, useSearchParams } from "react-router-dom"; import { useQuery, useMutation } from "@tanstack/react-query"; import { toast } from "sonner"; -import { getVehicle } from "@/api/vehicles"; +import { getVehicle, getTtHistory } from "@/api/vehicles"; import { createInspection, type InspectionType } from "@/api/inspections"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; @@ -10,7 +10,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; const INSPECTION_TYPE_LABELS: Record = { - handover: "Передача", + handover: "Выдача", return: "Приёмка", periodic: "Плановый", "ad-hoc": "Свободный", @@ -27,6 +27,18 @@ const STATUS_LABELS: Record = { cancelled: "Отменён", }; +function timeAgo(iso: string): string { + const sec = Math.max(0, (Date.now() - new Date(iso).getTime()) / 1000); + if (sec < 60) return "только что"; + const min = Math.floor(sec / 60); + if (min < 60) return `${min} мин назад`; + const h = Math.floor(min / 60); + if (h < 24) return `${h} ч назад`; + const d = Math.floor(h / 24); + if (d < 30) return `${d} дн назад`; + return new Date(iso).toLocaleDateString("ru-RU"); +} + export default function VehicleCard() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); @@ -37,12 +49,24 @@ export default function VehicleCard() { const [pendingType, setPendingType] = useState(null); const [mileageInput, setMileageInput] = useState(""); + const [mileageSource, setMileageSource] = useState< + "starline" | "last-inspection" | null + >(null); const { data: v, isLoading, isError } = useQuery({ queryKey: ["vehicle", vehicleId], queryFn: () => getVehicle(vehicleId), }); + // История из vendor (Element Mechanic). Молча возвращает пустой список + // если машина ещё не синхронизирована — секцию просто скрываем. + const { data: tt } = useQuery({ + queryKey: ["vehicle", vehicleId, "tt-history"], + queryFn: () => getTtHistory(vehicleId), + enabled: Number.isFinite(vehicleId), + staleTime: 5 * 60_000, + }); + const startInspection = useMutation({ mutationFn: (input: { type: InspectionType; mileage: number | undefined }) => createInspection({ vehicle_id: vehicleId, type: input.type, mileage: input.mileage }), @@ -54,8 +78,21 @@ export default function VehicleCard() { const handleStartClick = (type: InspectionType) => { setPendingType(type); + // Приоритет: 1) StarLine OBD (свежий одометр), 2) пробег прошлого + // осмотра. Менеджер всегда может скорректировать. + if (v?.starline_mileage != null) { + setMileageInput(String(v.starline_mileage)); + setMileageSource("starline"); + return; + } const lastWithMileage = v?.recent_inspections.find((i) => i.mileage != null); - setMileageInput(lastWithMileage?.mileage ? String(lastWithMileage.mileage) : ""); + if (lastWithMileage?.mileage != null) { + setMileageInput(String(lastWithMileage.mileage)); + setMileageSource("last-inspection"); + return; + } + setMileageInput(""); + setMileageSource(null); }; if (isLoading) @@ -91,7 +128,7 @@ export default function VehicleCard() { onClick={() => handleStartClick("handover")} disabled={startInspection.isPending} > - Передача + Выдача - - - - - -
@@ -202,6 +198,53 @@ export default function VehicleCard() { )} + {tt && tt.states.length > 0 && ( +
+

+ История осмотров (Element Mechanic) +

+
+ {tt.states.map((s) => { + const date = s.unix_time + ? new Date(s.unix_time * 1000).toLocaleString("ru-RU") + : "—"; + return ( + navigate(`/tt-states/${s.id}`)} + > +
+
+
+ {s.mechanic_name || "Осмотр"} +
+
+ {date} + {s.mileage != null && ( + <> + · + {s.mileage.toLocaleString("ru-RU")} км + + )} +
+
+
+
📷 {s.photos_count}
+ {s.damages_count > 0 && ( +
+ ⚠ {s.damages_count} +
+ )} +
+
+
+ ); + })} +
+
+ )} + {pendingType && (
setMileageInput(e.target.value.replace(/\D/g, ""))} + onChange={(e) => { + setMileageInput(e.target.value.replace(/\D/g, "")); + setMileageSource(null); + }} placeholder="123456" autoFocus /> + {mileageSource === "starline" && ( +
+ + + Из StarLine + {v?.starline_mileage_at && ( + <> · {timeAgo(v.starline_mileage_at)} + )} + + + · скорректируйте если надо + +
+ )} + {mileageSource === "last-inspection" && ( +
+ Из прошлого осмотра · скорректируйте если надо +
+ )}
diff --git a/recon/findings/damage_model.md b/recon/findings/damage_model.md new file mode 100644 index 0000000..58de5bb --- /dev/null +++ b/recon/findings/damage_model.md @@ -0,0 +1,79 @@ +# Vendor Damage Data Model — Extraction Notes + +Source: `Ttc.dll` (Xamarin Android app, reverse-engineered from `assemblies.blob`) +Date: 2026-05-17 + +## Core Objects + +### DetailDamageCoordinate +A single tap-point placed by the mechanic on the enlarged part SVG. + +| Field | Type | Notes | +|-------|------|-------| +| `Id` | int/Guid | PK | +| `X` | float | Relative X on the part SVG canvas | +| `Y` | float | Relative Y on the part SVG canvas | +| `InspectionGuid` | Guid | Ties this point to an inspection session | +| `MetaId` | int | Zone ID (0..56) — which part this point belongs to | +| `GroupId` | int | Groups multiple points belonging to ONE damage annotation | +| `ImageWithLinesId` | int | FK → the photo with polyline annotation | +| `LinesId` | int | FK → the DrawingLines set | + +Evidence: backing fields `k__BackingField`, `k__BackingField`, `k__BackingField`, `k__BackingField`, `k__BackingField`, `k__BackingField`, `k__BackingField` all found in contiguous block at 0x3baa0–0x3bd50 in Strings heap. `GetByGroupId` method confirms group-based querying. + +### DetailDamageCoordinateContract (API wire format) +Same fields as above, serialized as JSON. The JSON property for severity is `"degree"` (lowercase, confirmed at 0x3d752 in Strings heap alongside `"damageDegree"` widget name). + +### DetailDamageDrawPointContract +A single vertex in the finger-drawn polyline annotation on a photo. + +| Field | Type | Notes | +|-------|------|-------| +| `X` | float | Point X on the photo canvas | +| `Y` | float | Point Y on the photo canvas | + +The polyline is a **list** of `DetailDamageDrawPointContract` objects. The parent container is `DrawingLines` (backing field `k__BackingField` at 0x3cade). Method `DrawLine` (0x3e3c9) draws each segment. `StrokeWidth` (0x3f59a) controls line thickness. + +The photo itself is stored as `Image64StringWithLines` (base64 JPEG) and `ImageBase64StringWithLines` (alternate field). The link between a damage group and its annotated photo is: `DetailDamageCoordinate.ImageWithLinesId → VehicleStatePhotoInspection.Id`. + +## Multi-Point Damage Grouping + +**Yes, multi-point damages are grouped via `GroupId`.** The mechanic taps N spots on the part SVG — each tap creates a `DetailDamageCoordinate` with the same `GroupId`. All points in a group share: +- One `damage_type` (the vertical list selection) +- One `degree` (severity: 0=Легкое, 1=Среднее, 2=Тяжелое) +- One `ImageWithLinesId` (one photo with optional polyline) + +The `GetByGroupId` method (found in Strings heap at 0x394c0) fetches all coordinates for a group. `SaveByParts` (0x42f4f) persists the full group in one call. + +## Photo + Polyline Link + +``` +Inspection + └─ DetailDamageCoordinate[] (grouped by GroupId) + ├─ GroupId = 42 (e.g.) + ├─ MetaId = 0 (front bumper) + ├─ [X1,Y1], [X2,Y2], [X3,Y3] ← multiple tap points + ├─ ImageWithLinesId = 7 ← FK → photo record + └─ degree = 1 (Среднее) + +VehicleStatePhotoInspection (id=7) + ├─ Image64StringWithLines ← base64 JPEG (the photo) + ├─ DrawingLines[] ← list of DetailDamageDrawPointContract + │ └─ [{X:0.2, Y:0.4}, {X:0.5, Y:0.6}, ...] ← polyline vertices + └─ HistoryLines[] ← read-only history of old polylines +``` + +## Our Implementation Recommendation + +1. **DamageGroup** table: `{ id, inspection_id, zone_id, damage_type, severity(0-2), photo_id, created_at }` +2. **DamagePoint** table: `{ id, group_id, x REAL, y REAL, seq_order INT }` — multiple rows per group +3. **DamagePhoto** table: `{ id, image_b64, polyline JSONB }` where `polyline` is `[[x,y], ...]` +4. Severity stored as integer 0/1/2, displayed as Легкое/Среднее/Тяжелое + +This mirrors the vendor model exactly while using a standard relational schema. + +## Known Unknowns + +- The exact JSON shape returned by `damage/details` endpoint (API not captured live) +- Whether `GroupId` is a local UUID or server-assigned int +- Whether `DrawingLines` coordinates are normalized (0..1) or absolute pixels diff --git a/recon/findings/damage_vocabulary.json b/recon/findings/damage_vocabulary.json new file mode 100644 index 0000000..97b4526 --- /dev/null +++ b/recon/findings/damage_vocabulary.json @@ -0,0 +1,90 @@ +{ + "_meta": { + "source": "Ttc.dll #US heap, contiguous block at offsets 0xc2e24–0xc3084", + "confidence": "HIGH — full block scanned; order is the binary storage order which matches app display order (repository pattern typically loads in definition order)", + "notes": [ + "Items 0–12 are general exterior damage types (user-selectable for any zone)", + "Items 13–18 appear to be salon-specific (Грязный салон, Запах табака, etc.) — shown only for interior zones (44–46 seats, salon)", + "Установлено/Снято (19–20) and Контроль (21) are semi-system states used for equipment completeness zones (33–48 area)", + "Штат (25) appears to be a system/staff marker, not user-selectable", + "Порез, Прокол, Порвано are tire-specific damage types for zones 17–20", + "Есть/Нет повреждений found in binary (0x4599a/0x4597a) — likely used for salon damage yes/no toggle, not in the standard list" + ] + }, + "damage_types_ordered": [ + "Вмятина", + "Царапина", + "Трещина", + "Повреждено", + "Скол", + "Отсутствует", + "Прожжено", + "Погнуто", + "Требуется мойка", + "Не работает", + "Порез", + "Прокол", + "Порвано", + "Пятна", + "Грязный салон", + "Запах табака", + "Повреждение салонных ковриков", + "Повреждение обшивки дверей", + "Сломанные ручки", + "Установлено", + "Снято", + "Контроль", + "Требуется химчистка", + "Затертость", + "Грыжа", + "Штат" + ], + "exterior_selectable": [ + "Вмятина", + "Царапина", + "Трещина", + "Повреждено", + "Скол", + "Отсутствует", + "Прожжено", + "Погнуто", + "Требуется мойка", + "Не работает", + "Порез", + "Прокол", + "Порвано", + "Пятна", + "Затертость", + "Грыжа" + ], + "salon_selectable": [ + "Вмятина", + "Царапина", + "Трещина", + "Повреждено", + "Скол", + "Отсутствует", + "Прожжено", + "Погнуто", + "Требуется мойка", + "Не работает", + "Пятна", + "Грязный салон", + "Запах табака", + "Повреждение салонных ковриков", + "Повреждение обшивки дверей", + "Сломанные ручки", + "Требуется химчистка", + "Затертость", + "Грыжа" + ], + "severity_scale": { + "positions": 3, + "labels": ["Легкое", "Среднее", "Тяжелое"], + "values": [0, 1, 2], + "widget": "SeekBar (vehicleInspectionDegreeSelect / newDamageSeekBarDamageDegree)", + "api_field": "degree (lowercase, in JSON contract)", + "default": "Легкое (index 0)", + "source_note": "Легкое found at 0x59d79 (adjacent to 'Добавить повреждение' button label = default/initial value); Среднее+Тяжелое found together at 0xc39e8–0xc39f8 (loading state transitions)" + } +} diff --git a/recon/findings/scheme/api_endpoints.txt b/recon/findings/scheme/api_endpoints.txt new file mode 100644 index 0000000..5a22ac0 --- /dev/null +++ b/recon/findings/scheme/api_endpoints.txt @@ -0,0 +1,38 @@ +US+1023: inspectionId +US+2393: )" id="car" xmlns = "http://www.w3.org/2000/svg" viewBox = " +US+81926: inspectionvideo/ +US+83424: vehicle +US+83802: vehicle.json +US+83898: http://ttcontrol.naughtysoft.ru/api/ +US+83972: vehicletechnicalcontrol +US+84020: techinspectionitem +US+84448: vehicle/vehiclelist +US+84488: vehicle/statuses +US+84522: vehiclerepair +US+84550: inspection.json +US+511737: vehiclestatephotoinspection +US+512035: vehicletechinspection +US+512311: inspection +US+512945: customersettings/salondamages +US+513773: damage/details +US+517799: vehicletechnicalcontrol/driver?search= +US+517877: vehicletechnicalcontrol/driverinspection?vehicleNumber= +US+517989: driver/state?vehicleNum={0}&reason={1} +US+518099: inspectionvideo?vehicleId= +US+518153: inspectionvideo +US+518323: vehicle?id= +US+518347: vehicle/vehiclemeta?id= +US+518395: damage/damagescard?id= +US+518441: vehicle/ +US+518493: vehiclerepair/byvehicle?vehicleId= +US+518563: vehiclestate/history?id= +US+518613: vehiclestate/mileage +US+518655: vehiclestate/new +US+518689: vehiclestate/newpart +US+518731: vehiclestate/new?id= +US+518773: vehiclestatephotoinspection/photohistory?vehicleId={0}&type={1} +US+518901: vehiclestatephotoinspection/photoinspection?vehicleId= +US+519011: vehicletechinspection/byvehicle?vehicleId= +US+519097: inspection/byvehicle?id= +US+519147: inspection/photohistory?vehicleId={0}&type={1} +US+519253: damage diff --git a/recon/findings/scheme/circle_marker.txt b/recon/findings/scheme/circle_marker.txt new file mode 100644 index 0000000..c30a4cc --- /dev/null +++ b/recon/findings/scheme/circle_marker.txt @@ -0,0 +1,6 @@ +US+391601 (2929 chars): ' + +[539] US+513933: '{2}' + +[540] US+514059: '` data is embedded in the DLL binaries. The vendor app fetches detail part SVGs from the server at runtime. + +## How It Works + +### 1. Vehicle Load: `vehicle/vehiclemeta?id=` + +When the mechanic selects a vehicle, the app calls: + +``` +GET {baseUrl}/vehicle/vehiclemeta?id={vehicleId} +Authorization: Bearer {token} +``` + +The response includes a list of `DetailScheme` objects — one per tappable zone. Each `DetailScheme` has at minimum: + +- `Id` — database PK +- `MetaId` — maps to the SVG `class="N"` zone ID (0..56) +- `Scheme` — full SVG string for the enlarged part view +- `ViewBox` — the viewBox string for that part's SVG (e.g. `"0 0 400 300"`) + +### 2. Zone Tap → Detail View + +When user taps zone N on the composite SVG: + +1. `Foo.TouchEnd(N)` fires (JavaScript in WebView) +2. App calls `GetDamagedDetailScheme(metaId: N)` (C# method in `Ttc.dll`) +3. The cached `DetailScheme` for that MetaId is retrieved +4. Its `Scheme` SVG is rendered in a new WebView/Activity (`DetailSchemeActivity`) +5. The scheme has the same ` \ No newline at end of file diff --git a/recon/findings/scheme/salon_scheme.svg b/recon/findings/scheme/salon_scheme.svg new file mode 100644 index 0000000..d4e85d5 --- /dev/null +++ b/recon/findings/scheme/salon_scheme.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/recon/findings/scheme/strings_heap_damage.txt b/recon/findings/scheme/strings_heap_damage.txt new file mode 100644 index 0000000..ef62be8 --- /dev/null +++ b/recon/findings/scheme/strings_heap_damage.txt @@ -0,0 +1,795 @@ ++ 88: __StaticArrayInitTypeSize=40 ++ 158: <>c__DisplayClass10_0 ++ 233: <_vehicleStatusHistoryRepository_CarChanged>b__11_0 ++ 326: b__11_0 ++ 343: <>c__DisplayClass11_0 ++ 395: b__1_0 ++ 419: <>c__DisplayClass1_0 ++ 481: <>c__DisplayClass12_0 ++ 503: <>c__DisplayClass72_0 ++ 606: <>c__DisplayClass2_0 ++ 650: <>c__DisplayClass13_0 ++ 789: b__3_0 ++ 814: <>c__DisplayClass3_0 ++ 887: <>c__DisplayClass14_0 ++ 1114: <>c__DisplayClass4_0 ++ 1168: b__65_0 ++ 1188: <>c__DisplayClass65_0 ++ 1271: b__5_0 ++ 1312: b__5_0 ++ 1337: <>c__DisplayClass5_0 ++ 1452: <>c__DisplayClass66_0 ++ 1483: b__6_0 ++ 1521: b__6_0 ++ 1541: <>c__DisplayClass6_0 ++ 1605: <>c__DisplayClass17_0 ++ 1656: <>c__DisplayClass67_0 ++ 1678: <>c__DisplayClass77_0 ++ 1739: <>c__DisplayClass7_0 ++ 1760: <>c__DisplayClass68_0 ++ 1782: <>c__DisplayClass78_0 ++ 1826: <>c__DisplayClass8_0 ++ 1945: <>c__DisplayClass9_0 ++ 1966: b__0 ++ 1985: b__0 ++ 2003: b__0 ++ 2022: b__0 ++ 2045: b__0 ++ 2059: b__0 ++ 2103: b__0 ++ 2357: b__0 ++ 2391: b__0 ++ 2464: b__0 ++ 2480: b__0 ++ 2653: d__11 ++ 2835: b__11_1 ++ 2852: <>c__DisplayClass11_1 ++ 2955: <>c__DisplayClass72_1 ++ 3003: <>c__DisplayClass2_1 ++ 3226: <>c__DisplayClass14_1 ++ 3430: b__5_1 ++ 3807: b__1 ++ 3940: b__1 ++ 3963: b__1 ++ 4014: b__1 ++ 4031: d__1 ++ 4063: d__1 ++ 4132: d__11`1 ++ 4251: AsyncTaskMethodBuilder`1 ++ 4319: ArrayAdapter`1 ++ 4425: __StaticArrayInitTypeSize=12 ++ 4524: d__72 ++ 4689: <>c__DisplayClass14_2 ++ 4763: b__5_2 ++ 4987: 5__2 ++ 5071: 5__2 ++ 5188: d__2 ++ 5378: KeyValuePair`2 ++ 5393: Dictionary`2 ++ 5406: TextAppearance_Compat_Notification_Line2 ++ 5479: text2 ++ 5594: <>c__DisplayClass14_3 ++ 5642: b__5_3 ++ 5908: d__3 ++ 5927: d__3 ++ 5995: d__3 ++ 6034: d__3 ++ 6153: __StaticArrayInitTypeSize=24 ++ 6381: d__4 ++ 6512: d__65 ++ 6777: d__5 ++ 6822: d__5 ++ 6847: d__5 ++ 7066: d__6 ++ 7366: d__7 ++ 7392: __StaticArrayInitTypeSize=28 ++ 7421: __StaticArrayInitTypeSize=48 ++ 7747: d__9 ++ 7992: PointF ++ 8071: System.IO ++ 8104: get_X ++ 8110: set_X ++ 8116: GradientColor_android_endX ++ 8143: GradientColor_android_centerX ++ 8173: GetX ++ 8178: GradientColor_android_startX ++ 8207: _viewX ++ 8214: get_Y ++ 8220: set_Y ++ 8226: GradientColor_android_endY ++ 8253: GradientColor_android_centerY ++ 8283: GetY ++ 8288: GradientColor_android_startY ++ 8423: TextAppearance_Compat_Notification_Line2_Media ++ 8470: TextAppearance_Compat_Notification_Title_Media ++ 8517: TextAppearance_Compat_Notification_Time_Media ++ 8563: TextAppearance_Compat_Notification_Media ++ 8604: TextAppearance_Compat_Notification_Info_Media ++ 8792: newDamageTextureCamera ++ 8815: inspectionTextureCamera ++ 8847: GetStringExtra ++ 8862: GetBooleanExtra ++ 8878: GetIntExtra ++ 8890: PutExtra ++ 9059: System.Collections.Generic ++ 9086: GetResponseAsync ++ 9103: DownloadDataTaskAsync ++ 9125: GetRequestStreamAsync ++ 9147: async ++ 9157: <b__11_0>d ++ 9448: textViewResourceId ++ 9613: get_PhotoTypeId ++ 9629: set_PhotoTypeId ++ 10003: GetByGroupId ++ 10221: GetMetaById ++ 10233: GetImageById ++ 10246: GetImagePathById ++ 10263: GetById ++ 10271: FindViewById ++ 10347: detailhistoryadd ++ 10427: _damageAdded ++ 10614: _vehicleStatusHistoryRepository_CarChanged ++ 10694: DamageDegreeOnProgressChanged ++ 10791: _vehicleStatusHistoryRepository_ApprovedListChanged ++ 10872: add_TextChanged ++ 10888: SearchText_TextChanged ++ 10950: get_SalonChecked ++ 10967: set_SalonChecked ++ 11026: get_ComplecityChecked ++ 11048: set_ComplecityChecked ++ 11601: System.Collections.Specialized ++ 11770: k__BackingField ++ 11789: k__BackingField ++ 12000: k__BackingField ++ 12516: k__BackingField ++ 12580: k__BackingField ++ 13005: k__BackingField ++ 13282: k__BackingField ++ 13306: k__BackingField ++ 13335: k__BackingField ++ 13367: k__BackingField ++ 13397: k__BackingField ++ 13435: k__BackingField ++ 13631: k__BackingField ++ 13657: k__BackingField ++ 13829: k__BackingField ++ 13853: k__BackingField ++ 13883: k__BackingField ++ 13909: k__BackingField ++ 13946: k__BackingField ++ 14009: k__BackingField ++ 14031: k__BackingField ++ 14056: k__BackingField ++ 14100: k__BackingField ++ 14128: k__BackingField ++ 14155: k__BackingField ++ 14190: k__BackingField ++ 14241: k__BackingField ++ 15546: k__BackingField ++ 15586: k__BackingField ++ 15770: k__BackingField ++ 15795: k__BackingField ++ 15939: k__BackingField ++ 15969: k__BackingField ++ 15998: k__BackingField ++ 16025: k__BackingField ++ 16143: k__BackingField ++ 16779: k__BackingField ++ 16804: k__BackingField ++ 16837: k__BackingField ++ 16874: k__BackingField ++ 16909: k__BackingField ++ 16963: k__BackingField ++ 17001: k__BackingField ++ 17058: k__BackingField ++ 17096: k__BackingField ++ 17135: k__BackingField ++ 17188: k__BackingField ++ 17236: k__BackingField ++ 17273: k__BackingField ++ 17311: k__BackingField ++ 17357: k__BackingField ++ 17403: k__BackingField ++ 17441: k__BackingField ++ 17483: k__BackingField ++ 17538: k__BackingField ++ 17582: k__BackingField ++ 17625: k__BackingField ++ 17659: k__BackingField ++ 17700: k__BackingField ++ 17744: k__BackingField ++ 17789: k__BackingField ++ 17824: k__BackingField ++ 17861: k__BackingField ++ 17909: k__BackingField ++ 17941: textField ++ 18071: CoordinatorLayout_statusBarBackground ++ 18392: GetSystemService ++ 18875: get_DamageDegree ++ 18892: set_DamageDegree ++ 18909: newDamageSeekBarDamageDegree ++ 18938: _newDamageTextViewDamageDegree ++ 18969: _damageDegree ++ 18983: vehicleInspectionDamagesDegree ++ 19235: techinspectionHistoryTiMileage ++ 19266: CustomHistoryInspectionItemMileage ++ 19587: FontFamily_fontProviderPackage ++ 19862: get_Damage ++ 19873: set_Damage ++ 19884: detailPreviewAddDamage ++ 19907: SelectedDamage ++ 19922: get_ImageDamage ++ 19938: set_ImageDamage ++ 19954: DrawingImageDamage ++ 19973: DetailDamage ++ 19986: currentDamage ++ 20000: get_SalonGotDamage ++ 20019: set_SalonGotDamage ++ 20038: get_GotNewDamage ++ 20055: set_GotNewDamage ++ 20072: damage ++ 20144: CoordinatorLayout_Layout_layout_insetEdge ++ 20206: get_ReadyForStatusChange ++ 20231: set_ReadyForStatusChange ++ 20256: CompareExchange ++ 20272: get_ReadyForDischarge ++ 20294: set_ReadyForDischarge ++ 20454: Styleable ++ 20507: add_SurfaceTextureAvailable ++ 20535: TextureViewOnSurfaceTextureAvailable ++ 20807: GetByVehicle ++ 20847: RuntimeTypeHandle ++ 20865: GetTypeFromHandle ++ 21038: TextAppearance_Compat_Notification_Title ++ 21295: ProgressDialogStyle ++ 21315: SetProgressStyle ++ 21332: FontFamilyFont_android_fontStyle ++ 21365: FontFamilyFont_fontStyle ++ 21390: coordinatorLayoutStyle ++ 21507: _contextFileName ++ 21534: get_TypeName ++ 21547: set_TypeName ++ 21560: get_TiTypeName ++ 21575: set_TiTypeName ++ 21590: set_DefaultTextEncodingName ++ 21845: library_name ++ 21882: get_Scheme ++ 21893: set_Scheme ++ 21904: get_DetailScheme ++ 21921: set_DetailScheme ++ 21938: GetDamagedDetailScheme ++ 21961: TextAppearance_Compat_Notification_Time ++ 22010: get_UnixTime ++ 22023: set_UnixTime ++ 22036: get_LastServiceUnixTime ++ 22060: set_LastServiceUnixTime ++ 22084: get_LastServiceGasUnixTime ++ 22111: set_LastServiceGasUnixTime ++ 22222: IAsyncStateMachine ++ 22304: CoordinatorLayout_Layout_layout_keyline ++ 22410: get_Type ++ 22419: set_Type ++ 22428: get_SubType ++ 22440: set_SubType ++ 22452: methodType ++ 22463: VehicleType ++ 22475: _vehicleType ++ 22488: ValueType ++ 22498: get_VehicleRepairMetalWorkType ++ 22529: set_VehicleRepairMetalWorkType ++ 22560: ChangeDiskType ++ 22575: get_DetailType ++ 22590: set_DetailType ++ 22605: ApprovedCustomItemType ++ 22628: get_PhotoType ++ 22642: set_PhotoType ++ 22656: get_VehicleRepairType ++ 22678: set_VehicleRepairType ++ 22700: set_ContentType ++ 22716: get_AccountType ++ 22732: set_AccountType ++ 22748: InspectionPartType ++ 22767: GenerateDiskByType ++ 22786: GradientColor_android_type ++ 22854: System.Core ++ 22969: get_SurfaceTexture ++ 22988: completenessPhotoTexture ++ 23013: SetPreviewTexture ++ 23381: InspectionVideoHistoryResponse ++ 23420: TryParse ++ 23455: get_ExpectedDate ++ 23472: set_ExpectedDate ++ 23623: techinspectionHistoryTiDate ++ 23691: CustomHistoryInspectionItemDate ++ 23976: DetailDamageCoordinate ++ 24064: GetCurrentSalonDamageState ++ 24579: AssemblyTitleAttribute ++ 24602: AsyncStateMachineAttribute ++ 24629: AssemblyTrademarkAttribute ++ 24728: AssemblyFileVersionAttribute ++ 24757: AssemblyConfigurationAttribute ++ 24788: AssemblyDescriptionAttribute ++ 24866: CompilationRelaxationsAttribute ++ 24898: AssemblyProductAttribute ++ 24923: AssemblyCopyrightAttribute ++ 24950: ExportAttribute ++ 24966: AssemblyCompanyAttribute ++ 24991: RuntimeCompatibilityAttribute ++ 25021: ActivityAttribute ++ 25039: Byte ++ 25123: TrySave ++ 25173: newDamageRemove ++ 25189: vehicleInspectionDamageAprove ++ 25369: notification_action_text_size ++ 25399: notification_subtext_size ++ 25425: IndexOf ++ 25433: Exif ++ 25505: notify_panel_notification_icon_bg ++ 25737: System.Threading ++ 25844: ThenByDescending ++ 25861: OrderByDescending ++ 25946: System.Runtime.Versioning ++ 26831: compat_notification_large_icon_max_width ++ 26901: dayOfMonth ++ 26939: taxi ++ 26993: damageTypeGoBack ++ 27010: metalWorkTypeGoBack ++ 27030: detailTypeGoBack ++ 27047: repairTypeGoBack ++ 27141: vehicleRepairTypeSelectGoBack ++ 27229: damageViewGoBack ++ 27320: AsyncCallback ++ 27444: inspectionSalonGoback ++ 27604: videoplaybackgoback ++ 27784: techinspectiontypesgoback ++ 28100: inspectionHistorygoback ++ 28124: inspectionTakePhotoHistorygoback ++ 28157: detailhistorygoback ++ 28177: detailhistoryhistorygoback ++ 28245: GetDamagedDetailSchemeWithStack ++ 28277: GenerateDamagesFromStack ++ 28433: GoNextButton_Click ++ 28452: GoToNextButton_Click ++ 28650: customDamageItemCheckMark ++ 28676: customSalonItemCheckMark ++ 28701: secondary_text_default_material_dark ++ 28738: primary_text_default_material_dark ++ 29728: MemoryStream ++ 29947: InspectionVideoHistoryResponseItem ++ 30246: CustomDamageListViewItem ++ 30271: CustomSalonDamageListViewItem ++ 30569: CustomInspectionHistoryListViewItem ++ 30667: System ++ 30980: status_bar_notification_info_maxnum ++ 31258: notification_big_circle_margin ++ 31466: TextAppearance_Compat_Notification ++ 31608: SetCameraDisplayOrientation ++ 31636: SetDisplayOrientation ++ 31693: System.Globalization ++ 31861: System.Reflection ++ 32120: WebException ++ 32133: AggregateException ++ 32152: CustomApiException ++ 32171: InvalidOperationException ++ 32197: SetException ++ 32210: ContextException ++ 32227: IdsForSalon ++ 32239: GetForSalon ++ 32478: newDamageButton ++ 32546: damageTypeButton ++ 32925: salonApproveButton ++ 33279: newDamagePhotoButton ++ 33356: inspectionNextPhotoButton ++ 33607: tiGoNextButton ++ 33622: repairGoNextButton ++ 33641: sparePartGoNextButton ++ 33663: inspectionHistoryGoNextButton ++ 33693: _goToNextButton ++ 33709: inspectionPhotoHistoryButton ++ 33742: CopyTo ++ 33787: TextAppearance_Compat_Notification_Info ++ 33943: detailHistoryMakePhoto ++ 33966: newDamageTakePhoto ++ 34032: newDamageRetakePhoto ++ 34128: newDamageApprovePhoto ++ 34314: HistoryPhoto ++ 34327: detailhistoryPhoto ++ 34765: System.Linq ++ 34889: get_Year ++ 34898: monthOfYear ++ 34916: year ++ 35188: CustomDamageItemHeader ++ 35367: CustomSalonItemHeader ++ 35479: TextReader ++ 35617: inspectionHistoryheader ++ 35641: detailhistoryheader ++ 35733: ContextProvider ++ 35749: _contextProvider ++ 35775: AsyncVoidMethodBuilder ++ 36058: tag_unhandled_key_event_manager ++ 36234: System.CodeDom.Compiler ++ 36510: videoplaybackOpenInBrowser ++ 36558: get_LayoutInflater ++ 36666: TextWriter ++ 36886: CustomSalonDamageListViewAdapter ++ 37174: CustomTechInspectionHistoryListViewAdapter ++ 37217: CustomInspectionHistoryListViewAdapter ++ 37256: CustomArrayAdapter ++ 37401: get_ApprovedBySecondDriver ++ 37428: set_ApprovedBySecondDriver ++ 37527: get_ApprovedByDriver ++ 37548: set_ApprovedByDriver ++ 37621: CamelCasePropertyNamesContractResolver ++ 37688: get_MediaPlayer ++ 37704: set_MediaPlayer ++ 37720: _mediaPlayer ++ 37890: CoordinatorLayout_Layout_layout_anchor ++ 37929: CoordinatorLayout_Layout_layout_behavior ++ 38128: SetTextColor ++ 38506: get_Extras ++ 38517: extras ++ 38574: System.Diagnostics ++ 38604: FromUnixTimeSeconds ++ 38744: System.Runtime.InteropServices ++ 38775: System.Runtime.CompilerServices ++ 38914: get_Damages ++ 38926: set_Damages ++ 38938: GenerateDamages ++ 38954: DetailSchemeWithDamages ++ 38978: vehicleInspectionDamages ++ 39003: get_SalonDamages ++ 39020: set_SalonDamages ++ 39037: GetSalonDamages ++ 39053: GetDamages ++ 39064: _damages ++ 39073: CoordinatorLayout_Layout_layout_dodgeInsetEdges ++ 39147: QueryIntentActivities ++ 39221: damageNames ++ 39233: DetailSchemes ++ 39397: get_HistoryLines ++ 39414: set_HistoryLines ++ 39431: CoordinatorLayout_keylines ++ 39458: get_DamageTypes ++ 39474: set_DamageTypes ++ 39490: get_TireTypes ++ 39504: set_TireTypes ++ 39518: GetTiTypes ++ 39529: get_DiskTypes ++ 39543: set_DiskTypes ++ 39557: GetPhotoControlTypes ++ 39578: GetInspectionTypes ++ 39777: get_DamageCoordinates ++ 39799: set_DamageCoordinates ++ 39832: GetBytes ++ 39841: bytes ++ 39979: FontFamilyFont_android_fontVariationSettings ++ 40024: FontFamilyFont_fontVariationSettings ++ 40134: TextChangedEventArgs ++ 40180: SurfaceTextureAvailableEventArgs ++ 40213: surfaceTextureAvailableEventArgs ++ 40367: Microsoft.CodeAnalysis ++ 40390: System.Threading.Tasks ++ 40519: LayoutParams ++ 40865: System.Collections ++ 41022: GetHistoryPhotos ++ 41092: tag_unhandled_key_listeners ++ 41276: _damageColors ++ 41539: SaveByParts ++ 41572: FontFamily_fontProviderCerts ++ 41601: CheckDetailExists ++ 41755: ChangeSalonDamageStatus ++ 41950: AddDays ++ 42021: Xamarin.Android.Support.Compat ++ 42052: ContextCompat ++ 42066: ActivityCompat ++ 42146: DamageContract ++ 42219: DetailDamageCoordinateContract ++ 42426: DetailDamageDrawPointContract ++ 42632: System.Net ++ 42753: KeySet ++ 42904: FontFamilyFont_android_fontWeight ++ 42938: FontFamilyFont_fontWeight ++ 42995: compat_notification_large_icon_max_height ++ 43059: secondary_text_default_material_light ++ 43144: _commentTextEdit ++ 43161: textEdit ++ 43196: Exit ++ 43201: exit ++ 43261: IAsyncResult ++ 43274: StartActivityForResult ++ 43317: OnActivityResult ++ 43468: newDamageComment ++ 43485: _damageComment ++ 43795: DrawingPoint ++ 43808: FontFamilyFont ++ 43823: FontFamilyFont_android_font ++ 43851: FontFamilyFont_font ++ 43896: maxAttemptCount ++ 43937: SyncRoot ++ 44176: newDamageCoast ++ 44191: _damageCoast ++ 44236: salonDamageList ++ 44252: damageTypeList ++ 44452: detailhistoryDamagesList ++ 44477: techinspectiontypesList ++ 44581: inspectionHistoryList ++ 44603: videoHistoryList ++ 44620: inspectionTakePhotoHistoryList ++ 44651: detailhistoryhistorylist ++ 44730: FontFamily_fontProviderFetchTimeout ++ 44766: CoordinatorLayout_Layout ++ 44791: SignatureSecondLayout ++ 44813: VehicleInspectionDetailDamageLayout ++ 44849: InspectionRepairMetalWorkTypeLayout ++ 44885: InspectionRepairTypeLayout ++ 44912: InspectionReasonsMoreLayout ++ 44940: SignatureLayout ++ 44956: VehicleTechControlApproveLayout ++ 44988: VehicleInspectionDetailDrawingLayout ++ 45025: LoadingDialogLayout ++ 45045: VehicleInspectionVideoPlaybackLayout ++ 45082: InspectionRepairAdditionalLayout ++ 45115: VehicleInspectionCustomFuelLayout ++ 45149: VehicleInspectionFuelLayout ++ 45177: VehicleInspectionTierLevelLayout ++ 45210: VehicleTechControlLayout ++ 45235: vehicleTechControlLayout ++ 45260: VehicleInspectionLayout ++ 45284: VehicleInspectionSalonLayout ++ 45313: VehicleInspectionVideoLayout ++ 45342: VehicleInspectionCompletenessTakePhotoLayout ++ 45387: LinearLayout ++ 45400: Widget_Support_CoordinatorLayout ++ 45433: VehicleTechControlVehiclesLayout ++ 45466: VehicleStatusesLayout ++ 45488: DriverDischargeReasonsLayout ++ 45517: InspectionReasonsLayout ++ 45541: SpareGroupsLayout ++ 45559: VehicleTechControlDriversLayout ++ 45591: VehicleInspectionCompletenessLayout ++ 45627: VehicleInspectionDraftsLayout ++ 45657: VehicleInspectionDocumentsLayout ++ 45690: VehicleForRejectLayout ++ 45713: InspectionReasonsAfterAccidentLayout ++ 45750: VehicleInspectionCommentLayout ++ 45781: VehicleRepairCommentLayout ++ 45808: SparePartLayout ++ 45824: VehicleTechInspectionTypeListLayout ++ 45860: VehicleTechInspectionWorkListLayout ++ 45896: ImageViewLayout ++ 45912: imageViewLayout ++ 45928: ParkViewLayout ++ 45943: SignaturePreviewLayout ++ 45966: VehicleInspectionDetailPreviewLayout ++ 46003: VehicleInspectionDetailPhotoPreviewLayout ++ 46045: VehicleInspectionChangeHistoryLayout ++ 46082: VehicleInspectionDetailHistoryLayout ++ 46119: VehicleInspectionHistoryLayout ++ 46150: VehicleTechInspectionHistoryLayout ++ 46185: InspectionTakePhotoHistoryLayout ++ 46218: VehicleInspectionDetailHistoryHistoryLayout ++ 46262: videolayout ++ 46284: MoveNext ++ 46293: repairAditionaGoNext ++ 46314: inspectionDetailGoNext ++ 46337: detailPreviewGoNext ++ 46357: newDamageGoToNext ++ 46375: isNext ++ 46382: Android.Text ++ 46395: System.Text ++ 46407: get_Text ++ 46416: set_Text ++ 46425: vehicleTechControlMileageText ++ 46455: inspectionChangeNewMileageText ++ 46486: inspectionHistoryMileageText ++ 46515: _mileageText ++ 46528: MakeText ++ 46537: _titleText ++ 46548: vehicleRepairTypeText ++ 46570: _searchText ++ 46582: vehicleInspectionCustomFuelText ++ 46614: tierLevelText ++ 46628: Widget_Compat_NotificationActionText ++ 46665: customApprovedHeaderText ++ 46690: _headerText ++ 46702: _passwordEditText ++ 46720: _mileagEditText ++ 46736: driverSecondSearchEditText ++ 46763: vehicleSearchEditText ++ 46785: techcotroldriverSearchEditText ++ 46816: sparePartSearchEditText ++ 46840: _loginEditText ++ 46855: _stoEditText ++ 46868: vehicleInspectionCommentText ++ 46897: vehicleRepairCommentText ++ 46922: vehicleRejectCommentText ++ 46947: detailHistoryCommentText ++ 46972: progressViewText ++ 46989: next ++ 46994: notification_top_pad_large_text ++ 47026: action_text ++ 47038: inspectionsecondsignaturetext ++ 47068: inspectionsignaturetext ++ 47092: get_Context ++ 47104: SaveVehicleContext ++ 47123: GetVehicleContext ++ 47141: SaveAccountContext ++ 47160: GetAccountContext ++ 47178: context ++ 47186: txt ++ 47196: NewDamageMenu ++ 47210: NewDamagePhotoMenu ++ 47364: inspectionHistoryImageView ++ 47391: NewDamageView ++ 47416: DamageTypeView ++ 47431: DetailTypeView ++ 47446: TextureView ++ 47458: _textureView ++ 47602: VehicleRepairTypeSelectView ++ 47775: metalWorkTypeListView ++ 47797: detailTypeListView ++ 47816: vehicleRepairTypeListView ++ 47842: repairTypeListView ++ 48005: _headerTextView ++ 48021: textView ++ 48158: newDamagePhotoPreview ++ 48481: Max ++ 48485: FontFamilyFont_android_ttcIndex ++ 48517: FontFamilyFont_ttcIndex ++ 48541: Matrix ++ 48548: CheckBox ++ 48557: get_ViewBox ++ 48569: set_ViewBox ++ 48581: viewBox ++ 48589: GetItemBy ++ 48599: get_Day ++ 48607: get_Today ++ 48617: Play ++ 48622: get_DefaultDisplay ++ 48641: cm_gray ++ 48649: DecodeByteArray ++ 48665: InitializeArray ++ 48681: ToArray ++ 48689: set_Accuracy ++ 48702: _changeStatusReady ++ 48721: body ++ 48726: get_Key ++ 48734: ContainsKey ++ 48746: FontFamily_fontProviderFetchStrategy ++ 48783: FontFamily ++ 48794: IsReadOnly ++ 48805: _isReadOnly ++ 48817: Any ++ 48821: OnDestroy ++ 48831: Copy ++ 48836: _buttonInfoDictionary ++ 48858: detailPreviewGallery ++ 48879: gallery ++ 48887: FontFamily_fontProviderQuery ++ 48916: BitmapFactory ++ 48930: get_ExternalStorageDirectory ++ 48959: Ttc.Data.Repository ++ 48979: get_ImageRepository ++ 48999: get_NewDamageRepository ++ 49023: _newDamageRepository ++ 49044: _imageRepository ++ 49061: get_VehicleRepository ++ 49083: _vehicleRepository ++ 49102: get_VehicleRepairReferenceTypeRepository ++ 49143: get_DamageTypeRepository ++ 49168: _vehicleDamageTypeRepository ++ 49197: _damageTypeRepository ++ 49219: get_InspectionRepairMetalWorkTypeRepository ++ 49263: get_DetailTypeRepository ++ 49288: get_SetItemTypeRepository ++ 49314: get_VehicleTechInspectionTypeRepository ++ 49354: set_VehicleTechInspectionTypeRepository ++ 49394: _vehicleTechInspectionTypeRepository ++ 49431: get_InspectionReasonTypeRepository ++ 49466: get_PhotoTypeRepository ++ 49490: _photoTypeRepository ++ 49511: get_RepairTypeRepository ++ 49536: _repairTypeRepository ++ 49558: _vehicleDetailStateRepository ++ 49588: get_VehicleTechControlRepository ++ 49621: _vehicleTechControlRepository ++ 49651: get_TechInspectionItemRepository ++ 49684: _techInspectionItemRepository ++ 49714: get_InspectionRepository ++ 49739: get_TechInspectionRepository ++ 49768: _techInspectionRepository ++ 49794: get_VehicleStatePhotoInspectionRepository ++ 49836: _vehicleStatePhotoInspectionRepository ++ 49875: _inspectionRepository ++ 49897: get_InspectionReasonRepository ++ 49928: _inspectionReasonRepository ++ 49956: get_InspectionVideoRepository ++ 49986: _inspectionVideoRepository ++ 50013: get_DriverRepository ++ 50034: get_VehicleRepairRepository ++ 50062: _vehicleRepairRepository ++ 50087: get_CustomerSettingsRepository ++ 50118: _customerSettingsRepository ++ 50146: get_VehicleStateDraftRepository ++ 50178: _vehicleStateDraftRepository ++ 50207: get_AccountRepository ++ 50229: _accountRepository ++ 50248: get_SparePartRepository ++ 50272: RestRepository ++ 50287: get_VehicleStatusHistoryRepository ++ 50322: _vehicleStatusHistoryRepository ++ 50354: GetStateFromHistory ++ 50374: CachedCarHistory ++ 50391: IsHistory ++ 50401: GetHistory ++ 50412: photocarhistory ++ 50428: detailhistoryhistory ++ 50449: Retry ++ 50455: Entry ++ 50461: set_JpegQuality ++ 50477: op_Equality ++ 50489: Availability ++ 50502: get_Visibility ++ 50517: set_Visibility ++ 50532: _extraVisibility ++ 50549: FontFamily_fontProviderAuthority ++ 50582: CoordinatorLayout_Layout_layout_anchorGravity ++ 50628: CoordinatorLayout_Layout_android_layout_gravity ++ 50676: get_Activity ++ 50689: SignatureSecondActivity ++ 50713: VehicleInspectionDetailDamageActivity ++ 50751: VehicleInspectionDetailSchemeActivity ++ 50789: InspectionReasonsMoreActivity ++ 50819: SignatureActivity ++ 50837: BaseActivity ++ 50850: VehicleInspectionVideoPlaybackActivity ++ 50889: InspectionRepairAdditionalActivity ++ 50924: VehicleInspectionCustomFuelActivity ++ 50960: VehicleInspectionFuelActivity ++ 50990: VehicleInspectionCustomGasFuelActivity ++ 51029: VehicleInspectionGasFuelActivity ++ 51062: VehicleInspectionTierLevelActivity ++ 51097: VehicleTechControlActivity ++ 51124: VehicleDriverTechControlActivity ++ 51157: SplashScreenActivity ++ 51178: ManagerMainActivity ++ 51198: VehicleInspectionActivity ++ 51224: VehicleInspectionSalonActivity ++ 51255: VehicleInspectionVideoActivity ++ 51286: NewDamagePhotoActivity ++ 51309: VehicleInspectionDetailTakePhotoActivity ++ 51350: InspectionTakePhotoActivity ++ 51378: VehicleInspectionCompletenessTakePhotoActivity ++ 51425: VehicleRepairActivity ++ 51447: VehicleTechControlVehiclesActivity ++ 51482: VehicleStatusesActivity ++ 51506: DriverDischargeReasonsActivity ++ 51537: InspectionReasonsActivity ++ 51563: VehicleTechControlDriversActivity ++ 51597: VehicleRepairsActivity ++ 51620: VehicleInspectionCompletenessActivity ++ 51658: VehicleInspectionDraftsActivity ++ 51690: VehicleInspectionDocumentsActivity ++ 51725: VehicleForRejectActivity ++ 51750: VehiclesForRejectActivity ++ 51776: VehicleRepairTypeSelectActivity ++ 51808: InspectionReasonsAfterAccidentActivity ++ 51847: VehicleInspectionCommentActivity ++ 51880: VehicleRepairCommentActivity ++ 51909: StartActivity ++ 51923: VehicleTechInspectionTypeListActivity ++ 51961: VehicleTechInspectionWorkListActivity ++ 51999: ImageViewActivity ++ 52017: ParkViewActivity ++ 52034: SignaturePreviewActivity ++ 52059: VehicleInspectionDetailPhotoPreviewActivity ++ 52103: VehicleInspectionChangeHistoryActivity ++ 52142: VehicleTechInspectionHistoryActivity ++ 52179: InspectionTakePhotoHistoryActivity ++ 52214: InspectionPhotoHistoryActivity ++ 52245: activity ++ 52254: get_AccidentGuilty ++ 52273: set_AccidentGuilty ++ 52292: GetAccidentGuilty ++ 52310: IsNullOrEmpty ++ 52324: isEmpty diff --git a/recon/findings/scheme/svg_context.txt b/recon/findings/scheme/svg_context.txt new file mode 100644 index 0000000..d5350e1 --- /dev/null +++ b/recon/findings/scheme/svg_context.txt @@ -0,0 +1,37 @@ +=== Strings BEFORE exterior SVG (idx 391) === +[376] US+201813: 'Левый передний диск ' +[377] US+201855: 'Правый передний диск ' +[378] US+201899: 'Правый задний диск ' +[379] US+201939: 'Левый задний диск ' +[380] US+201977: 'штампованный' +[381] US+202003: 'штампованный с колпаком' +[382] US+202051: 'литой' +[383] US+202063: '' +[393] US+324378: '' +[394] US+324458: '' +[395] US+324540: '' +[401] US+342196: '' +[402] US+342429: ' + +US+151701: + +US+152444: + +US+153157: + +US+198789: " /> + +US+342196: + +US+346287: + +US+347230: + +US+451703: + +US+453461: + +US+453726: + +US+482566: + +US+504522: + +US+505185: + +US+505848: + +US+506515: + +US+507182: + +US+508183: + +US+508758: + +US+509009: + +US+511008: + +US+513813: \ No newline at end of file diff --git a/recon/findings/scheme/us_large_10_offset109159.txt b/recon/findings/scheme/us_large_10_offset109159.txt new file mode 100644 index 0000000..a27704c --- /dev/null +++ b/recon/findings/scheme/us_large_10_offset109159.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_11_offset111910.txt b/recon/findings/scheme/us_large_11_offset111910.txt new file mode 100644 index 0000000..7ff5822 --- /dev/null +++ b/recon/findings/scheme/us_large_11_offset111910.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_12_offset114753.txt b/recon/findings/scheme/us_large_12_offset114753.txt new file mode 100644 index 0000000..21262e2 --- /dev/null +++ b/recon/findings/scheme/us_large_12_offset114753.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_13_offset118420.txt b/recon/findings/scheme/us_large_13_offset118420.txt new file mode 100644 index 0000000..844671a --- /dev/null +++ b/recon/findings/scheme/us_large_13_offset118420.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_14_offset120501.txt b/recon/findings/scheme/us_large_14_offset120501.txt new file mode 100644 index 0000000..30056ad --- /dev/null +++ b/recon/findings/scheme/us_large_14_offset120501.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_15_offset122306.txt b/recon/findings/scheme/us_large_15_offset122306.txt new file mode 100644 index 0000000..bdb4733 --- /dev/null +++ b/recon/findings/scheme/us_large_15_offset122306.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_16_offset124063.txt b/recon/findings/scheme/us_large_16_offset124063.txt new file mode 100644 index 0000000..f9828c9 --- /dev/null +++ b/recon/findings/scheme/us_large_16_offset124063.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_17_offset125816.txt b/recon/findings/scheme/us_large_17_offset125816.txt new file mode 100644 index 0000000..2368e36 --- /dev/null +++ b/recon/findings/scheme/us_large_17_offset125816.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_18_offset127623.txt b/recon/findings/scheme/us_large_18_offset127623.txt new file mode 100644 index 0000000..1d4f5a4 --- /dev/null +++ b/recon/findings/scheme/us_large_18_offset127623.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_19_offset132972.txt b/recon/findings/scheme/us_large_19_offset132972.txt new file mode 100644 index 0000000..1745725 --- /dev/null +++ b/recon/findings/scheme/us_large_19_offset132972.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_1_offset84620.txt b/recon/findings/scheme/us_large_1_offset84620.txt new file mode 100644 index 0000000..a265237 --- /dev/null +++ b/recon/findings/scheme/us_large_1_offset84620.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_20_offset137887.txt b/recon/findings/scheme/us_large_20_offset137887.txt new file mode 100644 index 0000000..c7449c7 --- /dev/null +++ b/recon/findings/scheme/us_large_20_offset137887.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_21_offset138994.txt b/recon/findings/scheme/us_large_21_offset138994.txt new file mode 100644 index 0000000..68e8e0b --- /dev/null +++ b/recon/findings/scheme/us_large_21_offset138994.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_22_offset140865.txt b/recon/findings/scheme/us_large_22_offset140865.txt new file mode 100644 index 0000000..093b064 --- /dev/null +++ b/recon/findings/scheme/us_large_22_offset140865.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_23_offset142834.txt b/recon/findings/scheme/us_large_23_offset142834.txt new file mode 100644 index 0000000..e32f76b --- /dev/null +++ b/recon/findings/scheme/us_large_23_offset142834.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_24_offset144333.txt b/recon/findings/scheme/us_large_24_offset144333.txt new file mode 100644 index 0000000..d7ade54 --- /dev/null +++ b/recon/findings/scheme/us_large_24_offset144333.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_25_offset147262.txt b/recon/findings/scheme/us_large_25_offset147262.txt new file mode 100644 index 0000000..6bafc9e --- /dev/null +++ b/recon/findings/scheme/us_large_25_offset147262.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_26_offset153870.txt b/recon/findings/scheme/us_large_26_offset153870.txt new file mode 100644 index 0000000..166ef91 --- /dev/null +++ b/recon/findings/scheme/us_large_26_offset153870.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_27_offset155413.txt b/recon/findings/scheme/us_large_27_offset155413.txt new file mode 100644 index 0000000..d83c167 --- /dev/null +++ b/recon/findings/scheme/us_large_27_offset155413.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_28_offset156808.txt b/recon/findings/scheme/us_large_28_offset156808.txt new file mode 100644 index 0000000..6539de6 --- /dev/null +++ b/recon/findings/scheme/us_large_28_offset156808.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_29_offset158325.txt b/recon/findings/scheme/us_large_29_offset158325.txt new file mode 100644 index 0000000..be47d5c --- /dev/null +++ b/recon/findings/scheme/us_large_29_offset158325.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_2_offset89239.txt b/recon/findings/scheme/us_large_2_offset89239.txt new file mode 100644 index 0000000..38749c8 --- /dev/null +++ b/recon/findings/scheme/us_large_2_offset89239.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_30_offset159392.txt b/recon/findings/scheme/us_large_30_offset159392.txt new file mode 100644 index 0000000..45d87c7 --- /dev/null +++ b/recon/findings/scheme/us_large_30_offset159392.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_31_offset160685.txt b/recon/findings/scheme/us_large_31_offset160685.txt new file mode 100644 index 0000000..9413549 --- /dev/null +++ b/recon/findings/scheme/us_large_31_offset160685.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_32_offset162068.txt b/recon/findings/scheme/us_large_32_offset162068.txt new file mode 100644 index 0000000..bf3ef00 --- /dev/null +++ b/recon/findings/scheme/us_large_32_offset162068.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_33_offset167361.txt b/recon/findings/scheme/us_large_33_offset167361.txt new file mode 100644 index 0000000..969b0b1 --- /dev/null +++ b/recon/findings/scheme/us_large_33_offset167361.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_34_offset172712.txt b/recon/findings/scheme/us_large_34_offset172712.txt new file mode 100644 index 0000000..09cbda0 --- /dev/null +++ b/recon/findings/scheme/us_large_34_offset172712.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_35_offset183329.txt b/recon/findings/scheme/us_large_35_offset183329.txt new file mode 100644 index 0000000..b56fb2e --- /dev/null +++ b/recon/findings/scheme/us_large_35_offset183329.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_36_offset184873.txt b/recon/findings/scheme/us_large_36_offset184873.txt new file mode 100644 index 0000000..ce8da30 --- /dev/null +++ b/recon/findings/scheme/us_large_36_offset184873.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_37_offset187098.txt b/recon/findings/scheme/us_large_37_offset187098.txt new file mode 100644 index 0000000..a250fcc --- /dev/null +++ b/recon/findings/scheme/us_large_37_offset187098.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_38_offset188581.txt b/recon/findings/scheme/us_large_38_offset188581.txt new file mode 100644 index 0000000..4368b2d --- /dev/null +++ b/recon/findings/scheme/us_large_38_offset188581.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_39_offset190360.txt b/recon/findings/scheme/us_large_39_offset190360.txt new file mode 100644 index 0000000..9137f51 --- /dev/null +++ b/recon/findings/scheme/us_large_39_offset190360.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_3_offset90846.txt b/recon/findings/scheme/us_large_3_offset90846.txt new file mode 100644 index 0000000..6705c2c --- /dev/null +++ b/recon/findings/scheme/us_large_3_offset90846.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_40_offset197560.txt b/recon/findings/scheme/us_large_40_offset197560.txt new file mode 100644 index 0000000..87a0ffb --- /dev/null +++ b/recon/findings/scheme/us_large_40_offset197560.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_42_offset210660.txt b/recon/findings/scheme/us_large_42_offset210660.txt new file mode 100644 index 0000000..f86b274 --- /dev/null +++ b/recon/findings/scheme/us_large_42_offset210660.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_43_offset212935.txt b/recon/findings/scheme/us_large_43_offset212935.txt new file mode 100644 index 0000000..f688169 --- /dev/null +++ b/recon/findings/scheme/us_large_43_offset212935.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_44_offset221532.txt b/recon/findings/scheme/us_large_44_offset221532.txt new file mode 100644 index 0000000..ce00b75 --- /dev/null +++ b/recon/findings/scheme/us_large_44_offset221532.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_45_offset223807.txt b/recon/findings/scheme/us_large_45_offset223807.txt new file mode 100644 index 0000000..d7b8067 --- /dev/null +++ b/recon/findings/scheme/us_large_45_offset223807.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_46_offset232404.txt b/recon/findings/scheme/us_large_46_offset232404.txt new file mode 100644 index 0000000..653aac7 --- /dev/null +++ b/recon/findings/scheme/us_large_46_offset232404.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_47_offset234679.txt b/recon/findings/scheme/us_large_47_offset234679.txt new file mode 100644 index 0000000..9c4fb25 --- /dev/null +++ b/recon/findings/scheme/us_large_47_offset234679.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_48_offset243276.txt b/recon/findings/scheme/us_large_48_offset243276.txt new file mode 100644 index 0000000..bc9a510 --- /dev/null +++ b/recon/findings/scheme/us_large_48_offset243276.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_49_offset245551.txt b/recon/findings/scheme/us_large_49_offset245551.txt new file mode 100644 index 0000000..d7bb395 --- /dev/null +++ b/recon/findings/scheme/us_large_49_offset245551.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_4_offset92714.txt b/recon/findings/scheme/us_large_4_offset92714.txt new file mode 100644 index 0000000..eae855d --- /dev/null +++ b/recon/findings/scheme/us_large_4_offset92714.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_50_offset324540.txt b/recon/findings/scheme/us_large_50_offset324540.txt new file mode 100644 index 0000000..ca78d51 --- /dev/null +++ b/recon/findings/scheme/us_large_50_offset324540.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_51_offset330189.txt b/recon/findings/scheme/us_large_51_offset330189.txt new file mode 100644 index 0000000..ae0bbef --- /dev/null +++ b/recon/findings/scheme/us_large_51_offset330189.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_52_offset332198.txt b/recon/findings/scheme/us_large_52_offset332198.txt new file mode 100644 index 0000000..57d77fa --- /dev/null +++ b/recon/findings/scheme/us_large_52_offset332198.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_53_offset333995.txt b/recon/findings/scheme/us_large_53_offset333995.txt new file mode 100644 index 0000000..577b5c7 --- /dev/null +++ b/recon/findings/scheme/us_large_53_offset333995.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_54_offset338054.txt b/recon/findings/scheme/us_large_54_offset338054.txt new file mode 100644 index 0000000..9d8e292 --- /dev/null +++ b/recon/findings/scheme/us_large_54_offset338054.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_55_offset342429.txt b/recon/findings/scheme/us_large_55_offset342429.txt new file mode 100644 index 0000000..3a4a740 --- /dev/null +++ b/recon/findings/scheme/us_large_55_offset342429.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_56_offset344372.txt b/recon/findings/scheme/us_large_56_offset344372.txt new file mode 100644 index 0000000..470fa24 --- /dev/null +++ b/recon/findings/scheme/us_large_56_offset344372.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_57_offset348151.txt b/recon/findings/scheme/us_large_57_offset348151.txt new file mode 100644 index 0000000..7c6bdc3 --- /dev/null +++ b/recon/findings/scheme/us_large_57_offset348151.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_58_offset353102.txt b/recon/findings/scheme/us_large_58_offset353102.txt new file mode 100644 index 0000000..811d057 --- /dev/null +++ b/recon/findings/scheme/us_large_58_offset353102.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_59_offset358861.txt b/recon/findings/scheme/us_large_59_offset358861.txt new file mode 100644 index 0000000..bdb06a6 --- /dev/null +++ b/recon/findings/scheme/us_large_59_offset358861.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_5_offset94700.txt b/recon/findings/scheme/us_large_5_offset94700.txt new file mode 100644 index 0000000..637a718 --- /dev/null +++ b/recon/findings/scheme/us_large_5_offset94700.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_60_offset363706.txt b/recon/findings/scheme/us_large_60_offset363706.txt new file mode 100644 index 0000000..2b40b9a --- /dev/null +++ b/recon/findings/scheme/us_large_60_offset363706.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_61_offset369021.txt b/recon/findings/scheme/us_large_61_offset369021.txt new file mode 100644 index 0000000..29b1aba --- /dev/null +++ b/recon/findings/scheme/us_large_61_offset369021.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_62_offset376392.txt b/recon/findings/scheme/us_large_62_offset376392.txt new file mode 100644 index 0000000..f5d25cb --- /dev/null +++ b/recon/findings/scheme/us_large_62_offset376392.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_63_offset378635.txt b/recon/findings/scheme/us_large_63_offset378635.txt new file mode 100644 index 0000000..85c7f78 --- /dev/null +++ b/recon/findings/scheme/us_large_63_offset378635.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_64_offset380136.txt b/recon/findings/scheme/us_large_64_offset380136.txt new file mode 100644 index 0000000..d0d4658 --- /dev/null +++ b/recon/findings/scheme/us_large_64_offset380136.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_65_offset383275.txt b/recon/findings/scheme/us_large_65_offset383275.txt new file mode 100644 index 0000000..ea0d1a9 --- /dev/null +++ b/recon/findings/scheme/us_large_65_offset383275.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_66_offset386232.txt b/recon/findings/scheme/us_large_66_offset386232.txt new file mode 100644 index 0000000..14e29cd --- /dev/null +++ b/recon/findings/scheme/us_large_66_offset386232.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_67_offset391601.txt b/recon/findings/scheme/us_large_67_offset391601.txt new file mode 100644 index 0000000..aa1df25 --- /dev/null +++ b/recon/findings/scheme/us_large_67_offset391601.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_68_offset397462.txt b/recon/findings/scheme/us_large_68_offset397462.txt new file mode 100644 index 0000000..ebd1d80 --- /dev/null +++ b/recon/findings/scheme/us_large_68_offset397462.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_69_offset403197.txt b/recon/findings/scheme/us_large_69_offset403197.txt new file mode 100644 index 0000000..49b732d --- /dev/null +++ b/recon/findings/scheme/us_large_69_offset403197.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_6_offset95957.txt b/recon/findings/scheme/us_large_6_offset95957.txt new file mode 100644 index 0000000..99a2801 --- /dev/null +++ b/recon/findings/scheme/us_large_6_offset95957.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_70_offset408296.txt b/recon/findings/scheme/us_large_70_offset408296.txt new file mode 100644 index 0000000..319fddc --- /dev/null +++ b/recon/findings/scheme/us_large_70_offset408296.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_71_offset413105.txt b/recon/findings/scheme/us_large_71_offset413105.txt new file mode 100644 index 0000000..783bb35 --- /dev/null +++ b/recon/findings/scheme/us_large_71_offset413105.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_72_offset420252.txt b/recon/findings/scheme/us_large_72_offset420252.txt new file mode 100644 index 0000000..06bf610 --- /dev/null +++ b/recon/findings/scheme/us_large_72_offset420252.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_73_offset428239.txt b/recon/findings/scheme/us_large_73_offset428239.txt new file mode 100644 index 0000000..c4fd213 --- /dev/null +++ b/recon/findings/scheme/us_large_73_offset428239.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_74_offset436258.txt b/recon/findings/scheme/us_large_74_offset436258.txt new file mode 100644 index 0000000..a69c471 --- /dev/null +++ b/recon/findings/scheme/us_large_74_offset436258.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_75_offset441025.txt b/recon/findings/scheme/us_large_75_offset441025.txt new file mode 100644 index 0000000..0742dad --- /dev/null +++ b/recon/findings/scheme/us_large_75_offset441025.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_76_offset446910.txt b/recon/findings/scheme/us_large_76_offset446910.txt new file mode 100644 index 0000000..67d5adb --- /dev/null +++ b/recon/findings/scheme/us_large_76_offset446910.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_77_offset452178.txt b/recon/findings/scheme/us_large_77_offset452178.txt new file mode 100644 index 0000000..07e60e0 --- /dev/null +++ b/recon/findings/scheme/us_large_77_offset452178.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_78_offset453991.txt b/recon/findings/scheme/us_large_78_offset453991.txt new file mode 100644 index 0000000..50c85c5 --- /dev/null +++ b/recon/findings/scheme/us_large_78_offset453991.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_79_offset456242.txt b/recon/findings/scheme/us_large_79_offset456242.txt new file mode 100644 index 0000000..ffc4dee --- /dev/null +++ b/recon/findings/scheme/us_large_79_offset456242.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_7_offset99572.txt b/recon/findings/scheme/us_large_7_offset99572.txt new file mode 100644 index 0000000..ebe7e5d --- /dev/null +++ b/recon/findings/scheme/us_large_7_offset99572.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_80_offset458765.txt b/recon/findings/scheme/us_large_80_offset458765.txt new file mode 100644 index 0000000..e80a404 --- /dev/null +++ b/recon/findings/scheme/us_large_80_offset458765.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_81_offset462090.txt b/recon/findings/scheme/us_large_81_offset462090.txt new file mode 100644 index 0000000..296f284 --- /dev/null +++ b/recon/findings/scheme/us_large_81_offset462090.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_82_offset465285.txt b/recon/findings/scheme/us_large_82_offset465285.txt new file mode 100644 index 0000000..e288b11 --- /dev/null +++ b/recon/findings/scheme/us_large_82_offset465285.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_83_offset468722.txt b/recon/findings/scheme/us_large_83_offset468722.txt new file mode 100644 index 0000000..803cf4a --- /dev/null +++ b/recon/findings/scheme/us_large_83_offset468722.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_84_offset470095.txt b/recon/findings/scheme/us_large_84_offset470095.txt new file mode 100644 index 0000000..13e9b4a --- /dev/null +++ b/recon/findings/scheme/us_large_84_offset470095.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_85_offset473760.txt b/recon/findings/scheme/us_large_85_offset473760.txt new file mode 100644 index 0000000..055b1ca --- /dev/null +++ b/recon/findings/scheme/us_large_85_offset473760.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_86_offset475107.txt b/recon/findings/scheme/us_large_86_offset475107.txt new file mode 100644 index 0000000..724840d --- /dev/null +++ b/recon/findings/scheme/us_large_86_offset475107.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_87_offset478024.txt b/recon/findings/scheme/us_large_87_offset478024.txt new file mode 100644 index 0000000..61d4d5e --- /dev/null +++ b/recon/findings/scheme/us_large_87_offset478024.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_88_offset479597.txt b/recon/findings/scheme/us_large_88_offset479597.txt new file mode 100644 index 0000000..92f20d6 --- /dev/null +++ b/recon/findings/scheme/us_large_88_offset479597.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_89_offset482566.txt b/recon/findings/scheme/us_large_89_offset482566.txt new file mode 100644 index 0000000..29f2446 --- /dev/null +++ b/recon/findings/scheme/us_large_89_offset482566.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_8_offset103281.txt b/recon/findings/scheme/us_large_8_offset103281.txt new file mode 100644 index 0000000..8d4af9c --- /dev/null +++ b/recon/findings/scheme/us_large_8_offset103281.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_90_offset483689.txt b/recon/findings/scheme/us_large_90_offset483689.txt new file mode 100644 index 0000000..60ee043 --- /dev/null +++ b/recon/findings/scheme/us_large_90_offset483689.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_91_offset488898.txt b/recon/findings/scheme/us_large_91_offset488898.txt new file mode 100644 index 0000000..f6fa434 --- /dev/null +++ b/recon/findings/scheme/us_large_91_offset488898.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_92_offset494107.txt b/recon/findings/scheme/us_large_92_offset494107.txt new file mode 100644 index 0000000..e6c2e5d --- /dev/null +++ b/recon/findings/scheme/us_large_92_offset494107.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_93_offset509661.txt b/recon/findings/scheme/us_large_93_offset509661.txt new file mode 100644 index 0000000..e65767c --- /dev/null +++ b/recon/findings/scheme/us_large_93_offset509661.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_large_9_offset106364.txt b/recon/findings/scheme/us_large_9_offset106364.txt new file mode 100644 index 0000000..9780372 --- /dev/null +++ b/recon/findings/scheme/us_large_9_offset106364.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/recon/findings/scheme/us_strings_near_svg.txt b/recon/findings/scheme/us_strings_near_svg.txt new file mode 100644 index 0000000..debcdee Binary files /dev/null and b/recon/findings/scheme/us_strings_near_svg.txt differ diff --git a/recon/findings/scheme/zone_labels.json b/recon/findings/scheme/zone_labels.json new file mode 100644 index 0000000..09726c8 --- /dev/null +++ b/recon/findings/scheme/zone_labels.json @@ -0,0 +1,66 @@ +{ + "_meta": { + "source": "Ttc.dll #US heap, offsets 0x751c7–0x75a81 (consecutive UTF-16 LE strings)", + "confidence": "HIGH — 57 strings appear in exact sequential order in a single contiguous block, matching the 57 class IDs (0..56) in exterior_scheme_complete.svg", + "geographic_check": "confirmed: zone 0 (front bumper) at SVG Y=119 (top), zone 22 (rear bumper) at Y=1064 (bottom); left-side zones have small X, right-side zones have large X", + "note_typo": "zone 15 has a typo in the vendor binary: 'правй порог' (missing 'ы') — preserve as-is for matching but display as 'правый порог'", + "note_zones_34_57": "zones 34–37 are fog lights (противотуманки), zones 38–39 mirrors, 40–43 disks, 44–46 salon seats, 47–48 rear sill extensions, 49–56 bumper sub-parts and pillars" + }, + "0": "передний бампер", + "1": "передняя правая фара", + "2": "передняя левая фара", + "3": "радиатор", + "4": "капот", + "5": "лобовое стекло", + "6": "переднее левое крыло", + "7": "переднее правое крыло", + "8": "заднее левое крыло", + "9": "заднее правое крыло", + "10": "передняя левая дверь", + "11": "передняя правая дверь", + "12": "задняя левая дверь", + "13": "задняя правая дверь", + "14": "левый порог", + "15": "правый порог", + "16": "крыша", + "17": "передняя правая резина", + "18": "передняя левая резина", + "19": "задняя правая резина", + "20": "задняя левая резина", + "21": "багажник", + "22": "задний бампер", + "23": "заднее стекло", + "24": "переднее левое стекло", + "25": "переднее правое стекло", + "26": "заднее левое стекло", + "27": "заднее правое стекло", + "28": "передняя левая форточка", + "29": "передняя правая форточка", + "30": "задняя правая форточка", + "31": "задняя левая форточка", + "32": "задняя правая фара", + "33": "задняя левая фара", + "34": "передняя правая противотуманка", + "35": "передняя левая противотуманка", + "36": "задняя правая противотуманка", + "37": "задняя левая противотуманка", + "38": "правое зеркало", + "39": "левое зеркало", + "40": "задний левый диск", + "41": "задний правый диск", + "42": "передний правый диск", + "43": "передний левый диск", + "44": "водительское сидение", + "45": "пассажирское сидение", + "46": "задний диван", + "47": "задний левый порог", + "48": "задний правый порог", + "49": "задняя левая часть бампера", + "50": "задняя правая часть бампера", + "51": "передняя левая часть бампера", + "52": "передняя правая часть бампера", + "53": "задняя левая стойка", + "54": "задняя правая стойка", + "55": "передняя левая стойка", + "56": "передняя правая стойка" +}