tag:blogger.com,1999:blog-48430320681988562702024-02-08T19:49:47.472+01:00random_hopefully_useful_stuffUnknownnoreply@blogger.comBlogger15125tag:blogger.com,1999:blog-4843032068198856270.post-39967142923019078612014-02-07T19:59:00.003+01:002014-02-07T19:59:41.481+01:00Solving "grub-probe: error: cannot find a device for / (is /dev mounted?)"A few days ago I encountered this error, which caused me a bit of head scratching.
<p/>
There are a few causes for this; if you have your O/S installed on Btrfs, then the problem is most likely that you're mounting the entire partition (which is correct in case of other FSs, but not Btrfs) instead of the subvolume for the root.
<p/>
The correct mount option is:
<p/>
<pre>
$ sudo mount -o subvol=@ <dev> <mountpoint>
</pre>
So if for example you want to recover grub, using chroot from a live cd, a sample sequence is:
<pre>
$ sudo mount -o subvol=@ /dev/sda1 /mnt
$ sudo mount --bind /dev /mnt/dev
$ sudo mount --bind /proc /mnt/proc
$ sudo mount --bind /sys /mnt/sys
$ sudo chroot /mnt
</pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-69810249910906706612014-01-25T15:49:00.001+01:002014-01-25T15:49:33.289+01:00Sinclair A-Bike review (don't buy - it's broken and unusable)I'm writing this review to inform potential a-bike clients to warn them about what they're going to get into.
<p/>
TL;DR: A-Bike may seem an acceptable bike for people doing very small trips. This is not the case; due to a defective design, after some usage, even for very small trips, the bike will break. The problem is that the company ignores clients, so you will end up with an unusable bike.
<p/>
I've had several folding bikes in the past, and I used them with different patterns. Currently I live in a big city with an extended metro system, and almost no hills.
I currently use the bike to do the "last mile", and to go to shops close to my building.
<p/>
For the first few months, the abike is adequate for this purpose. The upsides are:
<ul>
<li>it's super light</li>
<li>it folds quite easily</li>
<li>it's not such a big deal to have such small wheels, if one cycles on cycleways</li>
</ul>
The downsides are:
<ul>
<li>pedaling is hard (very inefficient)</li>
<li>the brakes work, but they're not very good</li>
</ul>
After a few months though, the chain started to skip, more and more, until the bike became completely unusable.
<p/>
Bear in mind that I used the bike for short distances, less than 2 km every day, in flat roads.
<p/>
I've done some research, and I've found that this is due to the fact that the inner gears are put under heavy strain, so they slowly wear down.
<p/>
Bringing them to a bike shop is useless, since the parts are custom.
So I wrote the support. They ignored the email.
After a couple of weeks, I call. The woman on support says to open the bike, and send them a picture.
I have a look at the bike, but the operation requires lots of screws to be unscrewed, so I send them a video instead. The ignore the email again.
So I call again, and the woman says some circumstantial lie, then says a technical will call me shortly. Nobody calls.
<p/>
So now I have a piece of crap that the company refuses to fix, and that shops can't fix, either.
<p/>
So, to recap: if you want to buy an A-Bike thinking you can do small trips (I've seen other people with it), expect it to break quite quickly, and to have a unusuable piece of crap that the company refuses to repair.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-43277642373747154402014-01-25T15:45:00.000+01:002014-01-25T15:46:42.351+01:00Fortuna Studio Downtown (Zurich) - WATCH OUT, SCAMMERS!So, this is a horror story about Fortuna Studio Downtown (Zurich, Switzerland).
I apologize for the title of this post, but it's important for client to understand who they're dealing with.
<p/>
TL;DR: <b>They owners evicted us before the checkout date, without any reason, and without notice</b>, while we weren't in the room, because of an error in their schedule, <b>even if I told them in advance that they had an error</b> (they ignored me). They didn't help at all, and refunded the expenses for a night at another hotel, after a month of complaints. <b>They said plenty of lies to cover their continuous negligence</b>.
<p/>
--
<p/>
We (2 people) booked the studio-apartment for a few days (Thursday, checkout on Monday), via booking.com; I had a sports competition, and I wanted a very simple room.
<p/>
First of all, the <b>advertisement on the booking website is false - it says that there is air conditioning in the room, while there isn't</b>. Good luck when the temperature goes over 30°.
<p/>
On Friday, I call them asking if I could do a late check out on Monday. They say that would be ok, but that the checkout is on Sunday. I say that they're wrong, but they reply that they have it in their schedule. I explain that I have the booking confirmation in front of me, and it says checkout out on Monday. They say that it's ok and they will check.
<p/>
During the Sunday I do my competition, early morning to late evening, and come back after midnight. I find everything outside the apartment, and the lock changed. I turn on my mobile, and find that they wrote me several messages, during the day, warning me that they were about to evict me.
<p/>
I call back and tell them that I've told them already that my booking was until Monday, and I ask them why they didn't listen. They say the would call back in 5 minutes.
They call me and they say that they've done a mistake.
<p/>
They don't come to fix the problem and pay, but they say that they will pay for a taxi and a room in another hotel.
<p/>
So we carry everything down from the third floor, including a 15 kg 130x95x40 cm box (no elevator, and very narrow staircases), we take a cab, and we move to the other hotel.
<p/>
All of this took more than a couple of hours, and we were exhausted - I just had a 16 hours-long competition.
<p/>
After a week, they still didn't refund us. I call, and they lie, saying they would have completed the payment shortly.
<p/>
After a couple of weeks, they lie again with the same story.
<p/>
After a month, I ask their legal address, so I can initiate a lawsuit. They say a circumstantial lie, saying there has been a misunderstanding (again!), and that they would have refunded us shortly.
<p/>
Finally, the payment comes, with a miserable additional refund of around 20€.
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-64676855127350168522013-02-13T19:09:00.000+01:002013-02-13T19:11:12.106+01:00How to stop processes when a VPN connection drops (on Ubuntu)A very common concern among VPN users is that processes will switch to the insecure connection when the VPN drops.
<p/>
Before talking about the solution(s), be aware that VPN connections do drop and will drop - so this problem needs to be tackled, independently of the provider.
<p/>
I personally endorse the <a href="http://www.privateinternetaccess.com">privateinternetaccess</a> provider. Of the three I've tested (the others being BTGuard and TorrentPrivacy), it's been the only one with a working customer support. Other providers just don't have it, or don't reply to the support requests.<br/>
Moreover, the company releases an application, on Windows machines, which automatically stops the internet connection when the VPN connection drops.
<p/>
On a general basis, it's possible to write some scripts on Ubuntu, which solve the problem.
<p/>
The concept is very simple. In Ubuntu, we can write callbacks which are executed when a given connection drops. At the same time, we can use a background script which notices this and acts accordingly. We're using this structure (callback/listener) because there is a non-trivial problem to solve otherwise, caused by the fact that the root user doesn't have direct access to the VPN password stored by the user.
<p/>
First script to add (give executable permissions): /etc/NetworkManager/dispatcher.d/90vpnhandletransmission
<pre>#!/bin/bash
SIGNAL_FILE=/tmp/start_vpn_operations.tmp
APP_NAME=firefox # app to start/stop
DESKTOP_USER=username # name of the user!
if [ "$1" == "tun0" -a "$2" == 'vpn-down' ]
then
logger -s "NM Script vpn-down triggered"
pkill -f $APP_NAME
sudo -u $DESKTOP_USER touch "$SIGNAL_FILE"
fi</pre>
The second script to add may reside anywhere. It's a ruby script, so one needs a ruby interpreter (tested on 1.9). Just save it and give executable permissions.
There are two ways of operating:<ul>
<li>Execute the script without options, and leave it running; manually start the VPN and launch the application</li>
<li>Execute the script with the '-f' option; it will automatically start the VPN and launch the application</li>
</ul>
<pre>#!/usr/bin/env ruby
require 'fileutils'
SIGNAL_FILE = '/tmp/start_vpn_operations.tmp'
VPN_NAME = 'Privateinternetaccess' # this is the name of the VPN connection as created by the user
APP_NAME = 'firefox'
case ARGV[ 0 ]
when '-f'
FileUtils.touch( SIGNAL_FILE )
when nil
else
puts "Usage: #{ $0 } [-f]"
puts " [f]orce start"
exit
end
while true
if File.exists?( SIGNAL_FILE )
vpn_connection_started = system( "nmcli con up id '#{ VPN_NAME }'" )
exit if ! vpn_connection_started
FileUtils.rm_f SIGNAL_FILE
fork {
`#{ APP_NAME } > /dev/null 2>&1`
}
else
sleep 1
end
end</pre>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-58354092137927514092011-11-12T14:53:00.006+01:002011-11-12T15:03:15.860+01:00Jungledisk review: a rambling piece of junkSo, I've definitively switched over from Jungledisk to SpiderOak - I can't say the latter is a well-written backup application, but at least it works.<br /><br />In order to justify the definition of "Rambling piece of junk", here is the overview both of the problems encountered and the lack of professionalism in the development.<br /><br />Application problems:<br />- The dialog for choosing what to backup is broken for Natty. In addition to being broken, once it's opened, it will make the JD interface unusable until the next restart.<br />- Syncing of files with accented characters is broken.<br />- Syncing of directories with accented characters is broken.<br />- Because of a bug in the syncing, under some, not rare, conditions, JD will delete files in the sync folders.<br />- Big files can't be shared, because connection errors will cause the upload to fail. Note that this function works perfectly in Dropbox.<br />- Internet connection detection doesn't work well. Sometimes JD doesn't detect it (even if the connection works perfectly), so that the program can't be used (for a short time).<br />- Sometimes JD doesn't connect to the service, so that the program can't be used.<br />- I can't copy a specific file (video, ~600M) to the remote folder.<br /><br />Development:<br />- Until version 3.16, the option 'Backup as soon as possible when backup time is missed' didn't work; this is a core option for backup applications.<br />- In the timespan between the releases 3.14 and 3.16, which lasted several months, the only things done have been:<br /> - introducing a release with a very serious issue (3.15), which has been been retired after a short time<br /> - "fixing 5 bugs" (none of the above, which still stand) and "addressing two issues".<br />- At the date of today (November 2011), it's still 6 months that no updates have been released after version 3.16.<br /><br />You can judge by yourself.Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-4843032068198856270.post-56420352960720774512011-04-25T09:17:00.005+02:002011-04-25T09:37:07.864+02:00Setting up Voipstunt (and similar) on LinuxI've been spending some time trying all the softphone solutions I could find around, in order to use Voipstunt on Ubuntu.<br /><br />Most of them were either:<br />- not working, properly or at all<br />- had a horrible interface<br /><br />Ekiga is in the first category; I couldn't make more than one phone call per application run.<br />Linphone is in both categories.<br />I tried others that I don't remember.<br /><br />At the end I found two working ones:<br />- Qutecom<br />- Twinkle.<br /><br />Twinkle has a clunky and outdated interface, with at least one serious bug (segfault when trying to delete one contact). It's simple to configure, though.<br /><br />Qutecom has a modern interface, and it works reasonably well.<br />I again found a bug, but minor - if I try to close the application without hanging up a call, it hangs.<br /><br />At the end, I chose Qutecom.<br /><br />For people using Qutecom and Voipstunt, the only parameter to configure (aside the obvious login/password) is "sip.voipstunt.com" as SIP Domain/Realm.<br />Voipbuster, from the same phone company, uses most probably "sip.voipbuster.com".<br /><br />The package is in the main Ubuntu repository, so users can install it using the GUI application or command line.<br />login: <login>Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-4843032068198856270.post-55350294139885680812011-03-28T12:47:00.004+02:002011-11-12T15:03:01.759+01:00Jungledisk deletes your dataYes, the title is correct. There is a bug in the Jungledisk sync which, in not uncommon conditions, will delete your synchronised data across all the computers.<br /><br />Specifically, if a machine is downloading the data of a synced folder, and you shut down the machine, on the following startup, JD will think that the data not downloaded has been deleted, and proceed to delete it on the servers, which in cascade, will delete it also on your other synced machines.<br /><br />This of course make the Jungledisk sync unusable - I'm not going to trust code which deletes my data.<br /><br />You can still go through past backup, but that's very uncomfortable.<br /><br />In addition to that, JD sync is remarkably poorly coded:<br />- it has a very long lag, taking long time before detecting changes.<br /> a real use cases is the one where you made a modification to your data, then turn your computer off. while competitors start the sync upload immediately (e.g. DropBox), JD won't, so either you wait minutes doing nothing in front of the computer, or you can forget your syncing. what also happens, is that you may forget to wait, and change the data on another client - you will have to solve the conflicts when you turn your first machine on.<br />- it has two separate bugs related to non-ASCII characters. one bug is on the folders, and another one on the files; both cases will cause your sync on the folders/files to fail.<br /><br />All in all, JD's syncing has always been very poor, while being an important part of the application.<br /><br />Files deletion support request: http://support.jungledisk.com/requests/55573.<br />Folder syncing support request: http://support.jungledisk.com/requests/50416<br /><br />At this point I'm really annoyed by filing bugs, so I didn't file the bug for non-ASCII files.<br /><br />I'm trying SpiderOak now, and if it works well, I won't hesitate a moment before swithing.Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-4843032068198856270.post-62998361576091691802010-12-28T18:35:00.005+01:002011-11-12T15:02:14.136+01:00Short review: Dell Inspiron DuoI got the laptop a few days ago. Overall, it's a mixed experience.<br /><br /><span style="font-weight:bold;">Ubuntu compatibility</span><br /><br />The touch screen has problems with a clean install of Ubuntu NE 10.10. Some (private) people is working on it - informations here.<br />Also the function keys have a very annoying problem.<br /><br /><span style="font-weight:bold;">Screen</span><br /><br />Disappointing. Although it's "usable" (keeping in mind that as long as one sees something on a screen, it is "usable" by definition), the vertical range is very limited. This prevents the screen to be used in portrait mode, because the right part is darker than the left one, unless you keep the left farther than the right when you read.<br />10 inches are also a bit narrow, although this is a subjective opinion.<br /><br />As a matter of fact, when I use it to read on the bed, I use it in landscape mode.<br /><br /><span style="font-weight:bold;">CPU</span><br /><br />The N550 Atom platform is not slow itself. On a clean Windows 7 I felt it as responsive and didn't experience lag during regular desktop usage.<br /><br />The Ubuntu NE 10.10 [Desktop interface] as system instead is slow and unresponsive, although usable.<br />Always speaking about Ubuntu, the applications theirselves are fast enough - for example I tried some 480p videos in youtube and they were smooth; same thing for common applications like OpenOffice. Firefox is acceptably fast using a few tabs, but no more than around 4.<br /><br />So it all boils down to the O/S: I would recommend it for Windows users, but absolutely not for linux ones.<br />Some reviews around describe the machine as slow on Windows 7; keep in mind that they always refer to the pre-installed Windows 7, which I believe is full of junk and, certainly, has a terribly programmed custom interface.<br /><br /><span style="font-weight:bold;">Construction</span><br /><br />The construction is cheap.<br />The fan is noisy.<br />The lid around the screen is a bit too thick.<br />If you're used to SSD disks, you will feel the disk rotation.<br /><br />The weight is reasonable for a hybrid laptop (~1.5 kg): light if compared to full tablet pcs, heavy if compared to tablets. <br />Objectively speaking, the laptop is a bit heavy for "demanding tablet usages" (ie. bed reading), but I got used to. <br /><br />I feel that the lid however is robust enough not to break with the usage.<br />The keyboard is pleasant.<br /><br /><span style="font-weight:bold;">Overall</span><br /><br />The laptop makes sense for what is being sold, that is, a cheap tablet with the premium price of an innovation.<br /><br />I just noticed that Asus is going to produce the Eee Pad EP121, which according to this page, compared to the Inspiron weighs around 70% of it, it has a bigger screen, a more powerful CPU, and an SSD disk. Essentially, price aside, it wins hands off.<br /><br />I specifically purchased it because I needed a "powerful" tablet without spending too much. Wrapping up, I got what I wanted - it may satisfy your use cases as well.<br />If the Asus Eee Pad EP121 would have been out though, I would have chosen the latter.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-76254948071994438352010-12-28T18:16:00.006+01:002011-11-12T15:02:39.653+01:00Review: Simplenote for IPhone[post currently removed]Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4843032068198856270.post-77434889275762264162009-04-05T22:44:00.008+02:002009-04-05T23:03:26.692+02:00Compiling DosBox (in debug mode) on Ubuntu JauntyDosBox, when compiled in heavy debug mode, can be useful to reverse engineer old dos applications, like simple games or viruses (of course, the old dos ones don't replicate on modern windows systems, so there's no harm for anybody).<br /><br />Compiling DB 0.72 on the current Ubuntu Jaunty release (Beta) causes some errors, first of which is:<br /><pre><br /> gameblaster.cpp => error: ‘memset’ was not declared in this scope<br /></pre><br />All of them (three in total) can be fixed adding some appropriate includes at the top of the following files:<br /><pre><br /> src/hardware/gameblaster.cpp => #include <string.h><br /> src/hardware/tandy_sound.cpp => #include <string.h><br /> src/shell/shell_cmds.cpp => #include <cstdlib><br /></pre><br />and this will fix the build. To enable the debug mode (which enables the debugger) now you can compile using:<br /><pre><br /> ./configure --enable-debug=heavy<br /> ./make<br /></pre><br />This solution was found in this page:<br /><br /> <a href="http://usernametakenistaken.wordpress.com/2008/08/23/howto-compiling-dosbox-with-on-suse-110/">Howto: Compiling DosBox with Suse 11.0</a><br /><br />so he's the person to give credits to!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-16773775169837646782009-02-01T16:04:00.002+01:002012-03-09T23:51:03.674+01:00IPhone: Custom ringphonesI wanted to set an excerpt from a DVD as my IPhone ringphone, so I did a bit of research and fighted against the usual IPhone jail-mindset.<br />There are bits and pieces of information required to transfer an audio file to an iphone as a ringtone, so hopefully this will save someone's time.<br /><br />The system used is Windows XP and iTunes 8.0.2; this tutorial is meant for average/advanced computer users.<br /><br />Summary of requirements for a song to be generally set as a ringphone:<br /><br />* it must be in AAC format, LESS than 128000 kbits/sec (127999 works...) and LESS than 30 seconds (didn't try the exact limit, though)<br />* the extension must be ".m4r"<br /><br />Walkthrough for what I've done:<br /><br />1. identified the chapter in the DVD<br />2. used <a href="http://www.mpegx.com/view.php?detail=323">SmartRipper</a>, specifying the chapter, and under stream processing, to extract only the audio, in separate files<br />3. converted the file from .m4a to wave using Winamp (probably this step can be skipped)<br />4. opened the file using Audacity and removed the unneeded parts<br />5. converted to aac 127 kbits/sec using <a href="http://www.nero.com/eng/downloads-nerodigital-nero-aac-codec.php">Nero AAC Codec</a><br />6. copy/paste the resulting file from the folder to the ringphones section in itunes<br /><br />Additional notes:<br /><br />* If the source is not a DVD, go directly to step 3<br />* If the source is an AAC file and you're a Mac user, you can use <a href="http://rogueamoeba.com/fission/download.php">Fission</a> for the lossless editing, that is, to avoid decoding and reencoding; watch out! it's not a free tool.<br />* When using Audacity, if you want to play with the volume of the track, you can use "apmlify"; an appropriate max amplification has about -4 decibel as peakUnknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-69614257894423193652009-01-31T20:37:00.006+01:002009-03-20T20:12:26.263+01:00Programming: Programming in assembler for Commodore 64I've recently developed an interest in programming in assembler for the C64.<br /><br />I've always been fascinated by programming in low level (I once programmed some tools in ASM for 80x86), and the C64 can be interesting, aside because it's been my first machine :-), also because it's relatively simple in the architecture and thus in its applications - of course, there is lots of trickery required for producing games. There are also many books on the subject.<br /><br />I'm about to invent some kind of project for studying and applying the knowledge; in the meanwhile, I'll share the "starter kit" for whoever should have a similar, unusual, interest:<br /><br /><b>Didactic materials:</b><br /><ul><br /> <li><a href="http://project64.c64.org/misc/index.html">Machine language for Commodore 64 (J.Butterfield) [TXT, but you can buy it on Amazon]</a></li><br /> <li><a href="http://codebase64.org/">Codebase 64 [web]</a></li><br /></ul><br /><b>Tools:</b><br /><ul><br /> <li><a href="http://www.lemon64.com/apps/list.php?Genre=carts">The Final Cartridge 3</a>: A cartridge including a good machine language monitor.</li><br /> <li><a href="http://viceteam.org/#download">VICE</a>: faithful C64 emulator.</li><br /></ul><br />Note: In the ubuntu VICE distribution, the default positional keyboard mapping doesn't work properly on GB keyboards. Until now, I spotted two keys problematic keys, hash and double quotes; to fix them, edit (/usr/lib/vice/c64/)x11_pos.vkm and replace the old entries with the following ones:<br /><pre><br /> quotedbl 7 3 8<br /> sterling 1 0 8<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-89732652100417264152009-01-25T13:34:00.004+01:002009-01-25T13:43:10.528+01:00Development: Pomodoro techniqueIn spite of the very curious name, the Pomodoro technique is a very intresting and effective time management technique.<br />It's based on the use of a kitchen timer to mark 25-minutes intervals of study/work, with pauses between section; for this it can be useful to students or workers, where concentration is the core activity.<br /><br />The core/theory of the technique is on the fact that a very productive approach to study/work is to strive to reach 25-minutes intervals of full concentration without internal or external interruptions, with the aid of simple technique to relegate the interruptions in separate time slices.<br /><br />I found it to be really, really productive, especially because it automates/routinize (if accepted and done properly) the 'focused' state of mind, which can be more or less easy to break or destructure.<br /><br />Unfortunately I found it to be really hard to put in practice because of the external interruptions, but I'm trying to modify the model to accomodate the inevitable. Really inevitable?<br /><br />Essay: <a href="http://www.tecnicadelpomodoro.it/docs/francesco-cirillo/2007/ThePomodoroTechnique_v1-3.pdf">PDF format</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-35461748257688194112008-12-21T16:59:00.008+01:002008-12-22T01:37:08.800+01:00Programming: MAME roms cleanerA ruby script for cleaning up a collection of MAME roms.<br />It essentially keeps the subset of roms necessary to play the games, and deletes the clones or non working games; it also separates some systems to ease management.<br />This may sound an offense to anally retentive people, which use to keep the roms for the sake of keeping them, or thinking that maybe in a thousand year a nonworking rom could be emulated correctly.<br /><br />Notes:<br />* it's rule based, so one can actually write its own rules<br />* it only works in linux<br />* it caches (Marshal class) the data during the first run, as parsing the xml is darn slow<br />* it's written in ruby, so it requires the ruby interprer, plus the 'hpricot' gem<br />* it's redundant, for clarity<br /><br /><pre><br />require 'rubygems'<br />require 'hpricot'<br />require 'fileutils'<br /><br />################################################################################<br /># CLASS DEFINITIONS<br />################################################################################<br /><br />class Game<br /> PRELIMINARY = 'preliminary'<br /># GOOD = 'good'<br /># IMPERFECT = 'imperfect'<br /><br /> attr_reader :name, :sourcefile, :cloneof, :romof, :sampleof<br /> attr_reader :description<br /> attr_reader :status, :emulation, :color, :sound, :graphic<br /> attr_accessor :parent, :sampleparent, :clones # clones not actually assigned, just modified<br /><br /> def year<br /> (parent.year if parent) || @year || (sampleparent.year if sampleparent)<br /> end<br /><br /> def has_chd?<br /> @has_chd<br /> end<br /><br /> def initialize(name, sourcefile, cloneof, romof, sampleof, description, year, status, emulation, color, sound, graphic, has_chd)<br /> raise sprintf("%-8s: CO[%-8s] RO [%-8s]", name, cloneof, romof) if cloneof && (cloneof != romof)<br /><br /> @name, @sourcefile, @cloneof, @romof, @sampleof = name, sourcefile, cloneof, romof, sampleof<br /> @description, @year = description, year<br /> @status, @emulation, @color, @sound, @graphic = status, emulation, color, sound, graphic<br /> @has_chd = has_chd<br /> @parent, @sampleparent, @clones = nil, nil, []<br /> end<br /><br /> def working?<br /> @emulation != PRELIMINARY<br /> end<br /><br /> def non_working?<br /> ! working?<br /> end<br /><br /> def parent?<br /> @cloneof.nil?<br /> end<br /><br /> def clone?<br /> ! parent?<br /> end<br /><br /> def self.decode(game_node)<br /> driver_node = (game_node/'driver').first<br /> description_node = (game_node/'description').first<br /> year_node = (game_node/'year').first<br /> has_chd = ((game_node/'disk')).size > 0<br /><br /> Game.new(game_node['name'], game_node['sourcefile'], game_node['cloneof'], game_node['romof'], game_node['sampleof'],<br /> description_node.inner_html, (year_node.inner_html if year_node),<br /> driver_node['status'], driver_node['emulation'], driver_node['color'], driver_node['sound'], driver_node['graphic'],<br /> has_chd)<br /> end<br />end<br /><br />################################################################################<br /># ROUTINES<br />################################################################################<br /><br />def create_dirs(roms_dirs)<br /> puts '... creating dirs'<br /> roms_dirs.each { |sym, dir| FileUtils.mkdir_p(dir)}<br />end<br /><br />def load_games(mame_executable, cache_file)<br /> if File.exists?(cache_file)<br /> puts '... loading cached games'<br /> open(cache_file, 'r') { |file| Marshal.load(file) }<br /> else<br /> puts '... piping mame xml games list into hpricot'<br /> list_xml = IO.popen("#{mame_executable} -listxml") { |pipe| Hpricot::XML(pipe) }<br /><br /> games = parse_games(list_xml)<br /><br /> puts '... caching games'<br /> open(cache_file, 'w') { |file| Marshal.dump(games, file) }<br /><br /> games<br /> end<br />end<br /><br />def load_xml(mame_executable)<br />end<br /><br />def parse_games(list_xml)<br /> games = {}<br /><br /> print '... decoding games'<br /> (list_xml/'/mame/game').each do |xml_game|<br /> game = Game.decode(xml_game)<br /> games[game.name] = game<br /># print '.'<br /> end<br /> puts<br /><br /> print '... linking parents/clones'<br /> games.each do |name, game|<br /> if game.cloneof<br /> parent = games[game.cloneof]<br /> parent.clones << game if parent<br /> game.parent = parent<br /> end<br /><br /> if game.sampleof<br /> sampleparent = games[game.sampleof]<br /> game.sampleparent = sampleparent<br /> end<br /># print '.'<br /> end<br /> puts<br /><br /> games<br />end<br /><br />def filter_games(games, rules, roms_dirs)<br /> puts '... filtering games'<br /> games.each do |name, game|<br /> if File.exist?(File.join(roms_dirs[:base], "#{name}.zip"))<br /> filter = rules.call(game, roms_dirs)<br /> puts filter if filter<br /> end<br /> end<br />end<br /><br />################################################################################<br /># USER DEFINITIONS<br />################################################################################<br /><br />MAME_EXECUTABLE = 'sdlmame'<br />MARSHALLED_FILE = '/tmp/list_mame_xml.rms'<br /><br />ROMS_DIRS = {<br /> :base => '/home/saverio/docs/roms/mame',<br /> :neogeo => '/home/saverio/docs/roms/mame_neogeo',<br /> :modern => '/home/saverio/docs/roms/mame_modern',<br /> :chd => '/home/saverio/docs/roms/mame_chd'<br />}<br /><br /># laser games aren't included, as currently there aren't the mame disk dumps, so<br /># it's better to use DAPHNE ones.<br />MODERN_SYSTEMS_DRIVERS = %w{<br /> cps3.c # Capcom CPS3 => CPS3 Emulator<br /> model1.c # Sega Model 1 => ???<br /> model2.c # Sega Model 2 => Model 2 Emulator<br /> namcos11.c # Namco System 11 => ZiNc<br /> namcos12.c # Namco System 12 => ZiNc<br /> naomi.c # Sega NAOMI => nullDC<br />}<br />#MODERN_OTHER = %w{<br /># stv.c # Sega Titan Video<br /># vegas.c # Atary/Midway Vegas<br />#}<br />#MODERN_UNEMULATED = %w{<br /># model3.c # Sega Model 3<br /># viper.c # Konami Viper<br />#}<br /><br /># if a game is a preserved clone, don't delete it<br /># delete the very old games<br /># delete the nonworking clones, or the clones with a working parent<br /># keep/move the games better emulated by other emulators ('modern'), even if nonworking<br /># delete the parents of entirely nonworking gamesets<br /># move chd games<br /># other games are kept in the base dir<br />RULES_CLEAN = lambda do |game, roms_dirs|<br /> filename = File.join(roms_dirs[:base], game.name) + "{,.zip}"<br /><br /> if game.cloneof == 'sf2ce'<br /> # do nothing<br /> elsif (! game.year) || game.year.to_i < 1980<br /> sprintf "%-32s # Delete seventies game", "rm #{filename}"<br /> elsif game.clone? && game.parent.working?<br /> sprintf "%-32s # Delete clone of working parent", "rm #{filename}"<br /> elsif game.clone? && game.non_working?<br /> sprintf "%-32s # Delete nonworking clone", "rm #{filename}"<br /> elsif game.sourcefile == 'neodrvr.c'<br /> sprintf "%-32s # Move neo geo game", "mv #{filename} #{roms_dirs[:neogeo]}"<br /> elsif MODERN_SYSTEMS_DRIVERS.include?(game.sourcefile)<br /> sprintf "%-32s # Move modern game", "mv #{filename} #{roms_dirs[:modern]}"<br /> elsif game.parent? && game.non_working? && ! game.clones.any? { |clone| clone.working? }<br /> sprintf "%-32s # Delete parent of non working gameset", "rm #{filename}"<br /> elsif game.has_chd?<br /> sprintf "%-32s # Move chd game", "mv #{filename} #{roms_dirs[:chd]}"<br /> end<br />end<br /><br /># example, for displaying modern games with chd<br />RULES_MODERN_CHD = lambda do |game, roms_dirs|<br /> puts game.name if game.has_chd?&& MODERN_SYSTEMS_DRIVERS.include?(game.sourcefile)<br />end<br /><br />################################################################################<br /># RUNNER<br />################################################################################<br /><br />create_dirs(ROMS_DIRS)<br />games = load_games(MAME_EXECUTABLE, MARSHALLED_FILE)<br /><br />filter_games(games, RULES_CLEAN, ROMS_DIRS)<br /><br />#filter_games(games, RULES_MODERN_CHD , :base => '/home/saverio/docs/roms/mame_modern')<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4843032068198856270.post-72656401746192546622008-12-07T02:01:00.006+01:002008-12-07T02:09:50.368+01:00Programming: Lossless editing (cut/paste) of quicktime moviesModern cameras use the quicktime format, which is very easy to cut/paste losslessly.<br />This means that one can edit the movies shot during the vacations without worsening the already (relatively) low quality.<br /><br />Specifically, it takes one or more files with the timings (start/end) and merges them into a destination file.<br /><br />It's not usable by a non-programmer, but actually it's very easy to create a gui for it; unfortunately java doesn't help deadly simple applications deploying.<br /><br /><pre><br />/*<br /> * License: you can do what the heck you want with this file, as long as you<br /> * reference me as starting author.<br /> * <br /> * @author Saverio Miroddi (com.inbox@pub.saverio - reverse the tokens to get<br /> * the email)<br /> */<br />import quicktime.QTException;<br />import quicktime.QTSession;<br />import quicktime.io.IOConstants;<br />import quicktime.io.OpenMovieFile;<br />import quicktime.io.QTFile;<br />import quicktime.std.StdQTConstants;<br />import quicktime.std.movies.Movie;<br />import java.io.File;<br />import java.io.IOException;<br />import java.util.ArrayList;<br />import java.util.List;<br /><br />class SourceData<br />{<br /> public final String fileName;<br /> public final float start;<br /> /** -1 = end */<br /> public final float end;<br /> <br /> public SourceData(String fileName, float start, float end)<br /> {<br /> this.fileName = fileName;<br /> this.start = start;<br /> this.end = end;<br /> }<br />}<br /><br />/**<br /> * Edits (cuts/pastes) pieces of quicktime movies into a destination quicktime<br /> * file.<br><br /> * The advantage of using this method is that it's lossless - very useful when<br /> * editing movies shot with a portable camera, which are already (relatively)<br /> * low quality.<br /> * <p><br /> * Requires quicktime wrapper (QTJava.zip), that is installed along with<br /> * QuickTime, in the lib/ext folder of the JRE); at the time of writing, the QT<br /> * version is 7.5<br /> * <p><br /> * Note: I never tried executing it from the commandline, I always edited the <br /> * params directly in the code, but it should work fine.<br /> */<br />public class QuickTimeEditing<br />{<br /> private static final int SOURCE_TIMESCALE = 30;<br /> private static final String REF_FILENAME = "qt_edit.ref.tmp";<br /> <br /> public static void main(String[] args) throws Exception<br /> {<br /> args = new String[] {<br /> "d:/tmp/male_singer_falsetto.mov",<br /> "d:/desktop/videos/heli0.mov", "0", "-1",<br /> "d:/desktop/videos/heli1.mov", "0", "-1",<br /> "d:/desktop/videos/heli8.mov", "0", "-1",<br /> "d:/desktop/videos/heli9.mov", "0", "-1",<br /> "d:/desktop/videos/heli10.mov", "0", "-1",<br /> };<br /> <br /> String destFile = args[0];<br /> SourceData[] sourceData = extractSourceData(args);<br /> <br /> new QuickTimeEditing().edit(destFile, sourceData);<br /> }<br /> <br /> /**<br /> * @param args first is skipped<br /> */<br /> private static SourceData[] extractSourceData(String... args)<br /> {<br /> List<SourceData> sourceData = new ArrayList<SourceData>();<br /> <br /> for (int i = 1; i < args.length; )<br /> {<br /> String filename = args[i++];<br /> float start = Float.parseFloat(args[i++]);<br /> float end = Float.parseFloat(args[i++]);<br /> <br /> sourceData.add(new SourceData(filename, start, end));<br /> }<br /><br /> return sourceData.toArray(new SourceData[0]);<br /> }<br /> <br /> // PUBLIC INSTANCE METHODS /////////////////////////////////////////////////<br /><br /> public void edit(String destFile, SourceData... sourcesData) throws Exception<br /> {<br /> String refFile = createRefFilename(destFile);<br /><br /> try {<br /> System.out.println("Opening...");<br /><br /> QTSession.open();<br /><br /> joinSourceMovies(destFile, refFile, sourcesData);<br /> }<br /> finally {<br /> System.out.println("Closing...");<br /> <br /> QTSession.close();<br /><br /> System.out.println("Cleaning up...");<br /><br /> cleanupRefFiles(refFile);<br /> }<br /> }<br /><br /> // PRIVATE INSTANCE METHODS /////////////////////////////////////////////////<br /><br /> private final String createRefFilename(String destFile) throws IOException<br /> {<br /> File destFileDir = new File(destFile).getParentFile();<br /> return new File(destFileDir, REF_FILENAME).getCanonicalPath();<br /> }<br /><br /> private void joinSourceMovies(String destFile, String refFile, SourceData... sourcesData) throws QTException, IOException<br /> {<br /> Movie refMovie = Movie.createMovieFile(new QTFile(refFile),<br /> StdQTConstants.kMoviePlayer, StdQTConstants.createMovieFileDeleteCurFile);<br /> refMovie.setTimeScale(SOURCE_TIMESCALE);<br /><br /> int lastTimeScaled = 0;<br /><br /> for (SourceData sourceData : sourcesData)<br /> {<br /> System.out.println("Processing '" + sourceData.fileName + "' (" + sourceData.start + "->" + sourceData.end + ")...");<br /><br /> Movie sourceMovie = Movie.fromFile(OpenMovieFile.asRead(new QTFile(sourceData.fileName)));<br /> int timeScale = sourceMovie.getTimeScale();<br /><br /> if (timeScale != SOURCE_TIMESCALE) throw new RuntimeException("Too lazy to process time scales != " + SOURCE_TIMESCALE + ": " + timeScale);<br /><br /> int startTimeScaled = (int)(sourceData.start * timeScale);<br /> int lengthScaled = (int)(sourceData.end > 0 ?<br /> sourceData.end * timeScale :<br /> sourceMovie.getDuration() - startTimeScaled);<br /><br /> sourceMovie.insertSegment(refMovie, startTimeScaled, lengthScaled, lastTimeScaled);<br /><br /> lastTimeScaled += lengthScaled;<br /> }<br /><br /> refMovie.flatten (0, // movieFlattenFlags<br /> new QTFile(destFile),<br /> StdQTConstants.kMoviePlayer, // creator<br /> IOConstants.smSystemScript, // scriptTag<br /> StdQTConstants.createMovieFileDeleteCurFile, // createQTFileFlags<br /> StdQTConstants.movieInDataForkResID, // resId<br /> destFile);<br /><br /> }<br /> <br /> private void cleanupRefFiles(String refFile) <br /> {<br /> new File(refFile).delete();<br /> new File(refFile + ".#res").delete();<br /> }<br />}<br /></pre>Unknownnoreply@blogger.com0