Automatically extracts boot.img from downloaded Samsung firmware files
Size
11.2 KB
Version
1.0.1
Created
Jan 27, 2026
Updated
7 days ago
1// ==UserScript==
2// @name Samsung Firmware Boot.img Extractor
3// @description Automatically extracts boot.img from downloaded Samsung firmware files
4// @version 1.0.1
5// @match https://*.samfw.com/*
6// @icon https://samfw.com/assets/img/favicon.ico
7// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
8// @grant GM.xmlhttpRequest
9// ==/UserScript==
10(function() {
11 'use strict';
12
13 console.log('Samsung Firmware Boot.img Extractor loaded');
14
15 // UI Elements
16 let extractionPanel = null;
17 let statusText = null;
18 let progressBar = null;
19
20 // Create UI for extraction status
21 function createExtractionUI() {
22 if (extractionPanel) return;
23
24 extractionPanel = document.createElement('div');
25 extractionPanel.id = 'boot-extractor-panel';
26 extractionPanel.style.cssText = `
27 position: fixed;
28 top: 20px;
29 right: 20px;
30 width: 350px;
31 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
32 border-radius: 12px;
33 padding: 20px;
34 box-shadow: 0 10px 40px rgba(0,0,0,0.3);
35 z-index: 999999;
36 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
37 color: white;
38 display: none;
39 `;
40
41 extractionPanel.innerHTML = `
42 <div style="display: flex; align-items: center; margin-bottom: 15px;">
43 <div style="font-size: 24px; margin-right: 10px;">🔧</div>
44 <div style="flex: 1;">
45 <div style="font-weight: bold; font-size: 16px;">Boot.img Extractor</div>
46 <div id="extractor-status" style="font-size: 13px; opacity: 0.9; margin-top: 3px;">Ready</div>
47 </div>
48 </div>
49 <div style="background: rgba(255,255,255,0.2); border-radius: 8px; height: 8px; overflow: hidden; margin-bottom: 10px;">
50 <div id="extractor-progress" style="background: white; height: 100%; width: 0%; transition: width 0.3s;"></div>
51 </div>
52 <button id="extractor-close" style="
53 position: absolute;
54 top: 10px;
55 right: 10px;
56 background: rgba(255,255,255,0.2);
57 border: none;
58 color: white;
59 width: 24px;
60 height: 24px;
61 border-radius: 50%;
62 cursor: pointer;
63 font-size: 16px;
64 line-height: 1;
65 ">×</button>
66 `;
67
68 document.body.appendChild(extractionPanel);
69
70 statusText = document.getElementById('extractor-status');
71 progressBar = document.getElementById('extractor-progress');
72
73 document.getElementById('extractor-close').addEventListener('click', () => {
74 extractionPanel.style.display = 'none';
75 });
76
77 console.log('Extraction UI created');
78 }
79
80 // Show extraction panel
81 function showExtractionPanel() {
82 if (!extractionPanel) createExtractionUI();
83 extractionPanel.style.display = 'block';
84 }
85
86 // Update status
87 function updateStatus(message, progress = null) {
88 console.log('Extractor status:', message, progress);
89 if (statusText) statusText.textContent = message;
90 if (progressBar && progress !== null) {
91 progressBar.style.width = progress + '%';
92 }
93 }
94
95 // Extract boot.img from firmware file
96 async function extractBootImg(firmwareBlob, filename) {
97 try {
98 showExtractionPanel();
99 updateStatus('Loading firmware file...', 10);
100
101 console.log('Starting extraction from file:', filename, 'Size:', firmwareBlob.size);
102
103 // Load the ZIP file
104 const zip = await JSZip.loadAsync(firmwareBlob, {
105 createFolders: true
106 });
107
108 updateStatus('Searching for boot.img...', 30);
109 console.log('ZIP loaded, files:', Object.keys(zip.files));
110
111 // Look for boot.img in the archive
112 let bootImgFile = null;
113 let bootImgPath = null;
114
115 // Search for boot.img (case insensitive)
116 for (let path in zip.files) {
117 const file = zip.files[path];
118 const fileName = path.toLowerCase();
119
120 console.log('Checking file:', path);
121
122 if (fileName.includes('boot.img') && !file.dir) {
123 bootImgFile = file;
124 bootImgPath = path;
125 console.log('Found boot.img at:', path);
126 break;
127 }
128 }
129
130 // If not found directly, look inside nested archives (like .tar.md5)
131 if (!bootImgFile) {
132 updateStatus('Searching in nested archives...', 40);
133 console.log('boot.img not found in root, checking nested archives');
134
135 for (let path in zip.files) {
136 const file = zip.files[path];
137 const fileName = path.toLowerCase();
138
139 // Check for tar or tar.md5 files
140 if ((fileName.endsWith('.tar') || fileName.endsWith('.tar.md5')) && !file.dir) {
141 console.log('Found nested archive:', path);
142 updateStatus('Extracting nested archive...', 50);
143
144 try {
145 const tarBlob = await file.async('blob');
146 const tarZip = await JSZip.loadAsync(tarBlob);
147
148 console.log('Nested archive files:', Object.keys(tarZip.files));
149
150 for (let tarPath in tarZip.files) {
151 const tarFile = tarZip.files[tarPath];
152 if (tarPath.toLowerCase().includes('boot.img') && !tarFile.dir) {
153 bootImgFile = tarFile;
154 bootImgPath = tarPath;
155 console.log('Found boot.img in nested archive:', tarPath);
156 break;
157 }
158 }
159
160 if (bootImgFile) break;
161 } catch (e) {
162 console.log('Could not extract nested archive:', path, e);
163 }
164 }
165 }
166 }
167
168 if (!bootImgFile) {
169 updateStatus('❌ boot.img not found in firmware', 100);
170 console.error('boot.img not found in firmware archive');
171 setTimeout(() => {
172 extractionPanel.style.display = 'none';
173 }, 5000);
174 return;
175 }
176
177 updateStatus('Extracting boot.img...', 70);
178 console.log('Extracting boot.img from:', bootImgPath);
179
180 // Extract boot.img
181 const bootImgBlob = await bootImgFile.async('blob');
182
183 updateStatus('Preparing download...', 90);
184 console.log('boot.img extracted, size:', bootImgBlob.size);
185
186 // Create download link
187 const url = URL.createObjectURL(bootImgBlob);
188 const a = document.createElement('a');
189 a.href = url;
190 a.download = 'boot.img';
191 document.body.appendChild(a);
192 a.click();
193 document.body.removeChild(a);
194
195 // Clean up
196 setTimeout(() => URL.revokeObjectURL(url), 1000);
197
198 updateStatus('✅ boot.img downloaded successfully!', 100);
199 console.log('boot.img download initiated');
200
201 setTimeout(() => {
202 extractionPanel.style.display = 'none';
203 }, 5000);
204
205 } catch (error) {
206 console.error('Error extracting boot.img:', error);
207 updateStatus('❌ Extraction failed: ' + error.message, 100);
208 setTimeout(() => {
209 extractionPanel.style.display = 'none';
210 }, 5000);
211 }
212 }
213
214 // Add extraction button to download section
215 function addExtractionButton() {
216 const downloadSection = document.getElementById('download_section');
217 if (!downloadSection || document.getElementById('boot-extractor-button')) return;
218
219 console.log('Adding extraction button to page');
220
221 const buttonContainer = document.createElement('div');
222 buttonContainer.className = 'row my-2';
223 buttonContainer.innerHTML = `
224 <div class="col-lg-10 offset-lg-1 col-xl-6 offset-xl-3">
225 <button id="boot-extractor-button" class="btn btn-success btn-lg w-100" style="
226 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
227 border: none;
228 box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
229 ">
230 <i class="fas fa-magic"></i> Extract boot.img from Downloaded Firmware
231 </button>
232 <div class="small text-center mt-2" style="color: #666;">
233 Click after downloading firmware to extract boot.img
234 </div>
235 </div>
236 `;
237
238 const downloadArea = document.getElementById('download_area');
239 if (downloadArea) {
240 downloadArea.appendChild(buttonContainer);
241
242 document.getElementById('boot-extractor-button').addEventListener('click', async () => {
243 console.log('Extract button clicked');
244
245 // Create file input to select downloaded firmware
246 const fileInput = document.createElement('input');
247 fileInput.type = 'file';
248 fileInput.accept = '.zip,.tar,.tar.md5,.md5';
249 fileInput.style.display = 'none';
250
251 fileInput.addEventListener('change', async (e) => {
252 const file = e.target.files[0];
253 if (file) {
254 console.log('File selected:', file.name, file.size);
255 await extractBootImg(file, file.name);
256 }
257 document.body.removeChild(fileInput);
258 });
259
260 document.body.appendChild(fileInput);
261 fileInput.click();
262 });
263 }
264 }
265
266 // Initialize
267 function init() {
268 console.log('Initializing Boot.img Extractor');
269
270 // Wait for page to load
271 if (document.readyState === 'loading') {
272 document.addEventListener('DOMContentLoaded', init);
273 return;
274 }
275
276 // Add extraction button
277 addExtractionButton();
278
279 // Also try again after a delay in case page loads dynamically
280 setTimeout(addExtractionButton, 2000);
281
282 console.log('Boot.img Extractor initialized');
283 }
284
285 init();
286})();