no se si los demás pensaran lo mismo que yo, pero en lo personal siento que balena etcher en una herramienta digamos… inconsistente y no siento que funcione en todas las ocasiones. y viendo que tampoco encontré muchas alternativas realmente “fáciles de utilizar” y dije, a la porra, creare mi propia versión con juegos de azar y mujerzuelas… y así inicio este proyecto, que también lo hice para aprender a programar en java. No es la gran cosa, pero es trabajo honesto.
InterfazGrafica.java
// Librerías
import javax.swing.*;
// Sirve para componentes gráficos, es una biblioteca de GUI para Java¹
import java.awt.*;
// Sirve para organizar los componentes de las ventanas. Contiene gestores de diseño (layouts) y clases gráficas como Color, Dimension, etc.²
import java.awt.event.ActionEvent;
// Representa un evento generado por una acción del usuario (por ejemplo, al hacer clic en un botón).³
import java.awt.event.ActionListener;
// Interfaz que permite definir qué acción realizar cuando ocurre un ActionEvent (como un clic). Se usa para escuchar eventos.³
import java.io.File;
// Permite trabajar con archivos o directorios del sistema de archivos. Puedes verificar existencia, obtener rutas, etc.⁴
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
/*
¹ javax.swing.* Incluye lo siguiente:
JFrame, JTextField, JButton, JCheckBox, JLabel, JTextArea, JPasswordField.
¿Qué hace cada uno de ellos?
JFrame: Sirve para la ventana "principal". Se puede pensar como la casa dentro de la que añades los demás elementos.
JTextField: Es un campo de texto para que el usuario pueda escribir lo que quiera dentro de una línea.
JButton: Crea un botón pulsable al cual se le puede asignar un texto o una acción.
JCheckBox: Casilla para seleccionar o deseleccionar opciones booleanas (sí/no).
JLabel: Muestra un texto simple. No editable, se usa como descripción o etiqueta.
JTextArea: Similar a JTextField pero para escribir múltiples líneas de texto.
JPasswordField: Campo de texto que, a diferencia de JTextField, oculta los caracteres que se escriben (para contraseñas).
² java.awt.* Incluye lo siguiente:
BorderLayout, BoxLayout, FlowLayout, JPanel.
¿Qué hace cada uno?
BorderLayout: Divide la ventana en cinco zonas (Norte, Sur, Este, Oeste, Centro). Permite colocar componentes en zonas específicas.
BoxLayout: Organiza los componentes en una sola fila (EJE X) o columna (EJE Y). Ideal para diseño más vertical o lineal.
FlowLayout: Acomoda los componentes uno al lado del otro (como palabras en una línea). Cuando no cabe, baja a la siguiente línea.
JPanel: Contenedor que agrupa componentes. Se puede usar dentro de un JFrame. Puedes usarle layouts para organizar los elementos dentro de él.
³ java.awt.event.* incluye:
ActionEvent, ActionListener.
¿Qué hacen?
ActionEvent: Representa un evento generado por una acción (como un clic de botón).
ActionListener: Interfaz que define qué hacer cuando ocurre un evento. Se implementa para capturar clics u otras acciones del usuario.
⁴ java.io.File:
Sirve para representar y trabajar con archivos y carpetas del sistema operativo.
File: Permite obtener rutas, verificar si un archivo existe, abrir archivos, etc.
*/
// -------------------------------
// Declaración de la clase principal
// -------------------------------
/*
* En Java, todo programa se organiza en clases.
* La palabra clave "public" significa que esta clase es "pública", es decir, que puede ser utilizada desde otras partes del programa.
*
* "class" indica que estamos definiendo una clase.
*
* El nombre "InterfazGrafica" debe coincidir exactamente con el nombre del archivo .java (por ejemplo: InterfazGrafica.java).
* Esto es obligatorio en Java cuando una clase es pública.
*
* "extends JFrame" significa que esta clase hereda de la clase "JFrame" de Swing.
* Al heredar de JFrame, esta clase se convierte en una ventana gráfica (ventana principal).
* Esto permite usar directamente métodos como setTitle(), setLayout(), add(), etc.
*/
public class InterfazGrafica extends JFrame {
// -------------------------------
// Declaración de variables de la interfaz
// -------------------------------
/*
Estas variables son "atributos" de la clase InterfazGrafica.
Todas representan componentes visuales de la ventana (campos, botones, etc).
Se declaran como "private" para que solo puedan ser accedidas desde dentro de esta clase.
Cada componente se declara aquí para poder usarlo en varios métodos de la clase.
*/
private JTextField campoImagen;
// Campo de texto donde el usuario escribirá o verá la ruta de la imagen ISO seleccionada.
private JComboBox<String> listaDispositivos;
private JTextField campoBS;
// Campo de texto opcional para especificar el tamaño del bloque ("block size") al copiar, si se usa.
private JCheckBox opcionSync;
// Casilla para que el usuario elija si quiere usar la opción "sync" (sincronizar escritura).
private JCheckBox opcionFSync;
// Casilla para activar la opción "fsync", que asegura que los datos se escriban completamente en el dispositivo.
private JCheckBox opcionProgreso;
// Casilla para mostrar el progreso durante el proceso (si se ejecuta usando herramientas como dd o pv).
private JCheckBox mostrarDiscosSistema;
// Casilla para permitir ver los discos del sistema (usualmente se ocultan por seguridad).
private JCheckBox formatearUSB;
// Casilla para indicar si se desea formatear el dispositivo USB antes de copiar la imagen.
private JButton botonEjecutar;
// Botón que, al ser presionado, iniciará el proceso de copia de la imagen al USB.
private JTextArea areaSalida;
// Área de texto grande, usada para mostrar mensajes, resultados o errores del proceso.
private JPasswordField campoContraseña;
// Campo de texto especial que oculta los caracteres escritos, usado para ingresar contraseñas de forma segura.
//Este es el bloque que dicta como tiene que ser la interfaz grafica
public InterfazGrafica() {
setTitle("GUIDDO - Grabador de Imagenes USB");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JPanel panelPrincipal = new JPanel();
panelPrincipal.setLayout(new BoxLayout(panelPrincipal, BoxLayout.Y_AXIS));
// Seleccion de imagen ISO
JPanel panelImagen = new JPanel(new FlowLayout(FlowLayout.LEFT));
campoImagen = new JTextField(10);
JButton botonSeleccionarImagen = new JButton("Seleccionar Imagen");
botonSeleccionarImagen.addActionListener(e -> seleccionarArchivoISO());
panelImagen.add(new JLabel("Imagen ISO:"));
panelImagen.add(campoImagen);
panelImagen.add(botonSeleccionarImagen);
// Seleccion de dispositivo USB
JPanel panelUSB = new JPanel(new FlowLayout(FlowLayout.LEFT));
listaDispositivos = new JComboBox<>();
JButton botonActualizarDiscos = new JButton("Actualizar");
botonActualizarDiscos.addActionListener(e -> cargarDispositivos());
panelUSB.add(new JLabel("Dispositivo USB:"));
panelUSB.add(listaDispositivos);
panelUSB.add(botonActualizarDiscos);
cargarDispositivos();
// Opciones avanzadas
JPanel panelAvanzado = new JPanel();
panelAvanzado.setLayout(new BoxLayout(panelAvanzado, BoxLayout.Y_AXIS));
campoBS = new JTextField("4M", 5);
opcionSync = new JCheckBox("sync", true);
opcionFSync = new JCheckBox("fsync", false);
opcionProgreso = new JCheckBox("Mostrar progreso", true);
mostrarDiscosSistema = new JCheckBox("Mostrar discos del sistema", false);
formatearUSB = new JCheckBox("Formatear USB antes de grabar", false);
mostrarDiscosSistema.addActionListener(e -> cargarDispositivos());
JPanel opciones = new JPanel(new FlowLayout(FlowLayout.LEFT));
opciones.add(new JLabel("Tamano de bloque (bs):"));
opciones.add(campoBS);
opciones.add(opcionSync);
opciones.add(opcionFSync);
opciones.add(opcionProgreso);
opciones.add(formatearUSB);
opciones.add(mostrarDiscosSistema);
panelAvanzado.add(opciones);
// Contrasena sudo
JPanel panelContraseña = new JPanel(new FlowLayout(FlowLayout.LEFT));
campoContraseña = new JPasswordField(15);
panelContraseña.add(new JLabel("Contraseña sudo:"));
panelContraseña.add(campoContraseña);
// Boton ejecutar
botonEjecutar = new JButton("Crear USB Booteable");
botonEjecutar.addActionListener(e -> ejecutarProceso());
// Area de salida
areaSalida = new JTextArea(10, 50);
areaSalida.setEditable(false);
JScrollPane scroll = new JScrollPane(areaSalida);
// Agregar todo al panel principal
panelPrincipal.add(panelImagen);
panelPrincipal.add(panelUSB);
panelPrincipal.add(panelAvanzado);
panelPrincipal.add(panelContraseña);
panelPrincipal.add(botonEjecutar);
panelPrincipal.add(scroll);
add(panelPrincipal, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void seleccionarArchivoISO() {
JFileChooser selector = new JFileChooser();
selector.setDialogTitle("Selecciona la imagen ISO");
int resultado = selector.showOpenDialog(this);
if (resultado == JFileChooser.APPROVE_OPTION) {
File archivo = selector.getSelectedFile();
campoImagen.setText(archivo.getAbsolutePath());
}
}
private void cargarDispositivos() {
listaDispositivos.removeAllItems();
try {
ProcessBuilder builder = new ProcessBuilder("bash", "-c", "lsblk -ndo NAME,TYPE,SIZE | grep disk");
Process proceso = builder.start();
BufferedReader lector = new BufferedReader(new InputStreamReader(proceso.getInputStream()));
String linea;
while ((linea = lector.readLine()) != null) {
String[] partes = linea.trim().split("\\s+");
if (partes.length >= 1) {
String nombre = partes[0];
if (!mostrarDiscosSistema.isSelected() && nombre.equals("sda")) continue;
listaDispositivos.addItem("/dev/" + nombre);
}
}
} catch (Exception e) {
listaDispositivos.addItem("[Error al listar dispositivos]");
}
}
private void ejecutarProceso() {
String imagen = campoImagen.getText();
String usb = (String) listaDispositivos.getSelectedItem();
String bs = campoBS.getText();
boolean sync = opcionSync.isSelected();
boolean fsync = opcionFSync.isSelected();
boolean progreso = opcionProgreso.isSelected();
boolean formatear = formatearUSB.isSelected();
String contraseña = new String(campoContraseña.getPassword());
areaSalida.setText("[INFO] Ejecutando...\n");
Puente.ejecutarComando(imagen, usb, bs, sync, fsync, progreso, formatear, contraseña, new Puente.ResultadoListener() {
@Override
public void onOutput(String linea) {
SwingUtilities.invokeLater(() -> areaSalida.append(linea + "\n"));
}
@Override
public void onError(String error) {
SwingUtilities.invokeLater(() -> areaSalida.append("[ERROR] " + error + "\n"));
}
@Override
public void onFinalizado() {
SwingUtilities.invokeLater(() -> areaSalida.append("[INFO] Proceso finalizado.\n"));
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(InterfazGrafica::new);
}
}
Puente.java
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Puente {
public interface ResultadoListener {
void onOutput(String linea);
void onError(String error);
void onFinalizado();
}
public static void ejecutarComando(String imagen, String usb, String bs, boolean sync, boolean fsync, boolean progreso, boolean formatear, String contraseña, ResultadoListener listener) {
new Thread(() -> {
try {
List<String> comandos = new ArrayList<>();
if (formatear) {
String formateoProfundo = "echo '" + contraseña + "' | sudo -S bash -c \"" +
"wipefs -a " + usb + " && " +
"parted " + usb + " --script mklabel msdos mkpart primary fat32 1MiB 100% && " +
"mkfs.vfat -F 32 " + usb + "1" +
"\"";
comandos.add(formateoProfundo);
}
StringBuilder comandoDD = new StringBuilder();
comandoDD.append("echo ").append(contraseña).append(" | sudo -S dd if=").append(imagen).append(" of=").append(usb);
if (bs != null && !bs.isEmpty()) comandoDD.append(" bs=").append(bs);
if (sync) comandoDD.append(" conv=sync");
if (fsync) comandoDD.append(" && sync");
if (progreso) comandoDD.append(" status=progress");
comandos.add(comandoDD.toString());
for (String cmd : comandos) {
ejecutarLinea(cmd, listener);
}
listener.onFinalizado();
} catch (Exception e) {
listener.onError("Error al ejecutar el proceso: " + e.getMessage());
}
}).start();
}
private static void ejecutarLinea(String comando, ResultadoListener listener) throws IOException {
ProcessBuilder builder = new ProcessBuilder("bash", "-c", comando);
builder.redirectErrorStream(true);
Process proceso = builder.start();
try (BufferedReader lector = new BufferedReader(new InputStreamReader(proceso.getInputStream()))) {
String linea;
while ((linea = lector.readLine()) != null) {
listener.onOutput(linea);
}
}
}
}
Ya he provado mi propia herramienta y si, funciona, la primera distro que probe fue linux mint, porque le voy a instalar linux a el portatil de un familiar de un amigo. y bueno digamos que aquí lo mas libre, no es que funcione (el portatil es malisimo, tuve que borrar windows 11, poner windows 10, y optimizar todo lo que pude, le voy hacer un dual boot con linux mint xfce para que tenga un sistema operativo utilizable, lamentablemente windows lo necesita por ciertos programas, que capaz y corren en wine pero no me la iba a jugar)
quizas alguno se pregunte ¿por que el nombre de guiddo? es un juego de palabras con GUI, DD (la herramienta que utilizo para hacer la usb booteable) y en castellano la palabra guido significa bueno en su genero… que igual me estoy echando muchas flores a mi mismo diciendo que esto es bueno, pero lo mas elegi el nombre porque me permitia hacer referencia a DD. ah, y ademas que tambien me recuerda a guizo, por lo cual es el mejor programa del mundo.
Pienso hacerlo un programa libre, por eso lo publico en este lugar… eso si, no esperen que lo publique en algo como github o codeberg o donde sea proximamente. porque primero, alta paja, y porque segundo primero deberia terminar mi proyecto del instituto en fin, espero que esto les sea util. luego me dicen que les parecio, pasenme sugerencias, recomendaciones.
y por favor alguien me diga como soltar el celular, necesito ayuda.
ah y para compilarlo utilizo este comando por si no sabian:
javac -encoding UTF-8 InterfazGrafica.java Puente.java
y para ejecutarlo de momento es asi
java InterfazGrafica
evidentemente para ejecutarlo necesitan tener java instalado en su sistema y tambien dd, parted, wipefs y mkfs. Realmente lo digo por las puras esto ya que de normal esto ya deberia estar instalado por defecto. pero no se, lo digo por si acaso alguien instalo linux por lfs, o que se yo.