Skip to content

Commit 6adb5e8

Browse files
authored
Merge pull request #2 from josephbergdoll/feature/improved-ovg-converters
Refactor: CLI, auto-detection of format and dimensions, conversion of entire directories
2 parents de0bf45 + 341101a commit 6adb5e8

File tree

3 files changed

+1031
-217
lines changed

3 files changed

+1031
-217
lines changed

README.md

Lines changed: 266 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,277 @@
1-
# RCD330 Image Utilities
1+
# RCD330 Image Utilities: OVG Image Format Converter
22

3-
Python 3 only because who uses 2 any more? Nerds, that's who. To run these, you need Pillow and numpy. Install via pip:
3+
This repository contains tools for converting OVG (proprietary image format) files to PNG and back, specifically designed for car stereo firmware modification.
44

5-
`pip install Pillow numpy`
5+
## Background
66

7-
## Tools
7+
OVG files are a proprietary image format used in the RCD330 (and likely other) car stereo firmware. They use RLE (Run-Length Encoding) compression with RGBA color data, saved to a binary file extension.
88

9-
### Boot logos
9+
This project was spun up out of a desire to skin more of the interface than ust the bootlogo. Much credit goes to [@cr3ative](https://github.com/cr3ative/) for sharing his initial discoveries publicly on GitHub in the [RCD 330 Image Utilities repo](https://github.com/cr3ative/rcd_330g_image_utilities).
1010

11-
* To convert an RCD `logo.bin` file to a PNG called `logo.png`, edit and run `python3 rcd_to_png.py`
12-
* To convert any 800x480 PNG called `logo.png` to RCD `logo.bin`, run `python3 png_to_rcd.py`
11+
## File Format Description
1312

14-
I haven't yet flashed any results to my personal RCD330. This is a work in progress, but the BIN conversion works both ways and matches exactly the expected format.
13+
The converter supports two different OVG file formats:
1514

16-
- Credit to `mengxp` for the original image update tarball and conversion utility.
17-
- Credit to @tef and @marksteward for helping me understand XORs!
15+
### 1. RLE-Compressed OVG Format
16+
The traditional OVG format uses RLE (Run-Length Encoding) compression:
17+
- **Command Block**: 1 byte indicating compression type and pixel count
18+
- **Pixel Data**: RGBA data (4 bytes per pixel)
19+
- **RLE Compression**: Efficient encoding for repeated pixels
1820

19-
### OVG bin files (flags, etc)
21+
### 2. Raw RGBA Format
22+
Some OVG files contain raw RGBA pixel data without compression:
23+
- **Direct RGBA**: 4 bytes per pixel (R, G, B, A)
24+
- **No compression**: Pixel data stored sequentially
25+
- **Square dimensions**: Often forms perfect or near-perfect squares
2026

21-
* To convert an `_ovg.bin` file to PNG, edit and run `python3 ovg_to_png.py`
22-
* To convert any PNG to an `_ovg.bin` compatible file, edit and run `python3 png_to_ovg.py`
27+
The converter automatically detects which format is used and processes accordingly.
2328

24-
- Credit to `Niklas_1414` for pushing me to examine these files. Further credits in the python file.
29+
### RLE Command Block Format (Format 1 only)
30+
```
31+
Bit 7 (MSB): Compression flag (1 = compressed, 0 = uncompressed)
32+
Bits 6-0: Pixel count - 1 (0-127 representing 1-128 pixels)
33+
```
2534

26-
Cheers!
35+
### RLE Compression Types (Format 1 only)
36+
- **Compressed (MSB = 1)**: Next 4 bytes (RGBA) repeated N times
37+
- **Uncompressed (MSB = 0)**: Next N×4 bytes are individual RGBA pixels
38+
39+
### Format Auto-Detection
40+
The converter automatically detects the format by analyzing:
41+
- **File size**: Must be divisible by 4 for raw RGBA
42+
- **Dimensions**: Raw RGBA often forms perfect squares
43+
- **Alpha patterns**: Common alpha values (0, 255) indicate raw RGBA
44+
45+
## Tools Included
46+
47+
### 1. `ovg_to_png.py` - OVG to PNG Decoder
48+
Converts OVG files to PNG format with transparency preservation.
49+
50+
### 2. `png_to_ovg.py` - PNG to OVG Encoder
51+
Converts PNG files back to OVG format with RLE compression.
52+
53+
## Requirements
54+
55+
```bash
56+
pip3 install --user Pillow
57+
```
58+
59+
## Usage
60+
61+
### Decoding OVG to PNG
62+
63+
#### Basic Conversion
64+
```bash
65+
python3 ovg_to_png.py input.bin [output.png]
66+
```
67+
68+
Examples:
69+
```bash
70+
# Convert with auto-detected dimensions and format
71+
python3 ovg_to_png.py opt/gresfiles/img_off_clock_face_ovg.bin
72+
73+
# Convert with custom output name
74+
python3 ovg_to_png.py opt/gresfiles/img_off_clock_face_ovg.bin my_clock_face.png
75+
76+
# The converter will automatically detect if the file is RLE-compressed or raw RGBA
77+
# Example output:
78+
# Detected format: raw_rgba
79+
# Raw RGBA data contains 1521 pixels
80+
# Auto-detected dimensions: 39x39
81+
```
82+
83+
#### Specify Exact Dimensions
84+
```bash
85+
python3 ovg_to_png.py input.bin output.png --width 286 --height 286
86+
python3 ovg_to_png.py input.bin --width 200 # Height calculated automatically
87+
```
88+
89+
#### Interactive Size Discovery
90+
```bash
91+
# Use size discovery to find correct dimensions
92+
python3 ovg_to_png.py input.bin --discover
93+
94+
# Customize discovery range
95+
python3 ovg_to_png.py input.bin --discover --width-min 50 --width-max 300 --width-step 5
96+
```
97+
98+
The discovery mode will:
99+
- Generate test images at different sizes
100+
- Display each size and wait for your input
101+
- Let you jump to specific widths
102+
- Help you find the correct dimensions visually
103+
104+
#### Available Options
105+
- `-w, --width WIDTH` - Specify image width
106+
- `--height HEIGHT` - Specify image height
107+
- `-d, --discover` - Interactive size discovery mode
108+
- `--width-min MIN` - Minimum width for discovery (default: 35)
109+
- `--width-max MAX` - Maximum width for discovery (default: 400)
110+
- `--width-step STEP` - Width step for discovery (default: 1)
111+
112+
#### Usage Help
113+
```bash
114+
python3 ovg_to_png.py
115+
```
116+
117+
#### Output Files
118+
- **Clock Face**: 286×286 pixels (perfect square from 81,796 pixels)
119+
- **Clock Shadow**: 400×400 pixels
120+
- **Clock Spotlight**: 619×619 pixels
121+
- **Clock Hands**: 286×286 pixels each
122+
123+
### Encoding PNG to OVG
124+
125+
#### Convert Specific File
126+
```bash
127+
python3 png_to_ovg.py input.png [output.bin]
128+
```
129+
130+
Examples:
131+
```bash
132+
# Convert with custom output name
133+
python3 png_to_ovg.py my_custom_clock.png img_off_clock_face_ovg_new.bin
134+
135+
# Convert with auto-generated output name (replaces .png with .bin)
136+
python3 png_to_ovg.py clock_face_decoded.png
137+
# Creates: clock_face_decoded.bin
138+
```
139+
140+
#### Usage Help
141+
```bash
142+
python3 png_to_ovg.py
143+
```
144+
145+
Shows usage information and examples.
146+
147+
#### Test Roundtrip Conversion
148+
```bash
149+
# Test with default file
150+
python3 png_to_ovg.py test
151+
152+
# Test with specific file
153+
python3 png_to_ovg.py test opt/gresfiles/img_off_clock_face_ovg.bin
154+
```
155+
156+
This will:
157+
1. Decode the original OVG to PNG
158+
2. Encode the PNG back to OVG
159+
3. Compare file sizes and report compression efficiency
160+
4. Clean up temporary files automatically
161+
162+
## Complete Workflow for Clock Customization
163+
164+
### Step 1: Extract Original Images
165+
```bash
166+
# Extract specific files with custom names
167+
python3 ovg_to_png.py opt/gresfiles/img_off_clock_face_ovg.bin my_clock_face.png
168+
python3 ovg_to_png.py opt/gresfiles/img_off_clock_shadow_ovg.bin my_shadow.png
169+
python3 ovg_to_png.py opt/gresfiles/img_off_clock_spotlight_ovg.bin my_spotlight.png
170+
171+
# Or extract with auto-generated names
172+
python3 ovg_to_png.py opt/gresfiles/img_off_clock_face_ovg.bin
173+
# Creates: img_off_clock_face_ovg_decoded.png
174+
```
175+
176+
### Step 2: Edit Images
177+
- Open your extracted PNG files in your image editor
178+
- Modify the clock face design as desired
179+
- Edit other components (shadow, spotlight, hands) if needed
180+
- Save as PNG with transparency preserved
181+
182+
### Step 3: Convert Back to OVG
183+
```bash
184+
# Convert specific files with custom output names
185+
python3 png_to_ovg.py my_clock_face.png img_off_clock_face_ovg_new.bin
186+
python3 png_to_ovg.py my_shadow.png img_off_clock_shadow_ovg_new.bin
187+
188+
# Or convert with auto-generated names
189+
python3 png_to_ovg.py img_off_clock_face_ovg_decoded.png
190+
# Creates: img_off_clock_face_ovg_decoded.bin
191+
```
192+
193+
### Step 4: Replace in Firmware
194+
- Backup original files first!
195+
- Replace original `.bin` files with corresponding `*_new.bin` files
196+
- Update firmware with modified files
197+
198+
## Key Files
199+
200+
### Input Files (Original OVG)
201+
- `opt/gresfiles/img_off_clock_face_ovg.bin` - Main clock face
202+
- `opt/gresfiles/img_off_clock_shadow_ovg.bin` - Clock shadow
203+
- `opt/gresfiles/img_off_clock_spotlight_ovg.bin` - Clock spotlight
204+
- `opt/gresfiles/img_off_clock_hour_XX_ovg.bin` - Hour hand positions
205+
- `opt/gresfiles/img_off_clock_minute_XX_ovg.bin` - Minute hand positions
206+
- `opt/gresfiles/img_off_clock_second_XX_ovg.bin` - Second hand positions
207+
208+
### Output Files (Editable PNG)
209+
- `clock_face_decoded.png` - Main clock face (286×286)
210+
- `img_off_clock_*_decoded.png` - Individual components
211+
212+
### Generated Files (New OVG)
213+
- `img_off_clock_face_ovg_new.bin` - Modified clock face
214+
- `img_off_clock_*_ovg_new.bin` - Modified components
215+
216+
## Technical Details
217+
218+
### Compression Performance
219+
Typical compression ratios achieved:
220+
- **Clock Face**: ~2.8:1 compression
221+
- **Clock Shadow**: ~10.8:1 compression
222+
- **Clock Hands**: ~33-41:1 compression
223+
- **Spotlight**: ~11.3:1 compression
224+
225+
### Image Specifications
226+
- **Format**: RGBA (32-bit with alpha channel)
227+
- **Typical Size**: 286×286 pixels for main components
228+
- **Color Depth**: 8 bits per channel (R, G, B, A)
229+
- **Byte Order**: Standard RGBA pixel ordering
230+
231+
## Troubleshooting
232+
233+
### PIL/Pillow Not Found
234+
```bash
235+
pip3 install --user Pillow
236+
```
237+
238+
### File Not Found Errors
239+
Ensure the `opt/gresfiles/` directory exists with original OVG files.
240+
241+
### Size Mismatch
242+
The converter automatically calculates optimal dimensions and detects file format. If dimensions appear incorrect:
243+
244+
1. **Check format detection**: The converter will show "Detected format: raw_rgba" or "Detected format: rle_ovg"
245+
2. **Raw RGBA files**: Should auto-detect to perfect or near-perfect squares
246+
3. **RLE OVG files**: May require manual dimension specification with `--width` and `--height`
247+
4. **Use discovery mode**: `--discover` to find correct dimensions visually
248+
249+
### Compression Issues
250+
If the generated OVG file is significantly larger than the original:
251+
1. Check for unnecessary transparency in your PNG
252+
2. Ensure solid color areas are truly solid (no noise/gradients)
253+
3. The RLE compression works best with areas of repeated pixels
254+
255+
## Development Notes
256+
257+
### Discovery Process
258+
1. Initial attempts tried standard image formats (BMP, RGB, etc.)
259+
2. Analyzed byte patterns showing 0xFF padding and RLE-like structures
260+
3. Found reference to NXP AN4339 PDF describing similar RLE format
261+
4. Reverse-engineered the exact command block structure
262+
5. Implemented both decoder and encoder with roundtrip testing
263+
264+
### Format Insights
265+
- Files start with 0xFF padding that should be skipped
266+
- Command blocks use 7-bit pixel counts (1-128 pixels per command)
267+
- RLE compression is very effective for clock graphics with large solid areas
268+
- Alpha channel is preserved and properly handled
269+
270+
## References
271+
272+
- [NXP AN4339 Application Note](https://www.nxp.com.cn/docs/en/application-note/AN4339.pdf) - Describes similar RLE routine
273+
- [Reverse Engineering Stack Exchange](https://reverseengineering.stackexchange.com/questions/27688/open-unknown-image-format-probably-a-raw-image) - Initial format identification
274+
275+
## License
276+
277+
This project is provided as-is for educational and personal use. Always backup original firmware before making modifications.

0 commit comments

Comments
 (0)